mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-29 19:43:57 +01:00
481465e1ae
Support DNS feature on mobile systems --------- Co-authored-by: Givi Khojanashvili <gigovich@gmail.com>
163 lines
4.9 KiB
Go
163 lines
4.9 KiB
Go
//go:build !android
|
|
|
|
package dns
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
fileGeneratedResolvConfContentHeader = "# Generated by NetBird"
|
|
fileGeneratedResolvConfSearchBeginContent = "search "
|
|
fileGeneratedResolvConfContentFormat = fileGeneratedResolvConfContentHeader +
|
|
"\n# If needed you can restore the original file by copying back %s\n\nnameserver %s\n" +
|
|
fileGeneratedResolvConfSearchBeginContent + "%s\n"
|
|
)
|
|
|
|
const (
|
|
fileDefaultResolvConfBackupLocation = defaultResolvConfPath + ".original.netbird"
|
|
fileMaxLineCharsLimit = 256
|
|
fileMaxNumberOfSearchDomains = 6
|
|
)
|
|
|
|
var fileSearchLineBeginCharCount = len(fileGeneratedResolvConfSearchBeginContent)
|
|
|
|
type fileConfigurator struct {
|
|
originalPerms os.FileMode
|
|
}
|
|
|
|
func newFileConfigurator() (hostManager, error) {
|
|
return &fileConfigurator{}, nil
|
|
}
|
|
|
|
func (f *fileConfigurator) supportCustomPort() bool {
|
|
return false
|
|
}
|
|
|
|
func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
|
backupFileExist := false
|
|
_, err := os.Stat(fileDefaultResolvConfBackupLocation)
|
|
if err == nil {
|
|
backupFileExist = true
|
|
}
|
|
|
|
if !config.routeAll {
|
|
if backupFileExist {
|
|
err = f.restore()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to configure DNS for this peer using file manager without a Primary nameserver group. Restoring the original file return err: %s", err)
|
|
}
|
|
}
|
|
return fmt.Errorf("unable to configure DNS for this peer using file manager without a nameserver group with all domains configured")
|
|
}
|
|
managerType, err := getOSDNSManagerType()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch managerType {
|
|
case fileManager, netbirdManager:
|
|
if !backupFileExist {
|
|
err = f.backup()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to backup the resolv.conf file")
|
|
}
|
|
}
|
|
default:
|
|
// todo improve this and maybe restart DNS manager from scratch
|
|
return fmt.Errorf("something happened and file manager is not your prefered host dns configurator, restart the agent")
|
|
}
|
|
|
|
var searchDomains string
|
|
appendedDomains := 0
|
|
for _, dConf := range config.domains {
|
|
if dConf.matchOnly || dConf.disabled {
|
|
continue
|
|
}
|
|
if appendedDomains >= fileMaxNumberOfSearchDomains {
|
|
// lets log all skipped domains
|
|
log.Infof("already appended %d domains to search list. Skipping append of %s domain", fileMaxNumberOfSearchDomains, dConf.domain)
|
|
continue
|
|
}
|
|
if fileSearchLineBeginCharCount+len(searchDomains) > fileMaxLineCharsLimit {
|
|
// lets log all skipped domains
|
|
log.Infof("search list line is larger than %d characters. Skipping append of %s domain", fileMaxLineCharsLimit, dConf.domain)
|
|
continue
|
|
}
|
|
|
|
searchDomains += " " + dConf.domain
|
|
appendedDomains++
|
|
}
|
|
content := fmt.Sprintf(fileGeneratedResolvConfContentFormat, fileDefaultResolvConfBackupLocation, config.serverIP, searchDomains)
|
|
err = writeDNSConfig(content, defaultResolvConfPath, f.originalPerms)
|
|
if err != nil {
|
|
err = f.restore()
|
|
if err != nil {
|
|
log.Errorf("attempt to restore default file failed with error: %s", err)
|
|
}
|
|
return err
|
|
}
|
|
log.Infof("created a NetBird managed %s file with your DNS settings. Added %d search domains. Search list: %s", defaultResolvConfPath, appendedDomains, searchDomains)
|
|
return nil
|
|
}
|
|
|
|
func (f *fileConfigurator) restoreHostDNS() error {
|
|
return f.restore()
|
|
}
|
|
|
|
func (f *fileConfigurator) backup() error {
|
|
stats, err := os.Stat(defaultResolvConfPath)
|
|
if err != nil {
|
|
return fmt.Errorf("got an error while checking stats for %s file. Error: %s", defaultResolvConfPath, err)
|
|
}
|
|
|
|
f.originalPerms = stats.Mode()
|
|
|
|
err = copyFile(defaultResolvConfPath, fileDefaultResolvConfBackupLocation)
|
|
if err != nil {
|
|
return fmt.Errorf("got error while backing up the %s file. Error: %s", defaultResolvConfPath, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *fileConfigurator) restore() error {
|
|
err := copyFile(fileDefaultResolvConfBackupLocation, defaultResolvConfPath)
|
|
if err != nil {
|
|
return fmt.Errorf("got error while restoring the %s file from %s. Error: %s", defaultResolvConfPath, fileDefaultResolvConfBackupLocation, err)
|
|
}
|
|
|
|
return os.RemoveAll(fileDefaultResolvConfBackupLocation)
|
|
}
|
|
|
|
func writeDNSConfig(content, fileName string, permissions os.FileMode) error {
|
|
log.Debugf("creating managed file %s", fileName)
|
|
var buf bytes.Buffer
|
|
buf.WriteString(content)
|
|
err := os.WriteFile(fileName, buf.Bytes(), permissions)
|
|
if err != nil {
|
|
return fmt.Errorf("got an creating resolver file %s. Error: %s", fileName, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func copyFile(src, dest string) error {
|
|
stats, err := os.Stat(src)
|
|
if err != nil {
|
|
return fmt.Errorf("got an error while checking stats for %s file when copying it. Error: %s", src, err)
|
|
}
|
|
|
|
bytesRead, err := os.ReadFile(src)
|
|
if err != nil {
|
|
return fmt.Errorf("got an error while reading the file %s file for copy. Error: %s", src, err)
|
|
}
|
|
|
|
err = os.WriteFile(dest, bytesRead, stats.Mode())
|
|
if err != nil {
|
|
return fmt.Errorf("got an writing the destination file %s for copy. Error: %s", dest, err)
|
|
}
|
|
return nil
|
|
}
|