diff --git a/cmd/ctrld/cli.go b/cmd/ctrld/cli.go index f9c69cc..b48813d 100644 --- a/cmd/ctrld/cli.go +++ b/cmd/ctrld/cli.go @@ -111,11 +111,7 @@ func initCLI() { initCache() if iface == "auto" { - dri, err := interfaces.DefaultRouteInterface() - if err != nil { - mainLog.Error().Err(err).Msg("failed to get default route interface") - } - iface = dri + iface = defaultIfaceName() } if daemon { @@ -450,6 +446,7 @@ func readConfigFile(writeDefaultConfig bool) bool { err := v.ReadInConfig() if err == nil { fmt.Println("loading config file from:", v.ConfigFileUsed()) + defaultConfigFile = v.ConfigFileUsed() return true } @@ -529,6 +526,12 @@ func processCDFlags() { stderrMsg(err.Error()) return } + if iface == "auto" { + iface = defaultIfaceName() + } + if netIface, _ := netIfaceFromName(iface); netIface != nil { + _ = resetDNS(netIface) + } tasks := []task{{s.Uninstall, true}} if doTasks(tasks) { log.Println("uninstalled service") @@ -623,3 +626,11 @@ func netIfaceFromName(ifaceName string) (*net.Interface, error) { } return iface, err } + +func defaultIfaceName() string { + dri, err := interfaces.DefaultRouteInterface() + if err != nil { + mainLog.Error().Err(err).Msg("failed to get default route interface") + } + return dri +} diff --git a/cmd/ctrld/os_linux.go b/cmd/ctrld/os_linux.go index 39818a3..98ccf00 100644 --- a/cmd/ctrld/os_linux.go +++ b/cmd/ctrld/os_linux.go @@ -8,6 +8,10 @@ import ( "os/exec" "strings" + "github.com/insomniacslk/dhcp/dhcpv4" + "github.com/insomniacslk/dhcp/dhcpv4/client4" + "github.com/insomniacslk/dhcp/dhcpv6" + "github.com/insomniacslk/dhcp/dhcpv6/client6" "tailscale.com/net/dns" "tailscale.com/util/dnsname" @@ -45,7 +49,7 @@ func setDNS(iface *net.Interface, nameservers []string) error { mainLog.Error().Err(err).Msg("failed to create DNS OS configurator") return err } - + defer r.Close() ns := make([]netip.Addr, 0, len(nameservers)) for _, nameserver := range nameservers { ns = append(ns, netip.MustParseAddr(nameserver)) @@ -56,11 +60,44 @@ func setDNS(iface *net.Interface, nameservers []string) error { }) } -func resetDNS(iface *net.Interface, nameservers []string) error { - if err := setDNS(iface, nameservers); err != nil { - mainLog.Error().Err(err).Msg("resetDNS failed.") +func resetDNS(iface *net.Interface) error { + c := client4.NewClient() + conversation, err := c.Exchange(iface.Name) + if err != nil { return err } + for _, packet := range conversation { + if packet.MessageType() == dhcpv4.MessageTypeAck { + nameservers := packet.DNS() + ns := make([]string, 0, len(nameservers)) + for _, nameserver := range nameservers { + ns = append(ns, nameserver.String()) + } + _ = setDNS(iface, ns) + } + } + + if supportsIPv6() { + c := client6.NewClient() + conversation, err := c.Exchange(iface.Name) + if err != nil { + return err + } + for _, packet := range conversation { + if packet.Type() == dhcpv6.MessageTypeReply { + msg, err := packet.GetInnerMessage() + if err != nil { + return err + } + nameservers := msg.Options.DNS() + ns := make([]string, 0, len(nameservers)) + for _, nameserver := range nameservers { + ns = append(ns, nameserver.String()) + } + _ = setDNS(iface, ns) + } + } + } return nil } diff --git a/cmd/ctrld/os_mac.go b/cmd/ctrld/os_mac.go index 99d7f91..95786f3 100644 --- a/cmd/ctrld/os_mac.go +++ b/cmd/ctrld/os_mac.go @@ -6,6 +6,8 @@ package main import ( "net" "os/exec" + + "github.com/Control-D-Inc/ctrld/internal/resolvconffile" ) // allocate loopback ip @@ -44,7 +46,7 @@ func setDNS(iface *net.Interface, nameservers []string) error { } // TODO(cuonglm): use system API -func resetDNS(iface *net.Interface, _ []string) error { +func resetDNS(iface *net.Interface) error { cmd := "networksetup" args := []string{"-setdnsservers", iface.Name, "empty"} @@ -56,5 +58,5 @@ func resetDNS(iface *net.Interface, _ []string) error { } func currentDNS(_ *net.Interface) []string { - return nil + return resolvconffile.NameServers("") } diff --git a/cmd/ctrld/os_windows.go b/cmd/ctrld/os_windows.go index 075b30d..213c104 100644 --- a/cmd/ctrld/os_windows.go +++ b/cmd/ctrld/os_windows.go @@ -4,7 +4,6 @@ package main import ( - "bytes" "errors" "net" "os/exec" @@ -39,14 +38,18 @@ func setDNS(iface *net.Interface, nameservers []string) error { } // TODO(cuonglm): should we use system API? -func resetDNS(iface *net.Interface, nameservers []string) error { - if err := resetDNSUseDHCP(iface); err != nil { - mainLog.Debug().Err(err).Msg("could not reset DNS using DHCP") +func resetDNS(iface *net.Interface) error { + if supportsIPv6ListenLocal() { + if output, err := netsh("interface", "ipv6", "set", "dnsserver", strconv.Itoa(iface.Index), "dhcp"); err != nil { + mainLog.Warn().Err(err).Msgf("failed to reset ipv6 DNS: %s", string(output)) + } } - if len(nameservers) == 0 { - return nil + output, err := netsh("interface", "ipv4", "set", "dnsserver", strconv.Itoa(iface.Index), "dhcp") + if err != nil { + mainLog.Error().Err(err).Msgf("failed to reset ipv4 DNS: %s", string(output)) + return err } - return setDNS(iface, nameservers) + return nil } func setPrimaryDNS(iface *net.Interface, dns string) error { @@ -80,28 +83,11 @@ func addSecondaryDNS(iface *net.Interface, dns string) error { return nil } -func resetDNSUseDHCP(iface *net.Interface) error { - if supportsIPv6ListenLocal() { - if output, err := netsh("interface", "ipv6", "set", "dnsserver", strconv.Itoa(iface.Index), "dhcp"); err != nil { - mainLog.Warn().Err(err).Msgf("failed to reset ipv6 DNS: %s", string(output)) - } - } - output, err := netsh("interface", "ipv4", "set", "dnsserver", strconv.Itoa(iface.Index), "dhcp") - if err != nil { - mainLog.Error().Err(err).Msgf("failed to reset ipv4 DNS: %s", string(output)) - return err - } - return nil -} - func netsh(args ...string) ([]byte, error) { return exec.Command("netsh", args...).Output() } func currentDNS(iface *net.Interface) []string { - if hasDNSFromDHCP(iface, "ipv4") || hasDNSFromDHCP(iface, "ipv6") { - return nil - } luid, err := winipcfg.LUIDFromIndex(uint32(iface.Index)) if err != nil { mainLog.Error().Err(err).Msg("failed to get interface LUID") @@ -118,9 +104,3 @@ func currentDNS(iface *net.Interface) []string { } return ns } - -func hasDNSFromDHCP(iface *net.Interface, ipVer string) bool { - idx := strconv.Itoa(iface.Index) - output, _ := netsh("interface", ipVer, "show", "dnsservers", idx) - return bytes.Contains(output, []byte(" through DHCP:")) -} diff --git a/cmd/ctrld/prog.go b/cmd/ctrld/prog.go index 8d26abe..30a0688 100644 --- a/cmd/ctrld/prog.go +++ b/cmd/ctrld/prog.go @@ -23,9 +23,8 @@ var svcConfig = &service.Config{ } type prog struct { - cfg *ctrld.Config - cache dnscache.Cacher - origDNS []string + cfg *ctrld.Config + cache dnscache.Cacher } func (p *prog) Start(s service.Service) error { @@ -40,7 +39,6 @@ func (p *prog) run() { if err != nil { mainLog.Error().Err(err).Msg("could not get interface") } else { - p.origDNS = currentDNS(netIface) if err := setDNS(netIface, []string{cfg.Listener["0"].IP}); err != nil { mainLog.Error().Err(err).Str("iface", iface).Msgf("could not set DNS for interface") } @@ -179,7 +177,7 @@ func (p *prog) Stop(s service.Service) error { } if iface != "" { if netIface, err := netIfaceFromName(iface); err == nil { - if err := resetDNS(netIface, p.origDNS); err != nil { + if err := resetDNS(netIface); err != nil { mainLog.Error().Err(err).Str("iface", iface).Msgf("could not reset DNS") } } else {