mirror of
https://github.com/folbricht/routedns.git
synced 2025-12-17 23:54:37 -06:00
93 lines
2.3 KiB
Go
93 lines
2.3 KiB
Go
package rdns
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/txthinking/socks5"
|
|
)
|
|
|
|
type Socks5Dialer struct {
|
|
*socks5.Client
|
|
opt Socks5DialerOptions
|
|
|
|
once sync.Once
|
|
addr string
|
|
}
|
|
|
|
type Socks5DialerOptions struct {
|
|
Username string
|
|
Password string
|
|
UDPTimeout time.Duration
|
|
TCPTimeout time.Duration
|
|
LocalAddr net.IP
|
|
|
|
// When the resolver is configured with a name, not an IP, e.g. one.one.one.one:53
|
|
// this setting will resolve that name locally rather than on the SOCKS proxy. The
|
|
// name will be resolved either on the local system, or via the bootstrap-resolver
|
|
// if one is setup.
|
|
ResolveLocal bool
|
|
}
|
|
|
|
var _ Dialer = (*Socks5Dialer)(nil)
|
|
|
|
func NewSocks5Dialer(addr string, opt Socks5DialerOptions) *Socks5Dialer {
|
|
client, _ := socks5.NewClient(
|
|
addr,
|
|
opt.Username,
|
|
opt.Password,
|
|
int(opt.TCPTimeout.Seconds()),
|
|
int(opt.UDPTimeout.Seconds()),
|
|
)
|
|
return &Socks5Dialer{Client: client, opt: opt}
|
|
}
|
|
|
|
func (d *Socks5Dialer) Dial(network string, address string) (net.Conn, error) {
|
|
d.once.Do(func() {
|
|
d.addr = address
|
|
|
|
// If the address uses a hostname and ResolveLocal is enabled, lookup
|
|
// the IP for it locally and use that when talking to the proxy going
|
|
// forward. This avoids the DNS server's address leaking out from the
|
|
// proxy.
|
|
if d.opt.ResolveLocal {
|
|
host, port, err := net.SplitHostPort(address)
|
|
if err != nil {
|
|
Log.Error("failed to parse socks5 address", "error", err)
|
|
return
|
|
}
|
|
Log.With("addr", host).Debug("resolving dns server locally")
|
|
ip := net.ParseIP(host)
|
|
if ip != nil {
|
|
// Already an IP
|
|
return
|
|
}
|
|
timeout := d.opt.UDPTimeout
|
|
if timeout == 0 {
|
|
timeout = 5 * time.Second
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
defer cancel()
|
|
ips, err := net.DefaultResolver.LookupIP(ctx, "ip4", host)
|
|
if err != nil {
|
|
Log.Warn("failed to lookup host locally", "error", err,
|
|
"host", host)
|
|
return
|
|
}
|
|
if len(ips) == 0 {
|
|
Log.Warn("failed to resolve dns server locally, forwarding to socks5 proxy", "error", err)
|
|
return
|
|
}
|
|
d.addr = net.JoinHostPort(ips[0].String(), port)
|
|
}
|
|
|
|
})
|
|
|
|
if d.opt.LocalAddr != nil {
|
|
return d.Client.DialWithLocalAddr(network, d.opt.LocalAddr.String(), d.addr, nil)
|
|
}
|
|
return d.Client.Dial(network, d.addr)
|
|
}
|