Add WithDialer option for custom TCP dialer injection#12
Add WithDialer option for custom TCP dialer injection#12myleshorton wants to merge 3 commits intomainfrom
Conversation
Allow callers to inject a custom dialer function via WithDialer() that flows through to both DoH and DoT transports and their underlying uTLS connections. This enables kindling to set a single dialer that automatically applies to all dnstt connections. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce a single WithDialer option at the kindling level that automatically flows to all transports (fronted, dnstt, amp, smart). Key changes: - Add exported DialContextFunc type and WithDialer option - Add Close() to Kindling interface for resource cleanup - Rewrite WithDomainFronting to accept fronted.Option params - Rewrite WithDNSTunnel to accept dnstt.Option params - Rewrite WithAMPCache to accept amp.Config + amp.Option params - Update smart dialer to use injected dialer via FuncStreamDialer - Add streamConnAdapter and closerFunc helper types - Update tests for new API signatures The With* functions now construct transport instances internally and prepend the dialer option, so callers no longer need to create transport instances themselves. Requires getlantern/fronted#67 and getlantern/dnstt#12. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce a single WithDialer option at the kindling level that automatically flows to all transports (fronted, dnstt, amp, smart). Key changes: - Add exported DialContextFunc type and WithDialer option - Add Close() to Kindling interface for resource cleanup - Rewrite WithDomainFronting to accept fronted.Option params - Rewrite WithDNSTunnel to accept dnstt.Option params - Rewrite WithAMPCache to accept amp.Config + amp.Option params - Update smart dialer to use injected dialer via FuncStreamDialer - Add streamConnAdapter and closerFunc helper types - Update tests for new API signatures The With* functions now construct transport instances internally and prepend the dialer option, so callers no longer need to create transport instances themselves. Requires getlantern/fronted#67 and getlantern/dnstt#12. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds support for custom TCP dialer injection to the dnstt package, allowing callers to control how TCP connections are established for DNS-over-HTTPS (DoH) and DNS-over-TLS (DoT) transports. This is useful for scenarios like proxying, custom DNS resolution, or network-level connection control.
Changes:
- Adds
WithDialeroption to configure a custom TCP dialer function - Threads the custom dialer through both DoH and DoT transport implementations
- Refactors uTLS helper functions to support optional custom dialers while maintaining backward compatibility
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| dnstt.go | Adds WithDialer option, injects custom dialer into transport structs via type switch, adds dialContext field to dohDialer and dotDialer structs |
| utls.go | Adds utlsDialContextWithDialer and NewUTLSRoundTripperWithDialer functions, refactors existing functions to delegate to new dialer-aware variants, adds dialContext field to utlsRoundTripper |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case *dohDialer: | ||
| t.dialContext = dial | ||
| case *dotDialer: | ||
| t.dialContext = dial |
There was a problem hiding this comment.
The dialer injection uses a type switch that silently does nothing if the transport is not a recognized type. If a new transport type is added in the future and the developer forgets to update this switch statement, the custom dialer will be silently ignored.
Consider one of these alternatives:
- Add a default case that logs a warning or returns an error
- Add a method to the transport interface like
setDialer(func(...) (net.Conn, error))to make this more explicit and type-safe - Pass the dialer directly when creating the transport in
WithDoHandWithDoToptions (though this would require refactoring)
| t.dialContext = dial | |
| t.dialContext = dial | |
| default: | |
| slog.Warn("unable to inject custom dialer into transport; unrecognized transport type", "transportType", fmt.Sprintf("%T", t)) |
| // WithDialer sets a custom dialer function for the dnstt instance. This allows callers to | ||
| // inject their own dialer for making TCP connections used by the DNS transport. | ||
| func WithDialer(dial func(ctx context.Context, network, addr string) (net.Conn, error)) Option { | ||
| return func(d *dnstt) error { | ||
| d.dialContext = dial | ||
| return nil | ||
| } | ||
| } |
There was a problem hiding this comment.
The new WithDialer option lacks test coverage. Consider adding tests to verify:
- The custom dialer is actually called when making connections through DoH
- The custom dialer is actually called when making connections through DoT
- The behavior when no custom dialer is provided (default fallback works correctly)
- Error handling when the custom dialer returns an error
This is especially important since the dialer injection happens via type assertion and could fail silently if the code changes in the future.
| // make future TLS connections using the same TLS configuration and | ||
| // ClientHelloID. | ||
| func makeRoundTripper(req *http.Request, config *utls.Config, id *utls.ClientHelloID) (http.RoundTripper, error) { | ||
| func makeRoundTripper(req *http.Request, dialCtx func(ctx context.Context, network, addr string) (net.Conn, error), config *utls.Config, id *utls.ClientHelloID) (http.RoundTripper, error) { |
There was a problem hiding this comment.
The parameter name dialCtx is inconsistent with the naming used elsewhere in the codebase. In utlsDialContextWithDialer and NewUTLSRoundTripperWithDialer, the parameter is named dialContext, but here it's abbreviated to dialCtx.
Consider renaming to dialContext for consistency.
- Add default case to transport type switch with warning log for unrecognized transport types - Rename dialCtx to dialContext in makeRoundTripper for consistency - Add tests for WithDialer: DoH injection, DoT injection, default fallback behavior Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
WithDialer(func(ctx context.Context, network, addr string) (net.Conn, error))option todnsttdohDialeranddotDialertransport implementations via newutlsDialContextWithDialerandNewUTLSRoundTripperWithDialerfunctions(&net.Dialer{}).DialContextwhen no custom dialer is provided (preserving existing behavior)utlsDialContext,NewUTLSRoundTripper, andmakeRoundTripperto delegate to new dialer-aware variantsTest plan
go build ./...compiles cleanlygo vet ./...passesgo test ./...)WithDialer🤖 Generated with Claude Code