mirror of
https://github.com/netbirdio/netbird.git
synced 2024-12-01 04:23:44 +01:00
818c6b885f
* starting engine by passing file descriptor on engine start * inject logger that does not compile * logger and first client * first working connection * support for routes and working connection * small refactor for better code quality in swift * trying to add DNS * fix * updated * fix route deletion * trying to bind the DNS resolver dialer to an interface * use dns.Client.Exchange * fix metadata send on startup * switching between client to query upstream * fix panic on no dns response * fix after merge changes * add engine ready listener * replace engine listener with connection listener * disable relay connection for iOS until proxy is refactored into bind * Extract private upstream for iOS and fix function headers for other OS * Update mock Server * Fix dns server and upstream tests * Fix engine null pointer with mobile dependencies for other OS * Revert back to disabling upstream on no response * Fix some of the remarks from the linter * Fix linter * re-arrange duration calculation * revert exported HostDNSConfig * remove unused engine listener * remove development logs * refactor dns code and interface name propagation * clean dns server test * disable upstream deactivation for iOS * remove files after merge * fix dns server darwin * fix server mock * fix build flags * move service listen back to initialize * add wgInterface to hostManager initialization on android * fix typo and remove unused function * extract upstream exchange for ios and rest * remove todo * separate upstream logic to ios file * Fix upstream test * use interface and embedded struct for upstream * set properly upstream client * remove placeholder * remove ios specific attributes * fix upstream test * merge ipc parser and wg configurer for mobile * fix build annotation * use json for DNS settings handover through gomobile * add logs for DNS json string * bring back check on ios for private upstream * remove wrong (and unused) line * fix wrongly updated comments on DNSSetting export --------- Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
269 lines
7.2 KiB
Go
269 lines
7.2 KiB
Go
//go:build !android
|
|
|
|
package dns
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
fileGeneratedResolvConfContentHeader = "# Generated by NetBird"
|
|
fileGeneratedResolvConfContentHeaderNextLine = fileGeneratedResolvConfContentHeader + `
|
|
# If needed you can restore the original file by copying back ` + fileDefaultResolvConfBackupLocation + "\n\n"
|
|
|
|
fileDefaultResolvConfBackupLocation = defaultResolvConfPath + ".original.netbird"
|
|
|
|
fileMaxLineCharsLimit = 256
|
|
fileMaxNumberOfSearchDomains = 6
|
|
)
|
|
|
|
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")
|
|
}
|
|
|
|
if !backupFileExist {
|
|
err = f.backup()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to backup the resolv.conf file")
|
|
}
|
|
}
|
|
|
|
searchDomainList := searchDomains(config)
|
|
|
|
originalSearchDomains, nameServers, others, err := originalDNSConfigs(fileDefaultResolvConfBackupLocation)
|
|
if err != nil {
|
|
log.Error(err)
|
|
}
|
|
|
|
searchDomainList = mergeSearchDomains(searchDomainList, originalSearchDomains)
|
|
|
|
buf := prepareResolvConfContent(
|
|
searchDomainList,
|
|
append([]string{config.ServerIP}, nameServers...),
|
|
others)
|
|
|
|
log.Debugf("creating managed file %s", defaultResolvConfPath)
|
|
err = os.WriteFile(defaultResolvConfPath, buf.Bytes(), f.originalPerms)
|
|
if err != nil {
|
|
restoreErr := f.restore()
|
|
if restoreErr != nil {
|
|
log.Errorf("attempt to restore default file failed with error: %s", err)
|
|
}
|
|
return fmt.Errorf("got an creating resolver file %s. Error: %s", defaultResolvConfPath, err)
|
|
}
|
|
|
|
log.Infof("created a NetBird managed %s file with your DNS settings. Added %d search domains. Search list: %s", defaultResolvConfPath, len(searchDomainList), searchDomainList)
|
|
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 prepareResolvConfContent(searchDomains, nameServers, others []string) bytes.Buffer {
|
|
var buf bytes.Buffer
|
|
buf.WriteString(fileGeneratedResolvConfContentHeaderNextLine)
|
|
|
|
for _, cfgLine := range others {
|
|
buf.WriteString(cfgLine)
|
|
buf.WriteString("\n")
|
|
}
|
|
|
|
if len(searchDomains) > 0 {
|
|
buf.WriteString("search ")
|
|
buf.WriteString(strings.Join(searchDomains, " "))
|
|
buf.WriteString("\n")
|
|
}
|
|
|
|
for _, ns := range nameServers {
|
|
buf.WriteString("nameserver ")
|
|
buf.WriteString(ns)
|
|
buf.WriteString("\n")
|
|
}
|
|
return buf
|
|
}
|
|
|
|
func searchDomains(config HostDNSConfig) []string {
|
|
listOfDomains := make([]string, 0)
|
|
for _, dConf := range config.Domains {
|
|
if dConf.MatchOnly || dConf.Disabled {
|
|
continue
|
|
}
|
|
|
|
listOfDomains = append(listOfDomains, dConf.Domain)
|
|
}
|
|
return listOfDomains
|
|
}
|
|
|
|
func originalDNSConfigs(resolvconfFile string) (searchDomains, nameServers, others []string, err error) {
|
|
file, err := os.Open(resolvconfFile)
|
|
if err != nil {
|
|
err = fmt.Errorf(`could not read existing resolv.conf`)
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
reader := bufio.NewReader(file)
|
|
|
|
for {
|
|
lineBytes, isPrefix, readErr := reader.ReadLine()
|
|
if readErr != nil {
|
|
break
|
|
}
|
|
|
|
if isPrefix {
|
|
err = fmt.Errorf(`resolv.conf line too long`)
|
|
return
|
|
}
|
|
|
|
line := strings.TrimSpace(string(lineBytes))
|
|
|
|
if strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
|
|
if strings.HasPrefix(line, "domain") {
|
|
continue
|
|
}
|
|
|
|
if strings.HasPrefix(line, "options") && strings.Contains(line, "rotate") {
|
|
line = strings.ReplaceAll(line, "rotate", "")
|
|
splitLines := strings.Fields(line)
|
|
if len(splitLines) == 1 {
|
|
continue
|
|
}
|
|
line = strings.Join(splitLines, " ")
|
|
}
|
|
|
|
if strings.HasPrefix(line, "search") {
|
|
splitLines := strings.Fields(line)
|
|
if len(splitLines) < 2 {
|
|
continue
|
|
}
|
|
|
|
searchDomains = splitLines[1:]
|
|
continue
|
|
}
|
|
|
|
if strings.HasPrefix(line, "nameserver") {
|
|
splitLines := strings.Fields(line)
|
|
if len(splitLines) != 2 {
|
|
continue
|
|
}
|
|
nameServers = append(nameServers, splitLines[1])
|
|
continue
|
|
}
|
|
|
|
others = append(others, line)
|
|
}
|
|
return
|
|
}
|
|
|
|
// merge search Domains lists and cut off the list if it is too long
|
|
func mergeSearchDomains(searchDomains []string, originalSearchDomains []string) []string {
|
|
lineSize := len("search")
|
|
searchDomainsList := make([]string, 0, len(searchDomains)+len(originalSearchDomains))
|
|
|
|
lineSize = validateAndFillSearchDomains(lineSize, &searchDomainsList, searchDomains)
|
|
_ = validateAndFillSearchDomains(lineSize, &searchDomainsList, originalSearchDomains)
|
|
|
|
return searchDomainsList
|
|
}
|
|
|
|
// validateAndFillSearchDomains checks if the search Domains list is not too long and if the line is not too long
|
|
// extend s slice with vs elements
|
|
// return with the number of characters in the searchDomains line
|
|
func validateAndFillSearchDomains(initialLineChars int, s *[]string, vs []string) int {
|
|
for _, sd := range vs {
|
|
tmpCharsNumber := initialLineChars + 1 + len(sd)
|
|
if tmpCharsNumber > fileMaxLineCharsLimit {
|
|
// lets log all skipped Domains
|
|
log.Infof("search list line is larger than %d characters. Skipping append of %s domain", fileMaxLineCharsLimit, sd)
|
|
continue
|
|
}
|
|
|
|
initialLineChars = tmpCharsNumber
|
|
|
|
if len(*s) >= fileMaxNumberOfSearchDomains {
|
|
// lets log all skipped Domains
|
|
log.Infof("already appended %d domains to search list. Skipping append of %s domain", fileMaxNumberOfSearchDomains, sd)
|
|
continue
|
|
}
|
|
*s = append(*s, sd)
|
|
}
|
|
return initialLineChars
|
|
}
|
|
|
|
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
|
|
}
|