mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-23 02:36:42 +00:00
Compare commits
35 Commits
cli-ws-pro
...
for-pascal
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a279c698c | ||
|
|
ca9ea29255 | ||
|
|
148a537c19 | ||
|
|
c88e8139c7 | ||
|
|
f854ec9bb6 | ||
|
|
d6d2e64247 | ||
|
|
5f3f5dc1c6 | ||
|
|
0f7343dd58 | ||
|
|
71f1cf80b8 | ||
|
|
975e8e816a | ||
|
|
e03c07a3dc | ||
|
|
ad1cf388fb | ||
|
|
5f96c566ab | ||
|
|
b2300216bb | ||
|
|
a37c946abe | ||
|
|
0a8249e044 | ||
|
|
2b249ab9c9 | ||
|
|
0e7a67cf81 | ||
|
|
1c23a0e70c | ||
|
|
5632d222cc | ||
|
|
e193df3bc7 | ||
|
|
65052e5cba | ||
|
|
64084ca130 | ||
|
|
79f60b86c4 | ||
|
|
de46393a7c | ||
|
|
c4c59ed3a7 | ||
|
|
7f958e9338 | ||
|
|
91b45eab98 | ||
|
|
ec8eb76b42 | ||
|
|
8b8e4bbc6a | ||
|
|
e733cdcf33 | ||
|
|
cdbe9c4eef | ||
|
|
8653c32367 | ||
|
|
6743054451 | ||
|
|
7f7e10121d |
@@ -43,6 +43,16 @@ func RunClientMobile(ctx context.Context, config *Config, statusRecorder *peer.S
|
|||||||
return runClient(ctx, config, statusRecorder, mobileDependency)
|
return runClient(ctx, config, statusRecorder, mobileDependency)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RunClientiOS(ctx context.Context, config *Config, statusRecorder *peer.Status, fileDescriptor int32, networkChangeListener listener.NetworkChangeListener, dnsManager dns.IosDnsManager, interfaceName string) error {
|
||||||
|
mobileDependency := MobileDependency{
|
||||||
|
FileDescriptor: fileDescriptor,
|
||||||
|
InterfaceName: interfaceName,
|
||||||
|
NetworkChangeListener: networkChangeListener,
|
||||||
|
DnsManager: dnsManager,
|
||||||
|
}
|
||||||
|
return runClient(ctx, config, statusRecorder, mobileDependency)
|
||||||
|
}
|
||||||
|
|
||||||
func runClient(ctx context.Context, config *Config, statusRecorder *peer.Status, mobileDependency MobileDependency) error {
|
func runClient(ctx context.Context, config *Config, statusRecorder *peer.Status, mobileDependency MobileDependency) error {
|
||||||
log.Infof("starting NetBird client version %s", version.NetbirdVersion())
|
log.Infof("starting NetBird client version %s", version.NetbirdVersion())
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func newNoopHostMocker() hostManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) hostDNSConfig {
|
func dnsConfigTohostDNSConfig(dnsConfig nbdns.Config, ip string, port int) hostDNSConfig {
|
||||||
config := hostDNSConfig{
|
config := hostDNSConfig{
|
||||||
routeAll: false,
|
routeAll: false,
|
||||||
serverIP: ip,
|
serverIP: ip,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package dns
|
|||||||
type androidHostManager struct {
|
type androidHostManager struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHostManager(wgInterface WGIface) (hostManager, error) {
|
func newHostManager() (hostManager, error) {
|
||||||
return &androidHostManager{}, nil
|
return &androidHostManager{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !ios
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -32,7 +34,7 @@ type systemConfigurator struct {
|
|||||||
createdKeys map[string]struct{}
|
createdKeys map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHostManager(_ WGIface) (hostManager, error) {
|
func newHostManager() (hostManager, error) {
|
||||||
return &systemConfigurator{
|
return &systemConfigurator{
|
||||||
createdKeys: make(map[string]struct{}),
|
createdKeys: make(map[string]struct{}),
|
||||||
}, nil
|
}, nil
|
||||||
|
|||||||
45
client/internal/dns/host_ios.go
Normal file
45
client/internal/dns/host_ios.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type iosHostManager struct {
|
||||||
|
dnsManager IosDnsManager
|
||||||
|
config hostDNSConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHostManager(dnsManager IosDnsManager) (hostManager, error) {
|
||||||
|
return &iosHostManager{
|
||||||
|
dnsManager: dnsManager,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a iosHostManager) applyDNSConfig(config hostDNSConfig) error {
|
||||||
|
var configAsString []string
|
||||||
|
configAsString = append(configAsString, config.serverIP)
|
||||||
|
configAsString = append(configAsString, strconv.Itoa(config.serverPort))
|
||||||
|
configAsString = append(configAsString, strconv.FormatBool(config.routeAll))
|
||||||
|
var domainConfigAsString []string
|
||||||
|
for _, domain := range config.domains {
|
||||||
|
var domainAsString []string
|
||||||
|
domainAsString = append(domainAsString, strconv.FormatBool(domain.disabled))
|
||||||
|
domainAsString = append(domainAsString, domain.domain)
|
||||||
|
domainAsString = append(domainAsString, strconv.FormatBool(domain.matchOnly))
|
||||||
|
domainConfigAsString = append(domainConfigAsString, strings.Join(domainAsString, "|"))
|
||||||
|
}
|
||||||
|
domainConfig := strings.Join(domainConfigAsString, ";")
|
||||||
|
configAsString = append(configAsString, domainConfig)
|
||||||
|
outputString := strings.Join(configAsString, ",")
|
||||||
|
a.dnsManager.ApplyDns(outputString)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a iosHostManager) restoreHostDNS() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a iosHostManager) supportCustomPort() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ type MockServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize mock implementation of Initialize from Server interface
|
// Initialize mock implementation of Initialize from Server interface
|
||||||
func (m *MockServer) Initialize() error {
|
func (m *MockServer) Initialize(manager IosDnsManager) error {
|
||||||
if m.InitializeFunc != nil {
|
if m.InitializeFunc != nil {
|
||||||
return m.InitializeFunc()
|
return m.InitializeFunc()
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ func (m *MockServer) DnsIP() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockServer) OnUpdatedHostDNSServer(strings []string) {
|
func (m *MockServer) OnUpdatedHostDNSServer(strings []string) {
|
||||||
//TODO implement me
|
// TODO implement me
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import (
|
|||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
"github.com/hashicorp/go-version"
|
"github.com/hashicorp/go-version"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
nbversion "github.com/netbirdio/netbird/version"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
nbversion "github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -52,6 +52,6 @@ func (n *notifier) notify() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func(l listener.NetworkChangeListener) {
|
go func(l listener.NetworkChangeListener) {
|
||||||
l.OnNetworkChanged()
|
l.OnNetworkChanged("")
|
||||||
}(n.listener)
|
}(n.listener)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,14 @@ type ReadyListener interface {
|
|||||||
OnReady()
|
OnReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IosDnsManager is a dns manager interface for iosß
|
||||||
|
type IosDnsManager interface {
|
||||||
|
ApplyDns(string)
|
||||||
|
}
|
||||||
|
|
||||||
// Server is a dns server interface
|
// Server is a dns server interface
|
||||||
type Server interface {
|
type Server interface {
|
||||||
Initialize() error
|
Initialize(manager IosDnsManager) error
|
||||||
Stop()
|
Stop()
|
||||||
DnsIP() string
|
DnsIP() string
|
||||||
UpdateDNSServer(serial uint64, update nbdns.Config) error
|
UpdateDNSServer(serial uint64, update nbdns.Config) error
|
||||||
@@ -50,6 +55,9 @@ type DefaultServer struct {
|
|||||||
hostsDnsList []string
|
hostsDnsList []string
|
||||||
hostsDnsListLock sync.Mutex
|
hostsDnsListLock sync.Mutex
|
||||||
|
|
||||||
|
interfaceName string
|
||||||
|
wgAddr string
|
||||||
|
|
||||||
// make sense on mobile only
|
// make sense on mobile only
|
||||||
searchDomainNotifier *notifier
|
searchDomainNotifier *notifier
|
||||||
}
|
}
|
||||||
@@ -65,7 +73,7 @@ type muxUpdate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultServer returns a new dns server
|
// NewDefaultServer returns a new dns server
|
||||||
func NewDefaultServer(ctx context.Context, wgInterface WGIface, customAddress string) (*DefaultServer, error) {
|
func NewDefaultServer(ctx context.Context, wgInterface WGIface, customAddress string, interfaceName string, wgAddr string) (*DefaultServer, error) {
|
||||||
var addrPort *netip.AddrPort
|
var addrPort *netip.AddrPort
|
||||||
if customAddress != "" {
|
if customAddress != "" {
|
||||||
parsedAddrPort, err := netip.ParseAddrPort(customAddress)
|
parsedAddrPort, err := netip.ParseAddrPort(customAddress)
|
||||||
@@ -82,24 +90,24 @@ func NewDefaultServer(ctx context.Context, wgInterface WGIface, customAddress st
|
|||||||
dnsService = newServiceViaListener(wgInterface, addrPort)
|
dnsService = newServiceViaListener(wgInterface, addrPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newDefaultServer(ctx, wgInterface, dnsService), nil
|
return newDefaultServer(ctx, wgInterface, dnsService, interfaceName, wgAddr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, config nbdns.Config, listener listener.NetworkChangeListener) *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.currentConfig = dnsConfigTohostDNSConfig(config, ds.service.RuntimeIP(), ds.service.RuntimePort())
|
||||||
ds.searchDomainNotifier = newNotifier(ds.SearchDomains())
|
ds.searchDomainNotifier = newNotifier(ds.SearchDomains())
|
||||||
ds.searchDomainNotifier.setListener(listener)
|
ds.searchDomainNotifier.setListener(listener)
|
||||||
setServerDns(ds)
|
setServerDns(ds)
|
||||||
return ds
|
return ds
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService service) *DefaultServer {
|
func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService service, interfaceName string, wgAddr string) *DefaultServer {
|
||||||
ctx, stop := context.WithCancel(ctx)
|
ctx, stop := context.WithCancel(ctx)
|
||||||
defaultServer := &DefaultServer{
|
defaultServer := &DefaultServer{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
@@ -110,6 +118,8 @@ func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService servi
|
|||||||
registeredMap: make(registrationMap),
|
registeredMap: make(registrationMap),
|
||||||
},
|
},
|
||||||
wgInterface: wgInterface,
|
wgInterface: wgInterface,
|
||||||
|
interfaceName: interfaceName,
|
||||||
|
wgAddr: wgAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultServer
|
return defaultServer
|
||||||
@@ -124,15 +134,8 @@ func (s *DefaultServer) Initialize() (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.permanent {
|
s.hostManager, err = s.initialize()
|
||||||
err = s.service.Listen()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.hostManager, err = newHostManager(s.wgInterface)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DnsIP returns the DNS resolver server IP address
|
// DnsIP returns the DNS resolver server IP address
|
||||||
@@ -256,7 +259,7 @@ func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
|
|||||||
|
|
||||||
s.updateMux(muxUpdates)
|
s.updateMux(muxUpdates)
|
||||||
s.updateLocalResolver(localRecords)
|
s.updateLocalResolver(localRecords)
|
||||||
s.currentConfig = dnsConfigToHostDNSConfig(update, s.service.RuntimeIP(), s.service.RuntimePort())
|
s.currentConfig = dnsConfigTohostDNSConfig(update, s.service.RuntimeIP(), s.service.RuntimePort())
|
||||||
|
|
||||||
hostUpdate := s.currentConfig
|
hostUpdate := s.currentConfig
|
||||||
if s.service.RuntimePort() != defaultPort && !s.hostManager.supportCustomPort() {
|
if s.service.RuntimePort() != defaultPort && !s.hostManager.supportCustomPort() {
|
||||||
@@ -312,7 +315,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := newUpstreamResolver(s.ctx)
|
handler := newUpstreamResolver(s.ctx, s.interfaceName, s.wgAddr)
|
||||||
for _, ns := range nsGroup.NameServers {
|
for _, ns := range nsGroup.NameServers {
|
||||||
if ns.NSType != nbdns.UDPNameServerType {
|
if ns.NSType != nbdns.UDPNameServerType {
|
||||||
log.Warnf("skipping nameserver %s with type %s, this peer supports only %s",
|
log.Warnf("skipping nameserver %s with type %s, this peer supports only %s",
|
||||||
@@ -485,7 +488,7 @@ func (s *DefaultServer) upstreamCallbacks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultServer) addHostRootZone() {
|
func (s *DefaultServer) addHostRootZone() {
|
||||||
handler := newUpstreamResolver(s.ctx)
|
handler := newUpstreamResolver(s.ctx, s.interfaceName, s.wgAddr)
|
||||||
handler.upstreamServers = make([]string, len(s.hostsDnsList))
|
handler.upstreamServers = make([]string, len(s.hostsDnsList))
|
||||||
for n, ua := range s.hostsDnsList {
|
for n, ua := range s.hostsDnsList {
|
||||||
a, err := netip.ParseAddr(ua)
|
a, err := netip.ParseAddr(ua)
|
||||||
|
|||||||
10
client/internal/dns/server_android.go
Normal file
10
client/internal/dns/server_android.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
||||||
|
err = s.service.Listen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newHostManager()
|
||||||
|
}
|
||||||
5
client/internal/dns/server_darwin.go
Normal file
5
client/internal/dns/server_darwin.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
||||||
|
return newHostManager()
|
||||||
|
}
|
||||||
6
client/internal/dns/server_ios.go
Normal file
6
client/internal/dns/server_ios.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
||||||
|
// todo add ioDnsManager to constuctor
|
||||||
|
return newHostManager(m.ioDnsManager)
|
||||||
|
}
|
||||||
5
client/internal/dns/server_linux.go
Normal file
5
client/internal/dns/server_linux.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
||||||
|
return newHostManager(s.wgInterface)
|
||||||
|
}
|
||||||
@@ -268,11 +268,11 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
t.Log(err)
|
t.Log(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
dnsServer, err := NewDefaultServer(context.Background(), wgIface, "")
|
dnsServer, err := NewDefaultServer(context.Background(), wgIface, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = dnsServer.Initialize()
|
err = dnsServer.Initialize(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -368,13 +368,13 @@ func TestDNSFakeResolverHandleUpdates(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsServer, err := NewDefaultServer(context.Background(), wgIface, "")
|
dnsServer, err := NewDefaultServer(context.Background(), wgIface, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("create DNS server: %v", err)
|
t.Errorf("create DNS server: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dnsServer.Initialize()
|
err = dnsServer.Initialize(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("run DNS server: %v", err)
|
t.Errorf("run DNS server: %v", err)
|
||||||
return
|
return
|
||||||
@@ -463,7 +463,7 @@ func TestDNSServerStartStop(t *testing.T) {
|
|||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
dnsServer, err := NewDefaultServer(context.Background(), &mocWGIface{}, testCase.addrPort)
|
dnsServer, err := NewDefaultServer(context.Background(), &mocWGIface{}, testCase.addrPort, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("%v", err)
|
t.Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
@@ -595,7 +595,7 @@ func TestDNSPermanent_updateHostDNS_emptyUpstream(t *testing.T) {
|
|||||||
var dnsList []string
|
var dnsList []string
|
||||||
dnsConfig := nbdns.Config{}
|
dnsConfig := nbdns.Config{}
|
||||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList, dnsConfig, nil)
|
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList, dnsConfig, nil)
|
||||||
err = dnsServer.Initialize()
|
err = dnsServer.Initialize(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to initialize DNS server: %v", err)
|
t.Errorf("failed to initialize DNS server: %v", err)
|
||||||
return
|
return
|
||||||
@@ -619,7 +619,7 @@ func TestDNSPermanent_updateUpstream(t *testing.T) {
|
|||||||
defer wgIFace.Close()
|
defer wgIFace.Close()
|
||||||
dnsConfig := nbdns.Config{}
|
dnsConfig := nbdns.Config{}
|
||||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
|
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
|
||||||
err = dnsServer.Initialize()
|
err = dnsServer.Initialize(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to initialize DNS server: %v", err)
|
t.Errorf("failed to initialize DNS server: %v", err)
|
||||||
return
|
return
|
||||||
@@ -711,7 +711,7 @@ func TestDNSPermanent_matchOnly(t *testing.T) {
|
|||||||
defer wgIFace.Close()
|
defer wgIFace.Close()
|
||||||
dnsConfig := nbdns.Config{}
|
dnsConfig := nbdns.Config{}
|
||||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
|
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
|
||||||
err = dnsServer.Initialize()
|
err = dnsServer.Initialize(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to initialize DNS server: %v", err)
|
t.Errorf("failed to initialize DNS server: %v", err)
|
||||||
return
|
return
|
||||||
|
|||||||
5
client/internal/dns/server_windows.go
Normal file
5
client/internal/dns/server_windows.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
func (s *DefaultServer) initialize() (manager hostManager, err error) {
|
||||||
|
return newHostManager(s.wgInterface)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -35,20 +36,50 @@ type upstreamResolver struct {
|
|||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
reactivatePeriod time.Duration
|
reactivatePeriod time.Duration
|
||||||
upstreamTimeout time.Duration
|
upstreamTimeout time.Duration
|
||||||
|
lIP net.IP
|
||||||
|
lNet *net.IPNet
|
||||||
|
lName string
|
||||||
|
iIndex int
|
||||||
|
|
||||||
deactivate func()
|
deactivate func()
|
||||||
reactivate func()
|
reactivate func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUpstreamResolver(parentCTX context.Context) *upstreamResolver {
|
func getInterfaceIndex(interfaceName string) (int, error) {
|
||||||
|
iface, err := net.InterfaceByName(interfaceName)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to get interface by name error: %s", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return iface.Index, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUpstreamResolver(parentCTX context.Context, interfaceName string, wgAddr string) *upstreamResolver {
|
||||||
ctx, cancel := context.WithCancel(parentCTX)
|
ctx, cancel := context.WithCancel(parentCTX)
|
||||||
|
|
||||||
|
// Specify the local IP address you want to bind to
|
||||||
|
localIP, localNet, err := net.ParseCIDR(wgAddr) // Should be our interface IP
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error while parsing CIDR: %s", err)
|
||||||
|
}
|
||||||
|
index, err := getInterfaceIndex(interfaceName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("unable to get interface index for %s: %s", interfaceName, err)
|
||||||
|
}
|
||||||
|
localIFaceIndex := index // Should be our interface index
|
||||||
|
|
||||||
return &upstreamResolver{
|
return &upstreamResolver{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
upstreamClient: &dns.Client{},
|
|
||||||
upstreamTimeout: upstreamTimeout,
|
upstreamTimeout: upstreamTimeout,
|
||||||
reactivatePeriod: reactivatePeriod,
|
reactivatePeriod: reactivatePeriod,
|
||||||
failsTillDeact: failsTillDeact,
|
failsTillDeact: failsTillDeact,
|
||||||
|
lIP: localIP,
|
||||||
|
lNet: localNet,
|
||||||
|
iIndex: localIFaceIndex,
|
||||||
|
lName: interfaceName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,26 +101,57 @@ func (u *upstreamResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, upstream := range u.upstreamServers {
|
for _, upstream := range u.upstreamServers {
|
||||||
|
var (
|
||||||
|
exchangeErr error
|
||||||
|
t time.Duration
|
||||||
|
rm *dns.Msg
|
||||||
|
)
|
||||||
|
|
||||||
|
upstreamExchangeClient := &dns.Client{}
|
||||||
|
if runtime.GOOS != "ios" {
|
||||||
ctx, cancel := context.WithTimeout(u.ctx, u.upstreamTimeout)
|
ctx, cancel := context.WithTimeout(u.ctx, u.upstreamTimeout)
|
||||||
rm, t, err := u.upstreamClient.ExchangeContext(ctx, r, upstream)
|
rm, t, exchangeErr = upstreamExchangeClient.ExchangeContext(ctx, r, upstream)
|
||||||
|
|
||||||
cancel()
|
cancel()
|
||||||
|
} else {
|
||||||
|
upstreamHost, _, err := net.SplitHostPort(upstream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == context.DeadlineExceeded || isTimeout(err) {
|
log.Errorf("error while parsing upstream host: %s", err)
|
||||||
log.WithError(err).WithField("upstream", upstream).
|
return
|
||||||
|
}
|
||||||
|
upstreamIP := net.ParseIP(upstreamHost)
|
||||||
|
if u.lNet.Contains(upstreamIP) || net.IP.IsPrivate(upstreamIP) {
|
||||||
|
upstreamExchangeClient = u.getClientPrivate()
|
||||||
|
}
|
||||||
|
rm, t, exchangeErr = upstreamExchangeClient.Exchange(r, upstream)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exchangeErr != nil {
|
||||||
|
if exchangeErr == context.DeadlineExceeded || isTimeout(exchangeErr) {
|
||||||
|
log.WithError(exchangeErr).WithField("upstream", upstream).
|
||||||
Warn("got an error while connecting to upstream")
|
Warn("got an error while connecting to upstream")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
u.failsCount.Add(1)
|
u.failsCount.Add(1)
|
||||||
log.WithError(err).WithField("upstream", upstream).
|
log.WithError(exchangeErr).WithField("upstream", upstream).
|
||||||
Error("got an error while querying the upstream")
|
Error("got other error while querying the upstream")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if rm == nil {
|
||||||
|
log.WithError(exchangeErr).WithField("upstream", upstream).
|
||||||
|
Warn("no response from upstream")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// those checks need to be independent of each other due to memory address issues
|
||||||
|
if !rm.Response {
|
||||||
|
log.WithError(exchangeErr).WithField("upstream", upstream).
|
||||||
|
Warn("no response from upstream")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Tracef("took %s to query the upstream %s", t, upstream)
|
log.Tracef("took %s to query the upstream %s", t, upstream)
|
||||||
|
|
||||||
err = w.WriteMsg(rm)
|
err := w.WriteMsg(rm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("got an error while writing the upstream resolver response")
|
log.WithError(err).Error("got an error while writing the upstream resolver response")
|
||||||
}
|
}
|
||||||
@@ -118,6 +180,7 @@ func (u *upstreamResolver) checkUpstreamFails() {
|
|||||||
case <-u.ctx.Done():
|
case <-u.ctx.Done():
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
|
// todo test the deactivation logic, it seems to affect the client
|
||||||
log.Warnf("upstream resolving is disabled for %v", reactivatePeriod)
|
log.Warnf("upstream resolving is disabled for %v", reactivatePeriod)
|
||||||
u.deactivate()
|
u.deactivate()
|
||||||
u.disabled = true
|
u.disabled = true
|
||||||
|
|||||||
44
client/internal/dns/upstream_ios.go
Normal file
44
client/internal/dns/upstream_ios.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
//go:build ios
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getClientPrivate returns a new DNS client bound to the local IP address of the Netbird interface
|
||||||
|
// This method is needed for iOS
|
||||||
|
func (u *upstreamResolver) getClientPrivate() *dns.Client {
|
||||||
|
dialer := &net.Dialer{
|
||||||
|
LocalAddr: &net.UDPAddr{
|
||||||
|
IP: u.lIP,
|
||||||
|
Port: 0, // Let the OS pick a free port
|
||||||
|
},
|
||||||
|
Timeout: upstreamTimeout,
|
||||||
|
Control: func(network, address string, c syscall.RawConn) error {
|
||||||
|
var operr error
|
||||||
|
fn := func(s uintptr) {
|
||||||
|
operr = unix.SetsockoptInt(int(s), unix.IPPROTO_IP, unix.IP_BOUND_IF, u.iIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Control(fn); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if operr != nil {
|
||||||
|
log.Errorf("error while setting socket option: %s", operr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return operr
|
||||||
|
},
|
||||||
|
}
|
||||||
|
client := &dns.Client{
|
||||||
|
Dialer: dialer,
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
19
client/internal/dns/upstream_nonios.go
Normal file
19
client/internal/dns/upstream_nonios.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//go:build !ios
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getClientPrivate returns a new DNS client bound to the local IP address of the Netbird interface
|
||||||
|
// This method is needed for iOS
|
||||||
|
func (u *upstreamResolver) getClientPrivate() *dns.Client {
|
||||||
|
dialer := &net.Dialer{}
|
||||||
|
client := &dns.Client{
|
||||||
|
Dialer: dialer,
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
@@ -49,15 +49,15 @@ func TestUpstreamResolver_ServeDNS(t *testing.T) {
|
|||||||
timeout: upstreamTimeout,
|
timeout: upstreamTimeout,
|
||||||
responseShouldBeNil: true,
|
responseShouldBeNil: true,
|
||||||
},
|
},
|
||||||
//{
|
// {
|
||||||
// name: "Should Resolve CNAME Record",
|
// name: "Should Resolve CNAME Record",
|
||||||
// inputMSG: new(dns.Msg).SetQuestion("one.one.one.one", dns.TypeCNAME),
|
// inputMSG: new(dns.Msg).SetQuestion("one.one.one.one", dns.TypeCNAME),
|
||||||
//},
|
// },
|
||||||
//{
|
// {
|
||||||
// name: "Should Not Write When Not Found A Record",
|
// name: "Should Not Write When Not Found A Record",
|
||||||
// inputMSG: new(dns.Msg).SetQuestion("not.found.com", dns.TypeA),
|
// inputMSG: new(dns.Msg).SetQuestion("not.found.com", dns.TypeA),
|
||||||
// responseShouldBeNil: true,
|
// responseShouldBeNil: true,
|
||||||
//},
|
// },
|
||||||
}
|
}
|
||||||
// should resolve if first upstream times out
|
// should resolve if first upstream times out
|
||||||
// should not write when both fails
|
// should not write when both fails
|
||||||
@@ -66,7 +66,7 @@ func TestUpstreamResolver_ServeDNS(t *testing.T) {
|
|||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
ctx, cancel := context.WithCancel(context.TODO())
|
ctx, cancel := context.WithCancel(context.TODO())
|
||||||
resolver := newUpstreamResolver(ctx)
|
resolver := newUpstreamResolver(ctx, "", "")
|
||||||
resolver.upstreamServers = testCase.InputServers
|
resolver.upstreamServers = testCase.InputServers
|
||||||
resolver.upstreamTimeout = testCase.timeout
|
resolver.upstreamTimeout = testCase.timeout
|
||||||
if testCase.cancelCTX {
|
if testCase.cancelCTX {
|
||||||
|
|||||||
@@ -205,8 +205,7 @@ func (e *Engine) Start() error {
|
|||||||
go e.mobileDep.DnsReadyListener.OnReady()
|
go e.mobileDep.DnsReadyListener.OnReady()
|
||||||
}
|
}
|
||||||
} else if e.dnsServer == nil {
|
} else if e.dnsServer == nil {
|
||||||
// todo fix custom address
|
e.dnsServer, err = dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress, e.mobileDep.InterfaceName, wgAddr)
|
||||||
e.dnsServer, err = dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.close()
|
e.close()
|
||||||
return err
|
return err
|
||||||
@@ -216,13 +215,17 @@ 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.NetworkChangeListener)
|
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
|
||||||
|
|
||||||
if runtime.GOOS == "android" {
|
switch runtime.GOOS {
|
||||||
err = e.wgInterface.CreateOnMobile(iface.MobileIFaceArguments{
|
case "android":
|
||||||
|
err = e.wgInterface.CreateOnAndroid(iface.MobileIFaceArguments{
|
||||||
Routes: e.routeManager.InitialRouteRange(),
|
Routes: e.routeManager.InitialRouteRange(),
|
||||||
Dns: e.dnsServer.DnsIP(),
|
Dns: e.dnsServer.DnsIP(),
|
||||||
SearchDomains: e.dnsServer.SearchDomains(),
|
SearchDomains: e.dnsServer.SearchDomains(),
|
||||||
})
|
})
|
||||||
} else {
|
case "ios":
|
||||||
|
e.mobileDep.NetworkChangeListener.SetInterfaceIP(wgAddr)
|
||||||
|
err = e.wgInterface.CreateOniOS(e.mobileDep.FileDescriptor)
|
||||||
|
default:
|
||||||
err = e.wgInterface.Create()
|
err = e.wgInterface.Create()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -264,7 +267,11 @@ func (e *Engine) Start() error {
|
|||||||
e.acl = acl
|
e.acl = acl
|
||||||
}
|
}
|
||||||
|
|
||||||
err = e.dnsServer.Initialize()
|
if runtime.GOOS == "ios" {
|
||||||
|
err = e.dnsServer.Initialize(e.mobileDep.DnsManager)
|
||||||
|
} else {
|
||||||
|
err = e.dnsServer.Initialize(nil)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.close()
|
e.close()
|
||||||
return err
|
return err
|
||||||
@@ -466,7 +473,7 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error {
|
|||||||
}
|
}
|
||||||
// start SSH server if it wasn't running
|
// start SSH server if it wasn't running
|
||||||
if isNil(e.sshServer) {
|
if isNil(e.sshServer) {
|
||||||
//nil sshServer means it has not yet been started
|
// nil sshServer means it has not yet been started
|
||||||
var err error
|
var err error
|
||||||
e.sshServer, err = e.sshServerFunc(e.config.SSHKey,
|
e.sshServer, err = e.sshServerFunc(e.config.SSHKey,
|
||||||
fmt.Sprintf("%s:%d", e.wgInterface.Address().IP.String(), nbssh.DefaultSSHPort))
|
fmt.Sprintf("%s:%d", e.wgInterface.Address().IP.String(), nbssh.DefaultSSHPort))
|
||||||
|
|||||||
@@ -3,5 +3,6 @@ package listener
|
|||||||
// NetworkChangeListener is a callback interface for mobile system
|
// NetworkChangeListener is a callback interface for mobile system
|
||||||
type NetworkChangeListener interface {
|
type NetworkChangeListener interface {
|
||||||
// OnNetworkChanged invoke when network settings has been changed
|
// OnNetworkChanged invoke when network settings has been changed
|
||||||
OnNetworkChanged()
|
OnNetworkChanged(string)
|
||||||
|
SetInterfaceIP(string)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,4 +14,7 @@ type MobileDependency struct {
|
|||||||
NetworkChangeListener listener.NetworkChangeListener
|
NetworkChangeListener listener.NetworkChangeListener
|
||||||
HostDNSAddresses []string
|
HostDNSAddresses []string
|
||||||
DnsReadyListener dns.ReadyListener
|
DnsReadyListener dns.ReadyListener
|
||||||
|
DnsManager dns.IosDnsManager
|
||||||
|
FileDescriptor int32
|
||||||
|
InterfaceName string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -225,6 +226,10 @@ func (conn *Conn) candidateTypes() []ice.CandidateType {
|
|||||||
if hasICEForceRelayConn() {
|
if hasICEForceRelayConn() {
|
||||||
return []ice.CandidateType{ice.CandidateTypeRelay}
|
return []ice.CandidateType{ice.CandidateTypeRelay}
|
||||||
}
|
}
|
||||||
|
// TODO: remove this once we have refactored userspace proxy into the bind package
|
||||||
|
if runtime.GOOS == "ios" {
|
||||||
|
return []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive}
|
||||||
|
}
|
||||||
return []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay}
|
return []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,7 +469,7 @@ func (conn *Conn) cleanup() error {
|
|||||||
err := conn.statusRecorder.UpdatePeerState(peerState)
|
err := conn.statusRecorder.UpdatePeerState(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// pretty common error because by that time Engine can already remove the peer and status won't be available.
|
// pretty common error because by that time Engine can already remove the peer and status won't be available.
|
||||||
//todo rethink status updates
|
// todo rethink status updates
|
||||||
log.Debugf("error while updating peer's %s state, err: %v", conn.config.Key, err)
|
log.Debugf("error while updating peer's %s state, err: %v", conn.config.Key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
31
client/internal/routemanager/firewall_ios.go
Normal file
31
client/internal/routemanager/firewall_ios.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
//go:build ios
|
||||||
|
|
||||||
|
package routemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newFirewall returns a nil manager
|
||||||
|
func newFirewall(context.Context) (firewallManager, error) {
|
||||||
|
return iOSFirewallManager{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type iOSFirewallManager struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i iOSFirewallManager) RestoreOrCreateContainers() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i iOSFirewallManager) InsertRoutingRules(pair routerPair) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i iOSFirewallManager) RemoveRoutingRules(pair routerPair) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i iOSFirewallManager) CleanRoutingRules() {
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build !linux
|
//go:build !linux && !ios
|
||||||
// +build !linux
|
// +build !linux,!ios
|
||||||
|
|
||||||
package routemanager
|
package routemanager
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package routemanager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/listener"
|
"github.com/netbirdio/netbird/client/internal/listener"
|
||||||
@@ -50,9 +51,6 @@ func (n *notifier) onNewRoutes(idMap map[string][]*route.Route) {
|
|||||||
|
|
||||||
n.routeRangers = newNets
|
n.routeRangers = newNets
|
||||||
|
|
||||||
if !n.hasDiff(n.initialRouteRangers, newNets) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n.notify()
|
n.notify()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +62,7 @@ func (n *notifier) notify() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func(l listener.NetworkChangeListener) {
|
go func(l listener.NetworkChangeListener) {
|
||||||
l.OnNetworkChanged()
|
l.OnNetworkChanged(strings.Join(n.routeRangers, ","))
|
||||||
}(n.listener)
|
}(n.listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build android
|
||||||
|
|
||||||
package routemanager
|
package routemanager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
15
client/internal/routemanager/systemops_ios.go
Normal file
15
client/internal/routemanager/systemops_ios.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build ios
|
||||||
|
|
||||||
|
package routemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addToRouteTableIfNoExists(prefix netip.Prefix, addr string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeFromRouteTableIfNonSystem(prefix netip.Prefix, addr string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !android
|
//go:build !android && !ios
|
||||||
|
|
||||||
package routemanager
|
package routemanager
|
||||||
|
|
||||||
|
|||||||
223
client/ios/NetBirdSDK/client.go
Normal file
223
client/ios/NetBirdSDK/client.go
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
package NetBirdSDK
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/auth"
|
||||||
|
"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/system"
|
||||||
|
"github.com/netbirdio/netbird/formatter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnectionListener export internal Listener for mobile
|
||||||
|
type ConnectionListener interface {
|
||||||
|
peer.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// RouteListener export internal RouteListener for mobile
|
||||||
|
type NetworkChangeListener interface {
|
||||||
|
listener.NetworkChangeListener
|
||||||
|
}
|
||||||
|
|
||||||
|
// DnsManager export internal dns Manager for mobile
|
||||||
|
type DnsManager interface {
|
||||||
|
dns.IosDnsManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomLogger export internal CustomLogger for mobile
|
||||||
|
type CustomLogger interface {
|
||||||
|
Debug(message string)
|
||||||
|
Info(message string)
|
||||||
|
Error(message string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
formatter.SetLogcatFormatter(log.StandardLogger())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client struct manage the life circle of background service
|
||||||
|
type Client struct {
|
||||||
|
cfgFile string
|
||||||
|
recorder *peer.Status
|
||||||
|
ctxCancel context.CancelFunc
|
||||||
|
ctxCancelLock *sync.Mutex
|
||||||
|
deviceName string
|
||||||
|
osName string
|
||||||
|
osVersion string
|
||||||
|
networkChangeListener listener.NetworkChangeListener
|
||||||
|
onHostDnsFn func([]string)
|
||||||
|
dnsManager dns.IosDnsManager
|
||||||
|
loginComplete bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient instantiate a new Client
|
||||||
|
func NewClient(cfgFile, deviceName string, osVersion string, osName string, networkChangeListener NetworkChangeListener, dnsManager DnsManager) *Client {
|
||||||
|
return &Client{
|
||||||
|
cfgFile: cfgFile,
|
||||||
|
deviceName: deviceName,
|
||||||
|
osName: osName,
|
||||||
|
osVersion: osVersion,
|
||||||
|
recorder: peer.NewRecorder(""),
|
||||||
|
ctxCancelLock: &sync.Mutex{},
|
||||||
|
networkChangeListener: networkChangeListener,
|
||||||
|
dnsManager: dnsManager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run start the internal client. It is a blocker function
|
||||||
|
func (c *Client) Run(fd int32, interfaceName string) error {
|
||||||
|
log.Infof("Starting NetBird client")
|
||||||
|
log.Debugf("Tunnel uses interface: %s", interfaceName)
|
||||||
|
cfg, err := internal.UpdateOrCreateConfig(internal.ConfigInput{
|
||||||
|
ConfigPath: c.cfgFile,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
//nolint
|
||||||
|
ctxWithValues := context.WithValue(context.Background(), system.DeviceNameCtxKey, c.deviceName)
|
||||||
|
//nolint
|
||||||
|
ctxWithValues = context.WithValue(ctxWithValues, system.OsNameCtxKey, c.osName)
|
||||||
|
//nolint
|
||||||
|
ctxWithValues = context.WithValue(ctxWithValues, system.OsVersionCtxKey, c.osVersion)
|
||||||
|
c.ctxCancelLock.Lock()
|
||||||
|
ctx, c.ctxCancel = context.WithCancel(ctxWithValues)
|
||||||
|
defer c.ctxCancel()
|
||||||
|
c.ctxCancelLock.Unlock()
|
||||||
|
|
||||||
|
auth := NewAuthWithConfig(ctx, cfg)
|
||||||
|
err = auth.Login()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Auth successful")
|
||||||
|
// todo do not throw error in case of cancelled context
|
||||||
|
ctx = internal.CtxInitState(ctx)
|
||||||
|
c.onHostDnsFn = func([]string) {}
|
||||||
|
return internal.RunClientiOS(ctx, cfg, c.recorder, fd, c.networkChangeListener, c.dnsManager, interfaceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the internal client and free the resources
|
||||||
|
func (c *Client) Stop() {
|
||||||
|
c.ctxCancelLock.Lock()
|
||||||
|
defer c.ctxCancelLock.Unlock()
|
||||||
|
if c.ctxCancel == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ctxCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ÏSetTraceLogLevel configure the logger to trace level
|
||||||
|
func (c *Client) SetTraceLogLevel() {
|
||||||
|
log.SetLevel(log.TraceLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getStatusDetails return with the list of the PeerInfos
|
||||||
|
func (c *Client) GetStatusDetails() *StatusDetails {
|
||||||
|
|
||||||
|
fullStatus := c.recorder.GetFullStatus()
|
||||||
|
|
||||||
|
peerInfos := make([]PeerInfo, len(fullStatus.Peers))
|
||||||
|
for n, p := range fullStatus.Peers {
|
||||||
|
pi := PeerInfo{
|
||||||
|
p.IP,
|
||||||
|
p.FQDN,
|
||||||
|
p.ConnStatus.String(),
|
||||||
|
}
|
||||||
|
peerInfos[n] = pi
|
||||||
|
}
|
||||||
|
return &StatusDetails{items: peerInfos, fqdn: fullStatus.LocalPeerState.FQDN, ip: fullStatus.LocalPeerState.IP}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConnectionListener set the network connection listener
|
||||||
|
func (c *Client) SetConnectionListener(listener ConnectionListener) {
|
||||||
|
c.recorder.SetConnectionListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveConnectionListener remove connection listener
|
||||||
|
func (c *Client) RemoveConnectionListener() {
|
||||||
|
c.recorder.RemoveConnectionListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) IsLoginRequired() bool {
|
||||||
|
var ctx context.Context
|
||||||
|
//nolint
|
||||||
|
ctxWithValues := context.WithValue(context.Background(), system.DeviceNameCtxKey, c.deviceName)
|
||||||
|
//nolint
|
||||||
|
ctxWithValues = context.WithValue(ctxWithValues, system.OsNameCtxKey, c.osName)
|
||||||
|
//nolint
|
||||||
|
ctxWithValues = context.WithValue(ctxWithValues, system.OsVersionCtxKey, c.osVersion)
|
||||||
|
c.ctxCancelLock.Lock()
|
||||||
|
defer c.ctxCancelLock.Unlock()
|
||||||
|
ctx, c.ctxCancel = context.WithCancel(ctxWithValues)
|
||||||
|
|
||||||
|
cfg, _ := internal.UpdateOrCreateConfig(internal.ConfigInput{
|
||||||
|
ConfigPath: c.cfgFile,
|
||||||
|
})
|
||||||
|
|
||||||
|
needsLogin, _ := internal.IsLoginRequired(ctx, cfg.PrivateKey, cfg.ManagementURL, cfg.SSHKey)
|
||||||
|
return needsLogin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) LoginForMobile() string {
|
||||||
|
var ctx context.Context
|
||||||
|
//nolint
|
||||||
|
ctxWithValues := context.WithValue(context.Background(), system.DeviceNameCtxKey, c.deviceName)
|
||||||
|
//nolint
|
||||||
|
ctxWithValues = context.WithValue(ctxWithValues, system.OsNameCtxKey, c.osName)
|
||||||
|
//nolint
|
||||||
|
ctxWithValues = context.WithValue(ctxWithValues, system.OsVersionCtxKey, c.osVersion)
|
||||||
|
c.ctxCancelLock.Lock()
|
||||||
|
defer c.ctxCancelLock.Unlock()
|
||||||
|
ctx, c.ctxCancel = context.WithCancel(ctxWithValues)
|
||||||
|
|
||||||
|
cfg, _ := internal.UpdateOrCreateConfig(internal.ConfigInput{
|
||||||
|
ConfigPath: c.cfgFile,
|
||||||
|
})
|
||||||
|
|
||||||
|
oAuthFlow, err := auth.NewOAuthFlow(ctx, cfg, false)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
flowInfo, err := oAuthFlow.RequestAuthInfo(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// This could cause a potential race condition with loading the extension which need to be handled on swift side
|
||||||
|
go func() {
|
||||||
|
waitTimeout := time.Duration(flowInfo.ExpiresIn) * time.Second
|
||||||
|
waitCTX, cancel := context.WithTimeout(ctx, waitTimeout)
|
||||||
|
defer cancel()
|
||||||
|
tokenInfo, err := oAuthFlow.WaitToken(waitCTX, flowInfo)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jwtToken := tokenInfo.GetTokenToUse()
|
||||||
|
_ = internal.Login(ctx, cfg, "", jwtToken)
|
||||||
|
c.loginComplete = true
|
||||||
|
}()
|
||||||
|
|
||||||
|
return flowInfo.VerificationURIComplete
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) IsLoginComplete() bool {
|
||||||
|
return c.loginComplete
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ClearLoginComplete() {
|
||||||
|
c.loginComplete = false
|
||||||
|
}
|
||||||
5
client/ios/NetBirdSDK/gomobile.go
Normal file
5
client/ios/NetBirdSDK/gomobile.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package NetBirdSDK
|
||||||
|
|
||||||
|
import _ "golang.org/x/mobile/bind"
|
||||||
|
|
||||||
|
// to keep our CI/CD that checks go.mod and go.sum files happy, we need to import the package above
|
||||||
10
client/ios/NetBirdSDK/logger.go
Normal file
10
client/ios/NetBirdSDK/logger.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package NetBirdSDK
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitializeLog initializes the log file.
|
||||||
|
func InitializeLog(logLevel string, filePath string) error {
|
||||||
|
return util.InitLog(logLevel, filePath)
|
||||||
|
}
|
||||||
159
client/ios/NetBirdSDK/login.go
Normal file
159
client/ios/NetBirdSDK/login.go
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
package NetBirdSDK
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cenkalti/backoff/v4"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
gstatus "google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/cmd"
|
||||||
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
|
"github.com/netbirdio/netbird/client/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SSOListener is async listener for mobile framework
|
||||||
|
type SSOListener interface {
|
||||||
|
OnSuccess(bool)
|
||||||
|
OnError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrListener is async listener for mobile framework
|
||||||
|
type ErrListener interface {
|
||||||
|
OnSuccess()
|
||||||
|
OnError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// URLOpener it is a callback interface. The Open function will be triggered if
|
||||||
|
// the backend want to show an url for the user
|
||||||
|
type URLOpener interface {
|
||||||
|
Open(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth can register or login new client
|
||||||
|
type Auth struct {
|
||||||
|
ctx context.Context
|
||||||
|
config *internal.Config
|
||||||
|
cfgPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuth instantiate Auth struct and validate the management URL
|
||||||
|
func NewAuth(cfgPath string, mgmURL string) (*Auth, error) {
|
||||||
|
inputCfg := internal.ConfigInput{
|
||||||
|
ManagementURL: mgmURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := internal.CreateInMemoryConfig(inputCfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Auth{
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: cfg,
|
||||||
|
cfgPath: cfgPath,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthWithConfig instantiate Auth based on existing config
|
||||||
|
func NewAuthWithConfig(ctx context.Context, config *internal.Config) *Auth {
|
||||||
|
return &Auth{
|
||||||
|
ctx: ctx,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveConfigIfSSOSupported test the connectivity with the management server by retrieving the server device flow info.
|
||||||
|
// If it returns a flow info than save the configuration and return true. If it gets a codes.NotFound, it means that SSO
|
||||||
|
// is not supported and returns false without saving the configuration. For other errors return false.
|
||||||
|
func (a *Auth) SaveConfigIfSSOSupported() (bool, error) {
|
||||||
|
supportsSSO := true
|
||||||
|
err := a.withBackOff(a.ctx, func() (err error) {
|
||||||
|
_, err = internal.GetDeviceAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
|
||||||
|
if s, ok := gstatus.FromError(err); ok && s.Code() == codes.NotFound {
|
||||||
|
_, err = internal.GetPKCEAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
|
||||||
|
if s, ok := gstatus.FromError(err); ok && s.Code() == codes.NotFound {
|
||||||
|
supportsSSO = false
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
if !supportsSSO {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("backoff cycle failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = internal.WriteOutConfig(a.cfgPath, a.config)
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginWithSetupKeyAndSaveConfig test the connectivity with the management server with the setup key.
|
||||||
|
func (a *Auth) LoginWithSetupKeyAndSaveConfig(setupKey string, deviceName string) error {
|
||||||
|
//nolint
|
||||||
|
ctxWithValues := context.WithValue(a.ctx, system.DeviceNameCtxKey, deviceName)
|
||||||
|
|
||||||
|
err := a.withBackOff(a.ctx, func() error {
|
||||||
|
backoffErr := internal.Login(ctxWithValues, a.config, setupKey, "")
|
||||||
|
if s, ok := gstatus.FromError(backoffErr); ok && (s.Code() == codes.PermissionDenied) {
|
||||||
|
// we got an answer from management, exit backoff earlier
|
||||||
|
return backoff.Permanent(backoffErr)
|
||||||
|
}
|
||||||
|
return backoffErr
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("backoff cycle failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return internal.WriteOutConfig(a.cfgPath, a.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Auth) Login() error {
|
||||||
|
var needsLogin bool
|
||||||
|
|
||||||
|
// check if we need to generate JWT token
|
||||||
|
err := a.withBackOff(a.ctx, func() (err error) {
|
||||||
|
needsLogin, err = internal.IsLoginRequired(a.ctx, a.config.PrivateKey, a.config.ManagementURL, a.config.SSHKey)
|
||||||
|
return
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("backoff cycle failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jwtToken := ""
|
||||||
|
if needsLogin {
|
||||||
|
return fmt.Errorf("Not authenticated")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.withBackOff(a.ctx, func() error {
|
||||||
|
err := internal.Login(a.ctx, a.config, "", jwtToken)
|
||||||
|
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.InvalidArgument || s.Code() == codes.PermissionDenied) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("backoff cycle failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Auth) withBackOff(ctx context.Context, bf func() error) error {
|
||||||
|
return backoff.RetryNotify(
|
||||||
|
bf,
|
||||||
|
backoff.WithContext(cmd.CLIBackOffSettings, ctx),
|
||||||
|
func(err error, duration time.Duration) {
|
||||||
|
log.Warnf("retrying Login to the Management service in %v due to error %v", duration, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
50
client/ios/NetBirdSDK/peer_notifier.go
Normal file
50
client/ios/NetBirdSDK/peer_notifier.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package NetBirdSDK
|
||||||
|
|
||||||
|
// PeerInfo describe information about the peers. It designed for the UI usage
|
||||||
|
type PeerInfo struct {
|
||||||
|
IP string
|
||||||
|
FQDN string
|
||||||
|
ConnStatus string // Todo replace to enum
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerInfoCollection made for Java layer to get non default types as collection
|
||||||
|
type PeerInfoCollection interface {
|
||||||
|
Add(s string) PeerInfoCollection
|
||||||
|
Get(i int) string
|
||||||
|
Size() int
|
||||||
|
GetFQDN() string
|
||||||
|
GetIP() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusDetails is the implementation of the PeerInfoCollection
|
||||||
|
type StatusDetails struct {
|
||||||
|
items []PeerInfo
|
||||||
|
fqdn string
|
||||||
|
ip string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new PeerInfo to the collection
|
||||||
|
func (array StatusDetails) Add(s PeerInfo) StatusDetails {
|
||||||
|
array.items = append(array.items, s)
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get return an element of the collection
|
||||||
|
func (array StatusDetails) Get(i int) *PeerInfo {
|
||||||
|
return &array.items[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size return with the size of the collection
|
||||||
|
func (array StatusDetails) Size() int {
|
||||||
|
return len(array.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFQDN return with the FQDN of the local peer
|
||||||
|
func (array StatusDetails) GetFQDN() string {
|
||||||
|
return array.fqdn
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIP return with the IP of the local peer
|
||||||
|
func (array StatusDetails) GetIP() string {
|
||||||
|
return array.ip
|
||||||
|
}
|
||||||
78
client/ios/NetBirdSDK/preferences.go
Normal file
78
client/ios/NetBirdSDK/preferences.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package NetBirdSDK
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Preferences export a subset of the internal config for gomobile
|
||||||
|
type Preferences struct {
|
||||||
|
configInput internal.ConfigInput
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPreferences create new Preferences instance
|
||||||
|
func NewPreferences(configPath string) *Preferences {
|
||||||
|
ci := internal.ConfigInput{
|
||||||
|
ConfigPath: configPath,
|
||||||
|
}
|
||||||
|
return &Preferences{ci}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetManagementURL read url from config file
|
||||||
|
func (p *Preferences) GetManagementURL() (string, error) {
|
||||||
|
if p.configInput.ManagementURL != "" {
|
||||||
|
return p.configInput.ManagementURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return cfg.ManagementURL.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetManagementURL store the given url and wait for commit
|
||||||
|
func (p *Preferences) SetManagementURL(url string) {
|
||||||
|
p.configInput.ManagementURL = url
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAdminURL read url from config file
|
||||||
|
func (p *Preferences) GetAdminURL() (string, error) {
|
||||||
|
if p.configInput.AdminURL != "" {
|
||||||
|
return p.configInput.AdminURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return cfg.AdminURL.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAdminURL store the given url and wait for commit
|
||||||
|
func (p *Preferences) SetAdminURL(url string) {
|
||||||
|
p.configInput.AdminURL = url
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPreSharedKey read preshared key from config file
|
||||||
|
func (p *Preferences) GetPreSharedKey() (string, error) {
|
||||||
|
if p.configInput.PreSharedKey != nil {
|
||||||
|
return *p.configInput.PreSharedKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return cfg.PreSharedKey, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPreSharedKey store the given key and wait for commit
|
||||||
|
func (p *Preferences) SetPreSharedKey(key string) {
|
||||||
|
p.configInput.PreSharedKey = &key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit write out the changes into config file
|
||||||
|
func (p *Preferences) Commit() error {
|
||||||
|
_, err := internal.UpdateOrCreateConfig(p.configInput)
|
||||||
|
return err
|
||||||
|
}
|
||||||
120
client/ios/NetBirdSDK/preferences_test.go
Normal file
120
client/ios/NetBirdSDK/preferences_test.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package NetBirdSDK
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPreferences_DefaultValues(t *testing.T) {
|
||||||
|
cfgFile := filepath.Join(t.TempDir(), "netbird.json")
|
||||||
|
p := NewPreferences(cfgFile)
|
||||||
|
defaultVar, err := p.GetAdminURL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read default value: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if defaultVar != internal.DefaultAdminURL {
|
||||||
|
t.Errorf("invalid default admin url: %s", defaultVar)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultVar, err = p.GetManagementURL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read default management URL: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if defaultVar != internal.DefaultManagementURL {
|
||||||
|
t.Errorf("invalid default management url: %s", defaultVar)
|
||||||
|
}
|
||||||
|
|
||||||
|
var preSharedKey string
|
||||||
|
preSharedKey, err = p.GetPreSharedKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read default preshared key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if preSharedKey != "" {
|
||||||
|
t.Errorf("invalid preshared key: %s", preSharedKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPreferences_ReadUncommitedValues(t *testing.T) {
|
||||||
|
exampleString := "exampleString"
|
||||||
|
cfgFile := filepath.Join(t.TempDir(), "netbird.json")
|
||||||
|
p := NewPreferences(cfgFile)
|
||||||
|
|
||||||
|
p.SetAdminURL(exampleString)
|
||||||
|
resp, err := p.GetAdminURL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read admin url: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != exampleString {
|
||||||
|
t.Errorf("unexpected admin url: %s", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SetManagementURL(exampleString)
|
||||||
|
resp, err = p.GetManagementURL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read management url: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != exampleString {
|
||||||
|
t.Errorf("unexpected management url: %s", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SetPreSharedKey(exampleString)
|
||||||
|
resp, err = p.GetPreSharedKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read preshared key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != exampleString {
|
||||||
|
t.Errorf("unexpected preshared key: %s", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPreferences_Commit(t *testing.T) {
|
||||||
|
exampleURL := "https://myurl.com:443"
|
||||||
|
examplePresharedKey := "topsecret"
|
||||||
|
cfgFile := filepath.Join(t.TempDir(), "netbird.json")
|
||||||
|
p := NewPreferences(cfgFile)
|
||||||
|
|
||||||
|
p.SetAdminURL(exampleURL)
|
||||||
|
p.SetManagementURL(exampleURL)
|
||||||
|
p.SetPreSharedKey(examplePresharedKey)
|
||||||
|
|
||||||
|
err := p.Commit()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to save changes: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p = NewPreferences(cfgFile)
|
||||||
|
resp, err := p.GetAdminURL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read admin url: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != exampleURL {
|
||||||
|
t.Errorf("unexpected admin url: %s", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = p.GetManagementURL()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read management url: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != exampleURL {
|
||||||
|
t.Errorf("unexpected management url: %s", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = p.GetPreSharedKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read preshared key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != examplePresharedKey {
|
||||||
|
t.Errorf("unexpected preshared key: %s", resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,12 @@ import (
|
|||||||
// DeviceNameCtxKey context key for device name
|
// DeviceNameCtxKey context key for device name
|
||||||
const DeviceNameCtxKey = "deviceName"
|
const DeviceNameCtxKey = "deviceName"
|
||||||
|
|
||||||
|
// OsVersionCtxKey context key for operating system version
|
||||||
|
const OsVersionCtxKey = "OsVersion"
|
||||||
|
|
||||||
|
// OsNameCtxKey context key for operating system name
|
||||||
|
const OsNameCtxKey = "OsName"
|
||||||
|
|
||||||
// Info is an object that contains machine information
|
// Info is an object that contains machine information
|
||||||
// Most of the code is taken from https://github.com/matishsiao/goInfo
|
// Most of the code is taken from https://github.com/matishsiao/goInfo
|
||||||
type Info struct {
|
type Info struct {
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//go:build !ios
|
||||||
|
// +build !ios
|
||||||
|
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
45
client/system/info_ios.go
Normal file
45
client/system/info_ios.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//go:build ios
|
||||||
|
// +build ios
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetInfo retrieves and parses the system information
|
||||||
|
func GetInfo(ctx context.Context) *Info {
|
||||||
|
|
||||||
|
// Convert fixed-size byte arrays to Go strings
|
||||||
|
sysName := extractOsName(ctx, "sysName")
|
||||||
|
swVersion := extractOsVersion(ctx, "swVersion")
|
||||||
|
|
||||||
|
gio := &Info{Kernel: sysName, OSVersion: swVersion, Core: swVersion, Platform: "unknown", OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||||
|
// systemHostname, _ := os.Hostname()
|
||||||
|
gio.Hostname = extractDeviceName(ctx, "hostname")
|
||||||
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
|
gio.UIVersion = extractUserAgent(ctx)
|
||||||
|
|
||||||
|
return gio
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractOsVersion extracts operating system version from context or returns the default
|
||||||
|
func extractOsVersion(ctx context.Context, defaultName string) string {
|
||||||
|
v, ok := ctx.Value(OsVersionCtxKey).(string)
|
||||||
|
if !ok {
|
||||||
|
return defaultName
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractOsName extracts operating system name from context or returns the default
|
||||||
|
func extractOsName(ctx context.Context, defaultName string) string {
|
||||||
|
v, ok := ctx.Value(OsNameCtxKey).(string)
|
||||||
|
if !ok {
|
||||||
|
return defaultName
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
@@ -28,14 +28,20 @@ func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter
|
|||||||
return wgIFace, nil
|
return wgIFace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOnMobile creates a new Wireguard interface, sets a given IP and brings it up.
|
// CreateOnAndroid creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
// Will reuse an existing one.
|
// Will reuse an existing one.
|
||||||
func (w *WGIface) CreateOnMobile(mIFaceArgs MobileIFaceArguments) error {
|
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
return w.tun.Create(mIFaceArgs)
|
return w.tun.Create(mIFaceArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateOniOS creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
|
// Will reuse an existing one.
|
||||||
|
func (w *WGIface) CreateOniOS(tunFd int32) error {
|
||||||
|
return fmt.Errorf("this function has not implemented on mobile")
|
||||||
|
}
|
||||||
|
|
||||||
// Create this function make sense on mobile only
|
// Create this function make sense on mobile only
|
||||||
func (w *WGIface) Create() error {
|
func (w *WGIface) Create() error {
|
||||||
return fmt.Errorf("this function has not implemented on mobile")
|
return fmt.Errorf("this function has not implemented on mobile")
|
||||||
|
|||||||
51
iface/iface_ios.go
Normal file
51
iface/iface_ios.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
//go:build ios
|
||||||
|
// +build ios
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pion/transport/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewWGIFace Creates a new WireGuard interface instance
|
||||||
|
func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
||||||
|
wgIFace := &WGIface{
|
||||||
|
mu: sync.Mutex{},
|
||||||
|
}
|
||||||
|
|
||||||
|
wgAddress, err := parseWGAddress(address)
|
||||||
|
if err != nil {
|
||||||
|
return wgIFace, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tun := newTunDevice(wgAddress, mtu, tunAdapter, transportNet)
|
||||||
|
wgIFace.tun = tun
|
||||||
|
|
||||||
|
wgIFace.configurer = newWGConfigurer(tun)
|
||||||
|
|
||||||
|
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
||||||
|
|
||||||
|
return wgIFace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOniOS creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
|
// Will reuse an existing one.
|
||||||
|
func (w *WGIface) CreateOniOS(tunFd int32) error {
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
return w.tun.Create(tunFd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOnAndroid creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
|
// Will reuse an existing one.
|
||||||
|
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
||||||
|
return fmt.Errorf("this function has not implemented on mobile")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create this function make sense on mobile only
|
||||||
|
func (w *WGIface) Create() error {
|
||||||
|
return fmt.Errorf("this function has not implemented on mobile")
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//go:build !android
|
//go:build !android && !ios
|
||||||
|
// +build !android,!ios
|
||||||
|
|
||||||
package iface
|
package iface
|
||||||
|
|
||||||
@@ -27,8 +28,13 @@ func NewWGIFace(iFaceName string, address string, mtu int, tunAdapter TunAdapter
|
|||||||
return wgIFace, nil
|
return wgIFace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOnMobile this function make sense on mobile only
|
// CreateOnAndroid this function make sense on mobile only
|
||||||
func (w *WGIface) CreateOnMobile(mIFaceArgs MobileIFaceArguments) error {
|
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
||||||
|
return fmt.Errorf("this function has not implemented on non mobile")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOniOS this function make sense on mobile only
|
||||||
|
func (w *WGIface) CreateOniOS(tunFd int32) error {
|
||||||
return fmt.Errorf("this function has not implemented on non mobile")
|
return fmt.Errorf("this function has not implemented on non mobile")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//go:build android
|
||||||
|
// +build android
|
||||||
|
|
||||||
package iface
|
package iface
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
63
iface/ipc_parser_ios.go
Normal file
63
iface/ipc_parser_ios.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//go:build ios
|
||||||
|
// +build ios
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func toWgUserspaceString(wgCfg wgtypes.Config) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if wgCfg.PrivateKey != nil {
|
||||||
|
hexKey := hex.EncodeToString(wgCfg.PrivateKey[:])
|
||||||
|
sb.WriteString(fmt.Sprintf("private_key=%s\n", hexKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
if wgCfg.ListenPort != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("listen_port=%d\n", *wgCfg.ListenPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
if wgCfg.ReplacePeers {
|
||||||
|
sb.WriteString("replace_peers=true\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if wgCfg.FirewallMark != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("fwmark=%d\n", *wgCfg.FirewallMark))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range wgCfg.Peers {
|
||||||
|
hexKey := hex.EncodeToString(p.PublicKey[:])
|
||||||
|
sb.WriteString(fmt.Sprintf("public_key=%s\n", hexKey))
|
||||||
|
|
||||||
|
if p.PresharedKey != nil {
|
||||||
|
preSharedHexKey := hex.EncodeToString(p.PresharedKey[:])
|
||||||
|
sb.WriteString(fmt.Sprintf("preshared_key=%s\n", preSharedHexKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Remove {
|
||||||
|
sb.WriteString("remove=true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.ReplaceAllowedIPs {
|
||||||
|
sb.WriteString("replace_allowed_ips=true\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, aip := range p.AllowedIPs {
|
||||||
|
sb.WriteString(fmt.Sprintf("allowed_ip=%s\n", aip.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Endpoint != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("endpoint=%s\n", p.Endpoint.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.PersistentKeepaliveInterval != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", int(p.PersistentKeepaliveInterval.Seconds())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//go:build android
|
||||||
|
// +build android
|
||||||
|
|
||||||
package iface
|
package iface
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -56,7 +59,7 @@ func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
|||||||
t.device = device.NewDevice(t.wrapper, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
t.device = device.NewDevice(t.wrapper, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
||||||
// without this property mobile devices can discover remote endpoints if the configured one was wrong.
|
// without this property mobile devices can discover remote endpoints if the configured one was wrong.
|
||||||
// this helps with support for the older NetBird clients that had a hardcoded direct mode
|
// this helps with support for the older NetBird clients that had a hardcoded direct mode
|
||||||
//t.device.DisableSomeRoamingForBrokenMobileSemantics()
|
// t.device.DisableSomeRoamingForBrokenMobileSemantics()
|
||||||
|
|
||||||
err = t.device.Up()
|
err = t.device.Up()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//go:build !ios
|
||||||
|
// +build !ios
|
||||||
|
|
||||||
package iface
|
package iface
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
105
iface/tun_ios.go
Normal file
105
iface/tun_ios.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
//go:build ios
|
||||||
|
// +build ios
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pion/transport/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"golang.zx2c4.com/wireguard/device"
|
||||||
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tunDevice struct {
|
||||||
|
address WGAddress
|
||||||
|
mtu int
|
||||||
|
tunAdapter TunAdapter
|
||||||
|
iceBind *bind.ICEBind
|
||||||
|
|
||||||
|
fd int
|
||||||
|
name string
|
||||||
|
device *device.Device
|
||||||
|
wrapper *DeviceWrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTunDevice(address WGAddress, mtu int, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
|
||||||
|
return &tunDevice{
|
||||||
|
address: address,
|
||||||
|
mtu: mtu,
|
||||||
|
tunAdapter: tunAdapter,
|
||||||
|
iceBind: bind.NewICEBind(transportNet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Create(tunFd int32) error {
|
||||||
|
log.Infof("create tun interface")
|
||||||
|
|
||||||
|
dupTunFd, err := unix.Dup(int(tunFd))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to dup tun fd: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = unix.SetNonblock(dupTunFd, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to set tun fd as non blocking: %v", err)
|
||||||
|
unix.Close(dupTunFd)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tun, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Unable to create new tun device from fd: %v", err)
|
||||||
|
unix.Close(dupTunFd)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.wrapper = newDeviceWrapper(tun)
|
||||||
|
log.Debug("Attaching to interface")
|
||||||
|
t.device = device.NewDevice(t.wrapper, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
||||||
|
// without this property mobile devices can discover remote endpoints if the configured one was wrong.
|
||||||
|
// this helps with support for the older NetBird clients that had a hardcoded direct mode
|
||||||
|
// t.device.DisableSomeRoamingForBrokenMobileSemantics()
|
||||||
|
|
||||||
|
err = t.device.Up()
|
||||||
|
if err != nil {
|
||||||
|
t.device.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("device is ready to use: %s", t.name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Device() *device.Device {
|
||||||
|
return t.device
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) DeviceName() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) WgAddress() WGAddress {
|
||||||
|
return t.address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) UpdateAddr(addr WGAddress) error {
|
||||||
|
// todo implement
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Close() (err error) {
|
||||||
|
if t.device != nil {
|
||||||
|
t.device.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) routesToString(routes []string) string {
|
||||||
|
return strings.Join(routes, ";")
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build (linux || darwin) && !android
|
//go:build (linux || darwin) && !android && !ios
|
||||||
|
|
||||||
package iface
|
package iface
|
||||||
|
|
||||||
|
|||||||
165
iface/wg_configurer_ios.go
Normal file
165
iface/wg_configurer_ios.go
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
//go:build ios
|
||||||
|
// +build ios
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errFuncNotImplemented = errors.New("function not implemented")
|
||||||
|
)
|
||||||
|
|
||||||
|
type wGConfigurer struct {
|
||||||
|
tunDevice *tunDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWGConfigurer(tunDevice *tunDevice) wGConfigurer {
|
||||||
|
return wGConfigurer{
|
||||||
|
tunDevice: tunDevice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
|
||||||
|
log.Debugf("adding Wireguard private key")
|
||||||
|
key, err := wgtypes.ParseKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fwmark := 0
|
||||||
|
config := wgtypes.Config{
|
||||||
|
PrivateKey: &key,
|
||||||
|
ReplacePeers: true,
|
||||||
|
FirewallMark: &fwmark,
|
||||||
|
ListenPort: &port,
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
||||||
|
// parse allowed ips
|
||||||
|
_, ipNet, err := net.ParseCIDR(allowedIps)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
peer := wgtypes.PeerConfig{
|
||||||
|
PublicKey: peerKeyParsed,
|
||||||
|
ReplaceAllowedIPs: true,
|
||||||
|
AllowedIPs: []net.IPNet{*ipNet},
|
||||||
|
PersistentKeepaliveInterval: &keepAlive,
|
||||||
|
PresharedKey: preSharedKey,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
config := wgtypes.Config{
|
||||||
|
Peers: []wgtypes.PeerConfig{peer},
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wGConfigurer) removePeer(peerKey string) error {
|
||||||
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peer := wgtypes.PeerConfig{
|
||||||
|
PublicKey: peerKeyParsed,
|
||||||
|
Remove: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
config := wgtypes.Config{
|
||||||
|
Peers: []wgtypes.PeerConfig{peer},
|
||||||
|
}
|
||||||
|
return c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wGConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
||||||
|
_, ipNet, err := net.ParseCIDR(allowedIP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
peer := wgtypes.PeerConfig{
|
||||||
|
PublicKey: peerKeyParsed,
|
||||||
|
UpdateOnly: true,
|
||||||
|
ReplaceAllowedIPs: false,
|
||||||
|
AllowedIPs: []net.IPNet{*ipNet},
|
||||||
|
}
|
||||||
|
|
||||||
|
config := wgtypes.Config{
|
||||||
|
Peers: []wgtypes.PeerConfig{peer},
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wGConfigurer) removeAllowedIP(peerKey string, ip string) error {
|
||||||
|
ipc, err := c.tunDevice.Device().IpcGet()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||||
|
hexKey := hex.EncodeToString(peerKeyParsed[:])
|
||||||
|
|
||||||
|
lines := strings.Split(ipc, "\n")
|
||||||
|
|
||||||
|
output := ""
|
||||||
|
foundPeer := false
|
||||||
|
removedAllowedIP := false
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
// If we're within the details of the found peer and encounter another public key,
|
||||||
|
// this means we're starting another peer's details. So, reset the flag.
|
||||||
|
if strings.HasPrefix(line, "public_key=") && foundPeer {
|
||||||
|
foundPeer = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify the peer with the specific public key
|
||||||
|
if line == fmt.Sprintf("public_key=%s", hexKey) {
|
||||||
|
foundPeer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're within the details of the found peer and find the specific allowed IP, skip this line
|
||||||
|
if foundPeer && line == "allowed_ip="+ip {
|
||||||
|
removedAllowedIP = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the line to the output string
|
||||||
|
if strings.HasPrefix(line, "private_key=") || strings.HasPrefix(line, "listen_port=") ||
|
||||||
|
strings.HasPrefix(line, "public_key=") || strings.HasPrefix(line, "preshared_key=") ||
|
||||||
|
strings.HasPrefix(line, "endpoint=") || strings.HasPrefix(line, "persistent_keepalive_interval=") ||
|
||||||
|
strings.HasPrefix(line, "allowed_ip=") {
|
||||||
|
output += line + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !removedAllowedIP {
|
||||||
|
return fmt.Errorf("allowedIP not found")
|
||||||
|
} else {
|
||||||
|
return c.tunDevice.Device().IpcSet(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !android
|
//go:build !android && !ios
|
||||||
|
|
||||||
package iface
|
package iface
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
||||||
//parse allowed ips
|
// parse allowed ips
|
||||||
_, ipNet, err := net.ParseCIDR(allowedIps)
|
_, ipNet, err := net.ParseCIDR(allowedIps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user