Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

example(outline-cli): configure system DNS when resolv.conf does not exist #203

Merged
merged 12 commits into from
May 30, 2024
3 changes: 2 additions & 1 deletion x/examples/outline-cli/app_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ func (app App) Run() error {
logging.Info.Printf("OutlineDevice -> tun stopped: %v %v\n", written, err)
}()

if err := setSystemDNSServer(app.RoutingConfig.DNSServerIP); err != nil {
err = setSystemDNSServer(app.RoutingConfig.DNSServerIP)
if err != nil {
return fmt.Errorf("failed to configure system DNS: %w", err)
}
defer restoreSystemDNSServer()
Expand Down
73 changes: 55 additions & 18 deletions x/examples/outline-cli/dns_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package main

import (
"errors"
"fmt"
"os"
)
Expand All @@ -27,40 +28,76 @@ const (
resolvConfHeadBackupFile = "/etc/resolv.head.outlinecli.backup"
)

type systemDNSBackup struct {
original, backup string
}

var systemDNSBackups = make([]systemDNSBackup, 0, 2)

func setSystemDNSServer(serverHost string) error {
setting := []byte(`# Outline CLI DNS Setting
# The original file has been renamed as resolv[.head].outlinecli.backup
nameserver ` + serverHost + "\n")

if err := backupAndWriteFile(resolvConfFile, resolvConfBackupFile, setting); err != nil {
err := backupAndWriteFile(resolvConfFile, resolvConfBackupFile, setting)
if err != nil {
return err
}
return backupAndWriteFile(resolvConfHeadFile, resolvConfHeadBackupFile, setting)
}

func restoreSystemDNSServer() {
restoreFileIfExists(resolvConfBackupFile, resolvConfFile)
restoreFileIfExists(resolvConfHeadBackupFile, resolvConfHeadFile)
err = backupAndWriteFile(resolvConfHeadFile, resolvConfHeadBackupFile, setting)
if err != nil {
return err
}

return nil
}

func backupAndWriteFile(original, backup string, data []byte) error {
if err := os.Rename(original, backup); err != nil {
return fmt.Errorf("failed to backup DNS config file '%s' to '%s': %w", original, backup, err)
if _, err := os.Stat(original); err == nil {
// original file exist - move it into backup
if err := os.Rename(original, backup); err != nil {
return fmt.Errorf("failed to backup DNS config file '%s' to '%s': %w", original, backup, err)
}
} else if !errors.Is(err, os.ErrNotExist) { // if not exist - it's ok, just write to it
return fmt.Errorf("failed to check the existence of DNS config file '%s': %w", original, err)
}
if err := os.WriteFile(original, data, 0644); err != nil {

jyyi1 marked this conversation as resolved.
Show resolved Hide resolved
systemDNSBackups = append(systemDNSBackups, systemDNSBackup{
original: original,
backup: backup,
})

Comment on lines +64 to +69
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to put these lines before os.WriteFile. Because we have already backed up the original DNS config, even though we failed to write to the new file, we still need to copy the backup file back to original when we try to reset the DNS config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

// save data to original
if err := os.WriteFile(original, data, 0o644); err != nil {
return fmt.Errorf("failed to write DNS config file '%s': %w", original, err)
}

return nil
}

func restoreFileIfExists(backup, original string) {
if _, err := os.Stat(backup); err != nil {
logging.Warn.Printf("no DNS config backup file '%s' presents: %v\n", backup, err)
return
}
if err := os.Rename(backup, original); err != nil {
logging.Err.Printf("failed to restore DNS config from backup '%s' to '%s': %v\n", backup, original, err)
return
func restoreSystemDNSServer() {
for _, backup := range systemDNSBackups {
if _, err := os.Stat(backup.backup); err == nil {
// backup exist - replace original with it
if err := os.Rename(backup.backup, backup.original); err != nil {
logging.Err.Printf(
"failed to restore DNS config from backup '%s' to '%s': %v\n",
backup.backup,
backup.original,
err,
)
continue
}
logging.Info.Printf("DNS config restored from '%s' to '%s'\n", backup.backup, backup.original)
} else if errors.Is(err, os.ErrNotExist) {
// backup not exist - just remove original, because it's created by ourselves
if err := os.Remove(backup.original); err != nil {
logging.Err.Printf("failed to remove Outline DNS config file '%s': %v\n", backup.original, err)
continue
}
logging.Info.Printf("Outline DNS config '%s' has been removed\n", backup.original)
} else {
logging.Err.Printf("failed to check the existence of DNS config backup file '%s': %v\n", backup.backup, err)
}
}
logging.Info.Printf("DNS config restored from '%s' to '%s'\n", backup, original)
}
2 changes: 1 addition & 1 deletion x/examples/outline-cli/ipv6_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func enableIPv6(enabled bool) (bool, error) {
} else {
disabledStr[0] = '1'
}
if err := os.WriteFile(disableIPv6ProcFile, disabledStr, 0644); err != nil {
if err := os.WriteFile(disableIPv6ProcFile, disabledStr, 0o644); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍! Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can keep this change. Though it's not related to DNS, but it's CLI.

return prevEnabled, fmt.Errorf("failed to write IPv6 config: %w", err)
}

Expand Down
1 change: 0 additions & 1 deletion x/examples/outline-cli/outline_packet_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ func (proxy *outlinePacketProxy) testConnectivityAndRefresh(resolverAddr, domain
dialer := transport.PacketListenerDialer{Listener: proxy.remotePl}
dnsResolver := dns.NewUDPResolver(dialer, resolverAddr)
result, err := connectivity.TestConnectivityWithResolver(context.Background(), dnsResolver, domain)

if err != nil {
logging.Info.Printf("connectivity test failed. Refresh skipped. Error: %v\n", err)
return err
Expand Down
Loading