mitm: fix localhost fallback

Kim committed Mar 25, 2026 at 15:36 UTC 6b99192a6c0de22c03111772b5ee5c15d4fc5bfe
2 files changed +57 -3
sdk/mitm.go
+17 -3
@@ -108,16 +108,16 @@ func (m *mitmManager) probeTLSPassthrough(ctx context.Context) (MITMProbeReport,
108 report.PublicURL = publicURL
109 report.LeaseID = l.LeaseID()
110
111 - parsedURL, err := url.Parse(publicURL)
111 + dialAddr, err := m.probeDialAddress(publicURL)
112 if err != nil {
113 - return report, fmt.Errorf("parse public url: %w", err)
113 + return report, err
114 }
115
116 dialer := &tls.Dialer{
117 NetDialer: &net.Dialer{Timeout: l.api.dialTimeout},
118 Config: m.clientTLSConfig(hostname),
119 }
120 - conn, err := dialer.DialContext(probeCtx, "tcp", utils.EnsurePort(parsedURL.Host))
120 + conn, err := dialer.DialContext(probeCtx, "tcp", dialAddr)
121 if err != nil {
122 return report, fmt.Errorf("dial mitm probe: %w", err)
123 }
@@ -169,6 +169,20 @@ func (m *mitmManager) probeTLSPassthrough(ctx context.Context) (MITMProbeReport,
169 }
170 }
171
172 +func (m *mitmManager) probeDialAddress(publicURL string) (string, error) {
173 + parsedURL, err := url.Parse(publicURL)
174 + if err != nil {
175 + return "", fmt.Errorf("parse public url: %w", err)
176 + }
177 +
178 + dialHost := parsedURL.Host
179 + l := m.listener
180 + if l != nil && l.api != nil && l.api.baseURL != nil && utils.IsLocalRelayHost(l.api.baseURL.Hostname()) {
181 + dialHost = l.api.baseURL.Host
182 + }
183 + return utils.EnsurePort(dialHost), nil
184 +}
185 +
186 func (m *mitmManager) clientTLSConfig(hostname string) *tls.Config {
187 probeTLSConf := &tls.Config{
188 ServerName: hostname,
sdk/mitm_test.go
+40
@@ -235,6 +235,46 @@ func TestMITMProbeDetectionBansListener(t *testing.T) {
235 }
236 }
237
238 +func TestMITMProbeDialAddressUsesRelayHostForLocalRelay(t *testing.T) {
239 + relayURL, err := url.Parse("https://localhost:4017")
240 + if err != nil {
241 + t.Fatalf("url.Parse() error = %v", err)
242 + }
243 +
244 + listener := &Listener{
245 + api: &apiClient{baseURL: relayURL},
246 + }
247 + listener.mitmManager = newMITMManager(context.Background(), listener)
248 +
249 + got, err := listener.mitmManager.probeDialAddress("https://bravo-gecko-disco.localhost:4017")
250 + if err != nil {
251 + t.Fatalf("probeDialAddress() error = %v", err)
252 + }
253 + if got != "localhost:4017" {
254 + t.Fatalf("probeDialAddress() = %q, want %q", got, "localhost:4017")
255 + }
256 +}
257 +
258 +func TestMITMProbeDialAddressUsesPublicURLForRemoteRelay(t *testing.T) {
259 + relayURL, err := url.Parse("https://relay.example")
260 + if err != nil {
261 + t.Fatalf("url.Parse() error = %v", err)
262 + }
263 +
264 + listener := &Listener{
265 + api: &apiClient{baseURL: relayURL},
266 + }
267 + listener.mitmManager = newMITMManager(context.Background(), listener)
268 +
269 + got, err := listener.mitmManager.probeDialAddress("https://bravo-gecko-disco.example")
270 + if err != nil {
271 + t.Fatalf("probeDialAddress() error = %v", err)
272 + }
273 + if got != "bravo-gecko-disco.example:443" {
274 + t.Fatalf("probeDialAddress() = %q, want %q", got, "bravo-gecko-disco.example:443")
275 + }
276 +}
277 +
278 func newMITMProbeTLSPair(t *testing.T) (*tls.Conn, *tls.Conn) {
279 t.Helper()
280