mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-25 17:43:38 +01:00
c590518e0c
Support exit node on Android. With the protect socket function, we mark every connection that should be used out of VPN.
85 lines
2.5 KiB
Go
85 lines
2.5 KiB
Go
package dns
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/miekg/dns"
|
|
|
|
"github.com/netbirdio/netbird/client/internal/peer"
|
|
nbnet "github.com/netbirdio/netbird/util/net"
|
|
)
|
|
|
|
type upstreamResolver struct {
|
|
*upstreamResolverBase
|
|
hostsDNSHolder *hostsDNSHolder
|
|
}
|
|
|
|
// newUpstreamResolver in Android we need to distinguish the DNS servers to available through VPN or outside of VPN
|
|
// In case if the assigned DNS address is available only in the protected network then the resolver will time out at the
|
|
// first time, and we need to wait for a while to start to use again the proper DNS resolver.
|
|
func newUpstreamResolver(
|
|
ctx context.Context,
|
|
_ string,
|
|
_ net.IP,
|
|
_ *net.IPNet,
|
|
statusRecorder *peer.Status,
|
|
hostsDNSHolder *hostsDNSHolder,
|
|
) (*upstreamResolver, error) {
|
|
upstreamResolverBase := newUpstreamResolverBase(ctx, statusRecorder)
|
|
c := &upstreamResolver{
|
|
upstreamResolverBase: upstreamResolverBase,
|
|
hostsDNSHolder: hostsDNSHolder,
|
|
}
|
|
upstreamResolverBase.upstreamClient = c
|
|
return c, nil
|
|
}
|
|
|
|
// exchange in case of Android if the upstream is a local resolver then we do not need to mark the socket as protected.
|
|
// In other case the DNS resolvation goes through the VPN, so we need to force to use the
|
|
func (u *upstreamResolver) exchange(ctx context.Context, upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) {
|
|
if u.isLocalResolver(upstream) {
|
|
return u.exchangeWithoutVPN(ctx, upstream, r)
|
|
} else {
|
|
return u.exchangeWithinVPN(ctx, upstream, r)
|
|
}
|
|
}
|
|
|
|
func (u *upstreamResolver) exchangeWithinVPN(ctx context.Context, upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) {
|
|
upstreamExchangeClient := &dns.Client{}
|
|
return upstreamExchangeClient.ExchangeContext(ctx, r, upstream)
|
|
}
|
|
|
|
// exchangeWithoutVPN protect the UDP socket by Android SDK to avoid to goes through the VPN
|
|
func (u *upstreamResolver) exchangeWithoutVPN(ctx context.Context, upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) {
|
|
timeout := upstreamTimeout
|
|
if deadline, ok := ctx.Deadline(); ok {
|
|
timeout = time.Until(deadline)
|
|
}
|
|
dialTimeout := timeout
|
|
|
|
nbDialer := nbnet.NewDialer()
|
|
|
|
dialer := &net.Dialer{
|
|
Control: func(network, address string, c syscall.RawConn) error {
|
|
return nbDialer.Control(network, address, c)
|
|
},
|
|
Timeout: dialTimeout,
|
|
}
|
|
|
|
upstreamExchangeClient := &dns.Client{
|
|
Dialer: dialer,
|
|
}
|
|
|
|
return upstreamExchangeClient.Exchange(r, upstream)
|
|
}
|
|
|
|
func (u *upstreamResolver) isLocalResolver(upstream string) bool {
|
|
if u.hostsDNSHolder.isContain(upstream) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|