From bf38f9f8af16b5accff371ba27399dea7c262691 Mon Sep 17 00:00:00 2001 From: ivulit Date: Fri, 20 Feb 2026 12:34:17 +0300 Subject: [PATCH 1/2] Add domain-fronting-ip option Allow specifying an explicit IP address for the domain fronting host instead of relying on DNS resolution. Useful when DNS resolution of the fronting hostname is blocked. The hostname from the secret is still used for SNI in TLS handshake. --- internal/cli/run_proxy.go | 1 + internal/cli/simple_run.go | 7 +++++++ internal/config/config.go | 1 + internal/config/parse.go | 1 + mtglib/proxy.go | 10 +++++++++- mtglib/proxy_opts.go | 9 +++++++++ 6 files changed, 28 insertions(+), 1 deletion(-) diff --git a/internal/cli/run_proxy.go b/internal/cli/run_proxy.go index 23580eb..e8f708d 100644 --- a/internal/cli/run_proxy.go +++ b/internal/cli/run_proxy.go @@ -260,6 +260,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 74abdf0..4f21244 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 7cdf6da..3911528 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 104c723..872e97f 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 @@ -40,8 +41,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: opts.getLogger("proxy"), 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 53f3434..2cf5532 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. From 21129b6e008b45c81088a9fa1ec5b2566c60bf93 Mon Sep 17 00:00:00 2001 From: ivulit Date: Fri, 20 Feb 2026 12:34:22 +0300 Subject: [PATCH 2/2] Add domain-fronting-ip to example config --- example.config.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/example.config.toml b/example.config.toml index 68f7926..638fb33 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.