diff --git a/example.config.toml b/example.config.toml index eaff5de..ce95f95 100644 --- a/example.config.toml +++ b/example.config.toml @@ -59,6 +59,14 @@ prefer-ip = "prefer-ipv6" # access. domain-fronting-port = 443 +# By default, mtg resolves the fronting hostname (from the secret) via DNS +# to establish a TCP connection. If DNS resolution of that hostname is blocked, +# you can specify an IP address to connect to directly. The hostname is still +# used for SNI in the TLS handshake. +# +# default value is not set (DNS resolution is used). +# domain-fronting-ip = "142.250.185.112" + # FakeTLS can compare timestamps to prevent probes. Each message has # encrypted timestamp. So, mtg can compare this timestamp and decide if # we need to proceed with connection or not. diff --git a/internal/cli/run_proxy.go b/internal/cli/run_proxy.go index d9f3d1f..056b1b6 100644 --- a/internal/cli/run_proxy.go +++ b/internal/cli/run_proxy.go @@ -252,6 +252,7 @@ func runProxy(conf *config.Config, version string) error { //nolint: funlen Secret: conf.Secret, DomainFrontingPort: conf.DomainFrontingPort.Get(mtglib.DefaultDomainFrontingPort), + DomainFrontingIP: conf.DomainFrontingIP.String(), PreferIP: conf.PreferIP.Get(mtglib.DefaultPreferIP), AllowFallbackOnUnknownDC: conf.AllowFallbackOnUnknownDC.Get(false), diff --git a/internal/cli/simple_run.go b/internal/cli/simple_run.go index 53deb61..b0eebde 100644 --- a/internal/cli/simple_run.go +++ b/internal/cli/simple_run.go @@ -18,6 +18,7 @@ type SimpleRun struct { TCPBuffer string `kong:"name='tcp-buffer',short='b',default='4KB',help='Deprecated and ignored'"` //nolint: lll PreferIP string `kong:"name='prefer-ip',short='i',default='prefer-ipv6',help='IP preference. By default we prefer IPv6 with fallback to IPv4.'"` //nolint: lll DomainFrontingPort uint64 `kong:"name='domain-fronting-port',short='p',default='443',help='A port to access for domain fronting.'"` //nolint: lll + DomainFrontingIP string `kong:"name='domain-fronting-ip',help='An IP address to use for domain fronting instead of resolving the hostname via DNS.'"` //nolint: lll DOHIP net.IP `kong:"name='doh-ip',short='n',default='1.1.1.1',help='IP address of DNS-over-HTTP to use.'"` //nolint: lll Timeout time.Duration `kong:"name='timeout',short='t',default='10s',help='Network timeout to use'"` //nolint: lll Socks5Proxies []string `kong:"name='socks5-proxy',short='s',help='Socks5 proxies to use for network access.'"` //nolint: lll @@ -47,6 +48,12 @@ func (s *SimpleRun) Run(cli *CLI, version string) error { //nolint: cyclop,funle return fmt.Errorf("incorrect domain-fronting-port: %w", err) } + if s.DomainFrontingIP != "" { + if err := conf.DomainFrontingIP.Set(s.DomainFrontingIP); err != nil { + return fmt.Errorf("incorrect domain-fronting-ip: %w", err) + } + } + if err := conf.Network.DOHIP.Set(s.DOHIP.String()); err != nil { return fmt.Errorf("incorrect doh-ip: %w", err) } diff --git a/internal/config/config.go b/internal/config/config.go index 0a5a942..ea4cbb6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -28,6 +28,7 @@ type Config struct { ProxyProtocolListener TypeBool `json:"proxyProtocolListener"` PreferIP TypePreferIP `json:"preferIp"` DomainFrontingPort TypePort `json:"domainFrontingPort"` + DomainFrontingIP TypeIP `json:"domainFrontingIp"` TolerateTimeSkewness TypeDuration `json:"tolerateTimeSkewness"` Concurrency TypeConcurrency `json:"concurrency"` Defense struct { diff --git a/internal/config/parse.go b/internal/config/parse.go index 80fb07c..9461c07 100644 --- a/internal/config/parse.go +++ b/internal/config/parse.go @@ -16,6 +16,7 @@ type tomlConfig struct { ProxyProtocolListener bool `toml:"proxy-protocol-listener" json:"proxyProtocolListener"` PreferIP string `toml:"prefer-ip" json:"preferIp,omitempty"` DomainFrontingPort uint `toml:"domain-fronting-port" json:"domainFrontingPort,omitempty"` + DomainFrontingIP string `toml:"domain-fronting-ip" json:"domainFrontingIp,omitempty"` TolerateTimeSkewness string `toml:"tolerate-time-skewness" json:"tolerateTimeSkewness,omitempty"` Concurrency uint `toml:"concurrency" json:"concurrency,omitempty"` Defense struct { diff --git a/mtglib/proxy.go b/mtglib/proxy.go index 1f12345..567b88e 100644 --- a/mtglib/proxy.go +++ b/mtglib/proxy.go @@ -27,6 +27,7 @@ type Proxy struct { allowFallbackOnUnknownDC bool tolerateTimeSkewness time.Duration domainFrontingPort int + domainFrontingIP string workerPool *ants.PoolWithFunc telegram *dc.Telegram clientObfuscatror obfuscation.Obfuscator @@ -41,8 +42,14 @@ type Proxy struct { } // DomainFrontingAddress returns a host:port pair for a fronting domain. +// If DomainFrontingIP is set, it is used instead of resolving the hostname. func (p *Proxy) DomainFrontingAddress() string { - return net.JoinHostPort(p.secret.Host, strconv.Itoa(p.domainFrontingPort)) + host := p.secret.Host + if p.domainFrontingIP != "" { + host = p.domainFrontingIP + } + + return net.JoinHostPort(host, strconv.Itoa(p.domainFrontingPort)) } // ServeConn serves a connection. We do not check IP blocklist and concurrency @@ -317,6 +324,7 @@ func NewProxy(opts ProxyOpts) (*Proxy, error) { eventStream: opts.EventStream, logger: logger, domainFrontingPort: opts.getDomainFrontingPort(), + domainFrontingIP: opts.DomainFrontingIP, tolerateTimeSkewness: opts.getTolerateTimeSkewness(), allowFallbackOnUnknownDC: opts.AllowFallbackOnUnknownDC, telegram: tg, diff --git a/mtglib/proxy_opts.go b/mtglib/proxy_opts.go index 0399d80..3d84003 100644 --- a/mtglib/proxy_opts.go +++ b/mtglib/proxy_opts.go @@ -93,6 +93,15 @@ type ProxyOpts struct { // This is an optional setting. DomainFrontingPort uint + // DomainFrontingIP is an IP address to use when connecting to the fronting + // domain instead of resolving the hostname from the secret via DNS. + // + // This is useful when DNS resolution of the fronting host is blocked. + // The hostname from the secret is still used for SNI in the TLS handshake. + // + // This is an optional setting. + DomainFrontingIP string + // AllowFallbackOnUnknownDC defines how proxy behaves if unknown DC was // requested. If this setting is set to false, then such connection will be // rejected. Otherwise, proxy will chose any DC.