Feature/search domain for android (#1256)

Support search domain on Android

- pass list of search domains to Android SDK
- throw notification in case of search domain changes
This commit is contained in:
Zoltan Papp 2023-11-02 19:04:33 +01:00 committed by GitHub
parent 8cf2866a6a
commit e2f27502e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 180 additions and 77 deletions

View File

@ -8,8 +8,8 @@ import (
"github.com/netbirdio/netbird/client/internal" "github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/dns"
"github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/internal/routemanager"
"github.com/netbirdio/netbird/client/internal/stdnet" "github.com/netbirdio/netbird/client/internal/stdnet"
"github.com/netbirdio/netbird/client/system" "github.com/netbirdio/netbird/client/system"
"github.com/netbirdio/netbird/formatter" "github.com/netbirdio/netbird/formatter"
@ -31,9 +31,9 @@ type IFaceDiscover interface {
stdnet.ExternalIFaceDiscover stdnet.ExternalIFaceDiscover
} }
// RouteListener export internal RouteListener for mobile // NetworkChangeListener export internal NetworkChangeListener for mobile
type RouteListener interface { type NetworkChangeListener interface {
routemanager.RouteListener listener.NetworkChangeListener
} }
// DnsReadyListener export internal dns ReadyListener for mobile // DnsReadyListener export internal dns ReadyListener for mobile
@ -47,26 +47,26 @@ func init() {
// Client struct manage the life circle of background service // Client struct manage the life circle of background service
type Client struct { type Client struct {
cfgFile string cfgFile string
tunAdapter iface.TunAdapter tunAdapter iface.TunAdapter
iFaceDiscover IFaceDiscover iFaceDiscover IFaceDiscover
recorder *peer.Status recorder *peer.Status
ctxCancel context.CancelFunc ctxCancel context.CancelFunc
ctxCancelLock *sync.Mutex ctxCancelLock *sync.Mutex
deviceName string deviceName string
routeListener routemanager.RouteListener networkChangeListener listener.NetworkChangeListener
} }
// NewClient instantiate a new Client // NewClient instantiate a new Client
func NewClient(cfgFile, deviceName string, tunAdapter TunAdapter, iFaceDiscover IFaceDiscover, routeListener RouteListener) *Client { func NewClient(cfgFile, deviceName string, tunAdapter TunAdapter, iFaceDiscover IFaceDiscover, networkChangeListener NetworkChangeListener) *Client {
return &Client{ return &Client{
cfgFile: cfgFile, cfgFile: cfgFile,
deviceName: deviceName, deviceName: deviceName,
tunAdapter: tunAdapter, tunAdapter: tunAdapter,
iFaceDiscover: iFaceDiscover, iFaceDiscover: iFaceDiscover,
recorder: peer.NewRecorder(""), recorder: peer.NewRecorder(""),
ctxCancelLock: &sync.Mutex{}, ctxCancelLock: &sync.Mutex{},
routeListener: routeListener, networkChangeListener: networkChangeListener,
} }
} }
@ -96,7 +96,7 @@ func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsRead
// todo do not throw error in case of cancelled context // todo do not throw error in case of cancelled context
ctx = internal.CtxInitState(ctx) ctx = internal.CtxInitState(ctx)
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.routeListener, dns.items, dnsReadyListener) return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener)
} }
// RunWithoutLogin we apply this type of run function when the backed has been started without UI (i.e. after reboot). // RunWithoutLogin we apply this type of run function when the backed has been started without UI (i.e. after reboot).
@ -120,7 +120,7 @@ func (c *Client) RunWithoutLogin(dns *DNSList, dnsReadyListener DnsReadyListener
// todo do not throw error in case of cancelled context // todo do not throw error in case of cancelled context
ctx = internal.CtxInitState(ctx) ctx = internal.CtxInitState(ctx)
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.routeListener, dns.items, dnsReadyListener) return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener)
} }
// Stop the internal client and free the resources // Stop the internal client and free the resources

View File

@ -13,8 +13,8 @@ import (
gstatus "google.golang.org/grpc/status" gstatus "google.golang.org/grpc/status"
"github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/dns"
"github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/internal/routemanager"
"github.com/netbirdio/netbird/client/internal/stdnet" "github.com/netbirdio/netbird/client/internal/stdnet"
"github.com/netbirdio/netbird/client/ssh" "github.com/netbirdio/netbird/client/ssh"
"github.com/netbirdio/netbird/client/system" "github.com/netbirdio/netbird/client/system"
@ -31,14 +31,14 @@ func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status)
} }
// RunClientMobile with main logic on mobile system // RunClientMobile with main logic on mobile system
func RunClientMobile(ctx context.Context, config *Config, statusRecorder *peer.Status, tunAdapter iface.TunAdapter, iFaceDiscover stdnet.ExternalIFaceDiscover, routeListener routemanager.RouteListener, dnsAddresses []string, dnsReadyListener dns.ReadyListener) error { func RunClientMobile(ctx context.Context, config *Config, statusRecorder *peer.Status, tunAdapter iface.TunAdapter, iFaceDiscover stdnet.ExternalIFaceDiscover, networkChangeListener listener.NetworkChangeListener, dnsAddresses []string, dnsReadyListener dns.ReadyListener) error {
// in case of non Android os these variables will be nil // in case of non Android os these variables will be nil
mobileDependency := MobileDependency{ mobileDependency := MobileDependency{
TunAdapter: tunAdapter, TunAdapter: tunAdapter,
IFaceDiscover: iFaceDiscover, IFaceDiscover: iFaceDiscover,
RouteListener: routeListener, NetworkChangeListener: networkChangeListener,
HostDNSAddresses: dnsAddresses, HostDNSAddresses: dnsAddresses,
DnsReadyListener: dnsReadyListener, DnsReadyListener: dnsReadyListener,
} }
return runClient(ctx, config, statusRecorder, mobileDependency) return runClient(ctx, config, statusRecorder, mobileDependency)
} }

View File

@ -2,6 +2,7 @@ package dns
import ( import (
"fmt" "fmt"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
) )
@ -43,3 +44,7 @@ func (m *MockServer) UpdateDNSServer(serial uint64, update nbdns.Config) error {
} }
return fmt.Errorf("method UpdateDNSServer is not implemented") return fmt.Errorf("method UpdateDNSServer is not implemented")
} }
func (m *MockServer) SearchDomains() []string {
return make([]string, 0)
}

View File

@ -0,0 +1,57 @@
package dns
import (
"reflect"
"sort"
"sync"
"github.com/netbirdio/netbird/client/internal/listener"
)
type notifier struct {
listener listener.NetworkChangeListener
listenerMux sync.Mutex
searchDomains []string
}
func newNotifier(initialSearchDomains []string) *notifier {
sort.Strings(initialSearchDomains)
return &notifier{
searchDomains: initialSearchDomains,
}
}
func (n *notifier) setListener(listener listener.NetworkChangeListener) {
n.listenerMux.Lock()
defer n.listenerMux.Unlock()
n.listener = listener
}
func (n *notifier) onNewSearchDomains(searchDomains []string) {
sort.Strings(searchDomains)
if len(n.searchDomains) != len(searchDomains) {
n.searchDomains = searchDomains
n.notify()
return
}
if reflect.DeepEqual(n.searchDomains, searchDomains) {
return
}
n.searchDomains = searchDomains
n.notify()
}
func (n *notifier) notify() {
n.listenerMux.Lock()
defer n.listenerMux.Unlock()
if n.listener == nil {
return
}
go func(l listener.NetworkChangeListener) {
l.OnNetworkChanged()
}(n.listener)
}

View File

@ -10,6 +10,7 @@ import (
"github.com/mitchellh/hashstructure/v2" "github.com/mitchellh/hashstructure/v2"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/internal/listener"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
) )
@ -25,6 +26,7 @@ type Server interface {
DnsIP() string DnsIP() string
UpdateDNSServer(serial uint64, update nbdns.Config) error UpdateDNSServer(serial uint64, update nbdns.Config) error
OnUpdatedHostDNSServer(strings []string) OnUpdatedHostDNSServer(strings []string)
SearchDomains() []string
} }
type registeredHandlerMap map[string]handlerWithStop type registeredHandlerMap map[string]handlerWithStop
@ -47,6 +49,9 @@ type DefaultServer struct {
permanent bool permanent bool
hostsDnsList []string hostsDnsList []string
hostsDnsListLock sync.Mutex hostsDnsListLock sync.Mutex
// make sense on mobile only
searchDomainNotifier *notifier
} }
type handlerWithStop interface { type handlerWithStop interface {
@ -81,12 +86,15 @@ func NewDefaultServer(ctx context.Context, wgInterface WGIface, customAddress st
} }
// NewDefaultServerPermanentUpstream returns a new dns server. It optimized for mobile systems // NewDefaultServerPermanentUpstream returns a new dns server. It optimized for mobile systems
func NewDefaultServerPermanentUpstream(ctx context.Context, wgInterface WGIface, hostsDnsList []string) *DefaultServer { func NewDefaultServerPermanentUpstream(ctx context.Context, wgInterface WGIface, hostsDnsList []string, config nbdns.Config, listener listener.NetworkChangeListener) *DefaultServer {
log.Debugf("host dns address list is: %v", hostsDnsList) log.Debugf("host dns address list is: %v", hostsDnsList)
ds := newDefaultServer(ctx, wgInterface, newServiceViaMemory(wgInterface)) ds := newDefaultServer(ctx, wgInterface, newServiceViaMemory(wgInterface))
ds.permanent = true ds.permanent = true
ds.hostsDnsList = hostsDnsList ds.hostsDnsList = hostsDnsList
ds.addHostRootZone() ds.addHostRootZone()
ds.currentConfig = dnsConfigToHostDNSConfig(config, ds.service.RuntimeIP(), ds.service.RuntimePort())
ds.searchDomainNotifier = newNotifier(ds.SearchDomains())
ds.searchDomainNotifier.setListener(listener)
setServerDns(ds) setServerDns(ds)
return ds return ds
} }
@ -212,6 +220,21 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro
} }
} }
func (s *DefaultServer) SearchDomains() []string {
var searchDomains []string
for _, dConf := range s.currentConfig.domains {
if dConf.disabled {
continue
}
if dConf.matchOnly {
continue
}
searchDomains = append(searchDomains, dConf.domain)
}
return searchDomains
}
func (s *DefaultServer) applyConfiguration(update nbdns.Config) error { func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
// is the service should be disabled, we stop the listener or fake resolver // is the service should be disabled, we stop the listener or fake resolver
// and proceed with a regular update to clean up the handlers and records // and proceed with a regular update to clean up the handlers and records
@ -246,6 +269,10 @@ func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
log.Error(err) log.Error(err)
} }
if s.searchDomainNotifier != nil {
s.searchDomainNotifier.onNewSearchDomains(s.SearchDomains())
}
return nil return nil
} }

View File

@ -593,7 +593,8 @@ func TestDNSPermanent_updateHostDNS_emptyUpstream(t *testing.T) {
defer wgIFace.Close() defer wgIFace.Close()
var dnsList []string var dnsList []string
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList) dnsConfig := nbdns.Config{}
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList, dnsConfig, nil)
err = dnsServer.Initialize() err = dnsServer.Initialize()
if err != nil { if err != nil {
t.Errorf("failed to initialize DNS server: %v", err) t.Errorf("failed to initialize DNS server: %v", err)
@ -616,8 +617,8 @@ func TestDNSPermanent_updateUpstream(t *testing.T) {
t.Fatal("failed to initialize wg interface") t.Fatal("failed to initialize wg interface")
} }
defer wgIFace.Close() defer wgIFace.Close()
dnsConfig := nbdns.Config{}
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}) dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
err = dnsServer.Initialize() err = dnsServer.Initialize()
if err != nil { if err != nil {
t.Errorf("failed to initialize DNS server: %v", err) t.Errorf("failed to initialize DNS server: %v", err)
@ -708,8 +709,8 @@ func TestDNSPermanent_matchOnly(t *testing.T) {
t.Fatal("failed to initialize wg interface") t.Fatal("failed to initialize wg interface")
} }
defer wgIFace.Close() defer wgIFace.Close()
dnsConfig := nbdns.Config{}
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}) dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
err = dnsServer.Initialize() err = dnsServer.Initialize()
if err != nil { if err != nil {
t.Errorf("failed to initialize DNS server: %v", err) t.Errorf("failed to initialize DNS server: %v", err)

View File

@ -195,12 +195,13 @@ func (e *Engine) Start() error {
var routes []*route.Route var routes []*route.Route
if runtime.GOOS == "android" { if runtime.GOOS == "android" {
routes, err = e.readInitialSettings() var dnsConfig *nbdns.Config
routes, dnsConfig, err = e.readInitialSettings()
if err != nil { if err != nil {
return err return err
} }
if e.dnsServer == nil { if e.dnsServer == nil {
e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses) e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses, *dnsConfig, e.mobileDep.NetworkChangeListener)
go e.mobileDep.DnsReadyListener.OnReady() go e.mobileDep.DnsReadyListener.OnReady()
} }
} else { } else {
@ -215,15 +216,16 @@ func (e *Engine) Start() error {
} }
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, routes) e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, routes)
e.routeManager.SetRouteChangeListener(e.mobileDep.RouteListener) e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
if runtime.GOOS != "android" { if runtime.GOOS == "android" {
err = e.wgInterface.Create()
} else {
err = e.wgInterface.CreateOnMobile(iface.MobileIFaceArguments{ err = e.wgInterface.CreateOnMobile(iface.MobileIFaceArguments{
Routes: e.routeManager.InitialRouteRange(), Routes: e.routeManager.InitialRouteRange(),
Dns: e.dnsServer.DnsIP(), Dns: e.dnsServer.DnsIP(),
SearchDomains: e.dnsServer.SearchDomains(),
}) })
} else {
err = e.wgInterface.Create()
} }
if err != nil { if err != nil {
log.Errorf("failed creating tunnel interface %s: [%s]", wgIFaceName, err.Error()) log.Errorf("failed creating tunnel interface %s: [%s]", wgIFaceName, err.Error())
@ -1051,13 +1053,14 @@ func (e *Engine) close() {
} }
} }
func (e *Engine) readInitialSettings() ([]*route.Route, error) { func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
netMap, err := e.mgmClient.GetNetworkMap() netMap, err := e.mgmClient.GetNetworkMap()
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
routes := toRoutes(netMap.GetRoutes()) routes := toRoutes(netMap.GetRoutes())
return routes, nil dnsCfg := toDNSConfig(netMap.GetDNSConfig())
return routes, &dnsCfg, nil
} }
func findIPFromInterfaceName(ifaceName string) (net.IP, error) { func findIPFromInterfaceName(ifaceName string) (net.IP, error) {

View File

@ -0,0 +1,7 @@
package listener
// NetworkChangeListener is a callback interface for mobile system
type NetworkChangeListener interface {
// OnNetworkChanged invoke when network settings has been changed
OnNetworkChanged()
}

View File

@ -2,16 +2,16 @@ package internal
import ( import (
"github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/dns"
"github.com/netbirdio/netbird/client/internal/routemanager" "github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/client/internal/stdnet" "github.com/netbirdio/netbird/client/internal/stdnet"
"github.com/netbirdio/netbird/iface" "github.com/netbirdio/netbird/iface"
) )
// MobileDependency collect all dependencies for mobile platform // MobileDependency collect all dependencies for mobile platform
type MobileDependency struct { type MobileDependency struct {
TunAdapter iface.TunAdapter TunAdapter iface.TunAdapter
IFaceDiscover stdnet.ExternalIFaceDiscover IFaceDiscover stdnet.ExternalIFaceDiscover
RouteListener routemanager.RouteListener NetworkChangeListener listener.NetworkChangeListener
HostDNSAddresses []string HostDNSAddresses []string
DnsReadyListener dns.ReadyListener DnsReadyListener dns.ReadyListener
} }

View File

@ -7,6 +7,7 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/iface" "github.com/netbirdio/netbird/iface"
"github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/route"
@ -16,7 +17,7 @@ import (
// Manager is a route manager interface // Manager is a route manager interface
type Manager interface { type Manager interface {
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error
SetRouteChangeListener(listener RouteListener) SetRouteChangeListener(listener listener.NetworkChangeListener)
InitialRouteRange() []string InitialRouteRange() []string
Stop() Stop()
} }
@ -96,7 +97,7 @@ func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Ro
} }
// SetRouteChangeListener set RouteListener for route change notifier // SetRouteChangeListener set RouteListener for route change notifier
func (m *DefaultManager) SetRouteChangeListener(listener RouteListener) { func (m *DefaultManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
m.notifier.setListener(listener) m.notifier.setListener(listener)
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/iface" "github.com/netbirdio/netbird/iface"
"github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/route"
) )
@ -32,7 +33,7 @@ func (m *MockManager) Start(ctx context.Context, iface *iface.WGIface) {
} }
// SetRouteChangeListener mock implementation of SetRouteChangeListener from Manager interface // SetRouteChangeListener mock implementation of SetRouteChangeListener from Manager interface
func (m *MockManager) SetRouteChangeListener(listener RouteListener) { func (m *MockManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
} }

View File

@ -4,31 +4,26 @@ import (
"sort" "sort"
"sync" "sync"
"github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/route" "github.com/netbirdio/netbird/route"
) )
// RouteListener is a callback interface for mobile system
type RouteListener interface {
// OnNewRouteSetting invoke when new route setting has been arrived
OnNewRouteSetting()
}
type notifier struct { type notifier struct {
initialRouteRangers []string initialRouteRangers []string
routeRangers []string routeRangers []string
routeListener RouteListener listener listener.NetworkChangeListener
routeListenerMux sync.Mutex listenerMux sync.Mutex
} }
func newNotifier() *notifier { func newNotifier() *notifier {
return &notifier{} return &notifier{}
} }
func (n *notifier) setListener(listener RouteListener) { func (n *notifier) setListener(listener listener.NetworkChangeListener) {
n.routeListenerMux.Lock() n.listenerMux.Lock()
defer n.routeListenerMux.Unlock() defer n.listenerMux.Unlock()
n.routeListener = listener n.listener = listener
} }
func (n *notifier) setInitialClientRoutes(clientRoutes []*route.Route) { func (n *notifier) setInitialClientRoutes(clientRoutes []*route.Route) {
@ -62,15 +57,15 @@ func (n *notifier) onNewRoutes(idMap map[string][]*route.Route) {
} }
func (n *notifier) notify() { func (n *notifier) notify() {
n.routeListenerMux.Lock() n.listenerMux.Lock()
defer n.routeListenerMux.Unlock() defer n.listenerMux.Unlock()
if n.routeListener == nil { if n.listener == nil {
return return
} }
go func(l RouteListener) { go func(l listener.NetworkChangeListener) {
l.OnNewRouteSetting() l.OnNetworkChanged()
}(n.routeListener) }(n.listener)
} }
func (n *notifier) hasDiff(a []string, b []string) bool { func (n *notifier) hasDiff(a []string, b []string) bool {

View File

@ -1,8 +1,9 @@
package iface package iface
type MobileIFaceArguments struct { type MobileIFaceArguments struct {
Routes []string Routes []string
Dns string Dns string
SearchDomains []string
} }
// NetInterface represents a generic network tunnel interface // NetInterface represents a generic network tunnel interface

View File

@ -2,6 +2,6 @@ package iface
// TunAdapter is an interface for create tun device from externel service // TunAdapter is an interface for create tun device from externel service
type TunAdapter interface { type TunAdapter interface {
ConfigureInterface(address string, mtu int, dns string, routes string) (int, error) ConfigureInterface(address string, mtu int, dns string, searchDomains string, routes string) (int, error)
UpdateAddr(address string) error UpdateAddr(address string) error
} }

View File

@ -37,7 +37,8 @@ func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
log.Info("create tun interface") log.Info("create tun interface")
var err error var err error
routesString := t.routesToString(mIFaceArgs.Routes) routesString := t.routesToString(mIFaceArgs.Routes)
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, mIFaceArgs.Dns, routesString) searchDomainsToString := t.searchDomainsToString(mIFaceArgs.SearchDomains)
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, mIFaceArgs.Dns, searchDomainsToString, routesString)
if err != nil { if err != nil {
log.Errorf("failed to create Android interface: %s", err) log.Errorf("failed to create Android interface: %s", err)
return err return err
@ -94,3 +95,7 @@ func (t *tunDevice) Close() (err error) {
func (t *tunDevice) routesToString(routes []string) string { func (t *tunDevice) routesToString(routes []string) string {
return strings.Join(routes, ";") return strings.Join(routes, ";")
} }
func (t *tunDevice) searchDomainsToString(searchDomains []string) string {
return strings.Join(searchDomains, ";")
}