Skip to content

Commit

Permalink
Merge pull request #450 from fatanugraha/main
Browse files Browse the repository at this point in the history
Sync DNS Resolver Nameserver List
  • Loading branch information
openshift-merge-bot[bot] authored Jan 14, 2025
2 parents ed3693f + 1da534e commit 479f0c3
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 55 deletions.
28 changes: 13 additions & 15 deletions pkg/services/dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@ import (
)

type dnsHandler struct {
zones []types.Zone
zonesLock sync.RWMutex
udpClient *dns.Client
tcpClient *dns.Client
hostsFile *HostsFile
nameservers []string
zones []types.Zone
zonesLock sync.RWMutex
udpClient *dns.Client
tcpClient *dns.Client
hostsFile *HostsFile
dnsConfig *dnsConfig
}

func newDNSHandler(zones []types.Zone) (*dnsHandler, error) {

nameservers, err := getDNSHostAndPort()
dnsConfig, err := newDNSConfig()
if err != nil {
return nil, err
}
Expand All @@ -37,13 +36,12 @@ func newDNSHandler(zones []types.Zone) (*dnsHandler, error) {
}

return &dnsHandler{
zones: zones,
tcpClient: &dns.Client{Net: "tcp"},
udpClient: &dns.Client{Net: "udp"},
nameservers: nameservers,
hostsFile: hostsFile,
zones: zones,
tcpClient: &dns.Client{Net: "tcp"},
udpClient: &dns.Client{Net: "udp"},
dnsConfig: dnsConfig,
hostsFile: hostsFile,
}, nil

}

func (h *dnsHandler) handle(w dns.ResponseWriter, dnsClient *dns.Client, r *dns.Msg, responseMessageSize int) {
Expand Down Expand Up @@ -145,7 +143,7 @@ func (h *dnsHandler) addAnswers(dnsClient *dns.Client, r *dns.Msg) *dns.Msg {
return m
}
}
for _, nameserver := range h.nameservers {
for _, nameserver := range h.dnsConfig.Nameservers() {
msg := r.Copy()
r, _, err := dnsClient.Exchange(msg, nameserver)
// return first good answer
Expand Down
24 changes: 24 additions & 0 deletions pkg/services/dns/dns_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dns

import "sync"

type dnsConfig struct {
mu sync.RWMutex
nameservers []string
}

func newDNSConfig() (*dnsConfig, error) {
r := &dnsConfig{nameservers: []string{}}
if err := r.init(); err != nil {
return nil, err
}

return r, nil
}

func (r *dnsConfig) Nameservers() []string {
r.mu.RLock()
defer r.mu.RUnlock()

return r.nameservers
}
41 changes: 38 additions & 3 deletions pkg/services/dns/dns_config_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,51 @@ import (
"net"
"net/netip"

"github.com/containers/gvisor-tap-vsock/pkg/utils"
"github.com/miekg/dns"
log "github.com/sirupsen/logrus"
)

func getDNSHostAndPort() ([]string, error) {
conf, err := dns.ClientConfigFromFile("/etc/resolv.conf")
func (r *dnsConfig) init() error {
if err := r.refreshNameservers(); err != nil {
return err
}

w, err := utils.NewFileWatcher(etcResolvConfPath)
if err != nil {
return err
}

if err := w.Start(func() { _ = r.refreshNameservers() }); err != nil {
return err
}

return nil
}

func (r *dnsConfig) refreshNameservers() error {
nsList, err := getDNSHostAndPort(etcResolvConfPath)
if err != nil {
log.Errorf("can't load dns nameservers: %v", err)
return err
}

log.Infof("reloading dns nameservers to %v", nsList)

r.mu.Lock()
r.nameservers = nsList
r.mu.Unlock()
return nil
}

const etcResolvConfPath = "/etc/resolv.conf"

func getDNSHostAndPort(path string) ([]string, error) {
conf, err := dns.ClientConfigFromFile(path)
if err != nil {
return []string{}, err
}
var hosts = make([]string, len(conf.Servers))
hosts := make([]string, 0, len(conf.Servers))
for _, server := range conf.Servers {
dnsIP, err := netip.ParseAddr(server)
if err != nil {
Expand Down
11 changes: 10 additions & 1 deletion pkg/services/dns/dns_config_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ import (
qdmDns "github.com/qdm12/dns/v2/pkg/nameserver"
)

func (r *dnsConfig) init() error {
nsList, err := getDNSHostAndPort()
if err != nil {
return err
}

r.nameservers = nsList
return nil
}

func getDNSHostAndPort() ([]string, error) {
nameservers := qdmDns.GetDNSServers()

Expand All @@ -21,5 +31,4 @@ func getDNSHostAndPort() ([]string, error) {
}

return dnsServers, nil

}
54 changes: 18 additions & 36 deletions pkg/services/dns/hosts_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ package dns

import (
"net"
"path/filepath"
"sync"

"github.com/areYouLazy/libhosty"
"github.com/fsnotify/fsnotify"
"github.com/containers/gvisor-tap-vsock/pkg/utils"
log "github.com/sirupsen/logrus"
)

Expand All @@ -23,48 +22,31 @@ func NewHostsFile(hostsPath string) (*HostsFile, error) {
if err != nil {
return nil, err
}
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}

h := &HostsFile{
hostsFile: hostsFile,
hostsFilePath: hostsFile.Config.FilePath,
}
go func() {
h.startWatch(watcher)
}()
if err := h.startWatch(); err != nil {
return nil, err
}

return h, nil
}

func (h *HostsFile) startWatch(w *fsnotify.Watcher) {
err := w.Add(filepath.Dir(h.hostsFilePath))

func (h *HostsFile) startWatch() error {
watcher, err := utils.NewFileWatcher(h.hostsFilePath)
if err != nil {
log.Errorf("Hosts file adding watcher error:%s", err)
return
log.Errorf("Hosts file adding watcher error: %s", err)
return err
}
for {
select {
case err, ok := <-w.Errors:
if !ok {
return
}
log.Errorf("Hosts file watcher error:%s", err)
case event, ok := <-w.Events:
if !ok {
return
}
if event.Name == h.hostsFilePath && event.Op&fsnotify.Write == fsnotify.Write {
err := h.updateHostsFile()
if err != nil {
log.Errorf("Hosts file read error:%s", err)
return
}
}
}

if err := watcher.Start(h.updateHostsFile); err != nil {
log.Errorf("Hosts file adding watcher error: %s", err)
return err
}

return nil
}

func (h *HostsFile) LookupByHostname(name string) (net.IP, error) {
Expand All @@ -75,17 +57,17 @@ func (h *HostsFile) LookupByHostname(name string) (net.IP, error) {
return ip, err
}

func (h *HostsFile) updateHostsFile() error {
func (h *HostsFile) updateHostsFile() {
newHosts, err := readHostsFile(h.hostsFilePath)
if err != nil {
return err
log.Errorf("Hosts file read error:%s", err)
return
}

h.hostsReadLock.Lock()
defer h.hostsReadLock.Unlock()

h.hostsFile = newHosts
return nil
}

func readHostsFile(hostsFilePath string) (*libhosty.HostsFile, error) {
Expand Down
84 changes: 84 additions & 0 deletions pkg/utils/filewatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package utils

import (
"fmt"
"path/filepath"
"time"

"github.com/fsnotify/fsnotify"
)

// FileWatcher is an utility that
type FileWatcher struct {
w *fsnotify.Watcher
path string

writeGracePeriod time.Duration
timer *time.Timer
}

func NewFileWatcher(path string) (*FileWatcher, error) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}

return &FileWatcher{w: watcher, path: path, writeGracePeriod: 200 * time.Millisecond}, nil
}

func (fw *FileWatcher) Start(changeHandler func()) error {
// Ensure that the target that we're watching is not a symlink as we won't get any events when we're watching
// a symlink.
fileRealPath, err := filepath.EvalSymlinks(fw.path)
if err != nil {
return fmt.Errorf("adding watcher failed: %s", err)
}

// watch the directory instead of the individual file to ensure the notification still works when the file is modified
// through moving/renaming rather than writing into it directly (like what most modern editor does by default).
// ref: https://github.com/fsnotify/fsnotify/blob/a9bc2e01792f868516acf80817f7d7d7b3315409/README.md#watching-a-file-doesnt-work-well
if err = fw.w.Add(filepath.Dir(fileRealPath)); err != nil {
return fmt.Errorf("adding watcher failed: %s", err)
}

go func() {
for {
select {
case _, ok := <-fw.w.Errors:
if !ok {
return // watcher is closed.
}
case event, ok := <-fw.w.Events:
if !ok {
return // watcher is closed.
}

if event.Name != fileRealPath {
continue // we don't care about this file.
}

// Create may not always followed by Write e.g. when we replace the file with mv.
if event.Op.Has(fsnotify.Create) || event.Op.Has(fsnotify.Write) {
// as per the documentation, receiving Write does not mean that the write is finished.
// we try our best here to ignore "unfinished" write by assuming that after [writeGracePeriod] of
// inactivity the write has been finished.
fw.debounce(changeHandler)
}
}
}
}()

return nil
}

func (fw *FileWatcher) debounce(fn func()) {
if fw.timer != nil {
fw.timer.Stop()
}

fw.timer = time.AfterFunc(fw.writeGracePeriod, fn)
}

func (fw *FileWatcher) Stop() error {
return fw.w.Close()
}
Loading

0 comments on commit 479f0c3

Please sign in to comment.