2022-11-23 13:39:42 +01:00
package dns
import (
"bytes"
"fmt"
"os"
2023-02-13 15:25:11 +01:00
log "github.com/sirupsen/logrus"
2022-11-23 13:39:42 +01:00
)
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"
)
2023-02-13 15:25:11 +01:00
2022-11-23 13:39:42 +01:00
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 ) 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 )
}
}
2022-11-29 14:51:18 +01:00
return fmt . Errorf ( "unable to configure DNS for this peer using file manager without a nameserver group with all domains configured" )
2022-11-23 13:39:42 +01:00
}
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 {
2023-02-13 15:25:11 +01:00
if dConf . matchOnly || dConf . disabled {
2022-11-23 13:39:42 +01:00
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
}
2022-11-29 14:51:18 +01:00
log . Infof ( "created a NetBird managed %s file with your DNS settings. Added %d search domains. Search list: %s" , defaultResolvConfPath , appendedDomains , searchDomains )
2022-11-23 13:39:42 +01:00
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
}