@@ -90,6 +90,29 @@ func NewClient(conf *config.Config) (c *Client, err error) {
9090 Net : "tcp" ,
9191 Timeout : time .Duration (conf .Other .Timeout ) * time .Second ,
9292 }
93+
94+ if c .conf .Other .Interface != "" {
95+ // Setup UDP Dialer
96+ udpLocalAddr , err := c .bindToInterface ("udp" )
97+ if err != nil {
98+ return nil , fmt .Errorf ("failed to bind passthrough UDP to interface %s: %v" , c .conf .Other .Interface , err )
99+ }
100+ c .udpClient .Dialer = & net.Dialer {
101+ Timeout : time .Duration (conf .Other .Timeout ) * time .Second ,
102+ LocalAddr : udpLocalAddr ,
103+ }
104+
105+ // Setup TCP Dialer
106+ tcpLocalAddr , err := c .bindToInterface ("tcp" )
107+ if err != nil {
108+ return nil , fmt .Errorf ("failed to bind passthrough TCP to interface %s: %v" , c .conf .Other .Interface , err )
109+ }
110+ c .tcpClient .Dialer = & net.Dialer {
111+ Timeout : time .Duration (conf .Other .Timeout ) * time .Second ,
112+ LocalAddr : tcpLocalAddr ,
113+ }
114+ }
115+
93116 for _ , addr := range conf .Listen {
94117 c .udpServers = append (c .udpServers , & dns.Server {
95118 Addr : addr ,
@@ -120,6 +143,14 @@ func NewClient(conf *config.Config) (c *Client, err error) {
120143 PreferGo : true ,
121144 Dial : func (ctx context.Context , network , address string ) (net.Conn , error ) {
122145 var d net.Dialer
146+ if c .conf .Other .Interface != "" {
147+ localAddr , err := c .bindToInterface (network )
148+ if err != nil {
149+ log .Printf ("Bootstrap dial warning: %v" , err )
150+ } else {
151+ d .LocalAddr = localAddr
152+ }
153+ }
123154 numServers := len (c .bootstrap )
124155 bootstrap := c .bootstrap [rand .Intn (numServers )]
125156 conn , err := d .DialContext (ctx , network , bootstrap )
@@ -241,6 +272,14 @@ func (c *Client) newHTTPClient() error {
241272 // DualStack: true,
242273 Resolver : c .bootstrapResolver ,
243274 }
275+ if c .conf .Other .Interface != "" {
276+ localAddr , err := c .bindToInterface ("tcp" )
277+ if err != nil {
278+ log .Printf ("Failed to resolve interface %s: %v" , c .conf .Other .Interface , err )
279+ return err
280+ }
281+ dialer .LocalAddr = localAddr
282+ }
244283 c .httpTransport = & http.Transport {
245284 DialContext : dialer .DialContext ,
246285 ExpectContinueTimeout : 1 * time .Second ,
@@ -485,3 +524,50 @@ func (c *Client) findClientIP(w dns.ResponseWriter, r *dns.Msg) (ednsClientAddre
485524 }
486525 return
487526}
527+
528+ func (c * Client ) bindToInterface (network string ) (net.Addr , error ) {
529+ if c .conf .Other .Interface == "" {
530+ return nil , nil
531+ }
532+ ifi , err := net .InterfaceByName (c .conf .Other .Interface )
533+ if err != nil {
534+ return nil , err
535+ }
536+ addrs , err := ifi .Addrs ()
537+ if err != nil {
538+ return nil , err
539+ }
540+
541+ // Determine if we need IPv4 or IPv6 based on the network string (e.g., "tcp4", "udp6")
542+ wantIPv6 := strings .Contains (network , "6" )
543+ wantIPv4 := strings .Contains (network , "4" ) || ! wantIPv6 // Default to 4 if not specified, or if generic "tcp"/"udp"
544+
545+ for _ , addr := range addrs {
546+ ip , _ , err := net .ParseCIDR (addr .String ())
547+ if err != nil {
548+ continue
549+ }
550+
551+ // Skip if we want IPv4 but got IPv6
552+ if ip .To4 () == nil && wantIPv4 && ! wantIPv6 {
553+ continue
554+ }
555+ // Skip if we want IPv6 but got IPv4
556+ if ip .To4 () != nil && wantIPv6 {
557+ continue
558+ }
559+ // Skip IPv6 if disabled in config
560+ if ip .To4 () == nil && c .conf .Other .NoIPv6 {
561+ continue
562+ }
563+
564+ // Return the appropriate address type
565+ if strings .HasPrefix (network , "tcp" ) {
566+ return & net.TCPAddr {IP : ip }, nil
567+ }
568+ if strings .HasPrefix (network , "udp" ) {
569+ return & net.UDPAddr {IP : ip }, nil
570+ }
571+ }
572+ return nil , fmt .Errorf ("no suitable address found on interface %s for network %s" , c .conf .Other .Interface , network )
573+ }
0 commit comments