mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
So it would work in more general case than just captive portal network, which ctrld have supported recently. Uses who may want no leaking behavior can use a config to turn off this feature.
74 lines
2.2 KiB
Go
74 lines
2.2 KiB
Go
package cli
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"path/filepath"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
)
|
|
|
|
// watchResolvConf watches any changes to /etc/resolv.conf file,
|
|
// and reverting to the original config set by ctrld.
|
|
func (p *prog) watchResolvConf(iface *net.Interface, ns []netip.Addr, setDnsFn func(iface *net.Interface, ns []netip.Addr) error) {
|
|
resolvConfPath := "/etc/resolv.conf"
|
|
// Evaluating symbolics link to watch the target file that /etc/resolv.conf point to.
|
|
if rp, _ := filepath.EvalSymlinks(resolvConfPath); rp != "" {
|
|
resolvConfPath = rp
|
|
}
|
|
mainLog.Load().Debug().Msgf("start watching %s file", resolvConfPath)
|
|
watcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
mainLog.Load().Warn().Err(err).Msg("could not create watcher for /etc/resolv.conf")
|
|
return
|
|
}
|
|
defer watcher.Close()
|
|
|
|
// We watch /etc instead of /etc/resolv.conf directly,
|
|
// see: https://github.com/fsnotify/fsnotify#watching-a-file-doesnt-work-well
|
|
watchDir := filepath.Dir(resolvConfPath)
|
|
if err := watcher.Add(watchDir); err != nil {
|
|
mainLog.Load().Warn().Err(err).Msgf("could not add %s to watcher list", watchDir)
|
|
return
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case <-p.dnsWatcherStopCh:
|
|
return
|
|
case <-p.stopCh:
|
|
mainLog.Load().Debug().Msgf("stopping watcher for %s", resolvConfPath)
|
|
return
|
|
case event, ok := <-watcher.Events:
|
|
if p.leakingQuery.Load() {
|
|
return
|
|
}
|
|
if !ok {
|
|
return
|
|
}
|
|
if event.Name != resolvConfPath { // skip if not /etc/resolv.conf changes.
|
|
continue
|
|
}
|
|
if event.Has(fsnotify.Write) || event.Has(fsnotify.Create) {
|
|
mainLog.Load().Debug().Msg("/etc/resolv.conf changes detected, reverting to ctrld setting")
|
|
if err := watcher.Remove(watchDir); err != nil {
|
|
mainLog.Load().Error().Err(err).Msg("failed to pause watcher")
|
|
continue
|
|
}
|
|
if err := setDnsFn(iface, ns); err != nil {
|
|
mainLog.Load().Error().Err(err).Msg("failed to revert /etc/resolv.conf changes")
|
|
}
|
|
if err := watcher.Add(watchDir); err != nil {
|
|
mainLog.Load().Error().Err(err).Msg("failed to continue running watcher")
|
|
return
|
|
}
|
|
}
|
|
case err, ok := <-watcher.Errors:
|
|
if !ok {
|
|
return
|
|
}
|
|
mainLog.Load().Err(err).Msg("could not get event for /etc/resolv.conf")
|
|
}
|
|
}
|
|
}
|