diff --git a/cmd/ctrld/cli.go b/cmd/ctrld/cli.go index f687642..5c897a4 100644 --- a/cmd/ctrld/cli.go +++ b/cmd/ctrld/cli.go @@ -117,10 +117,6 @@ func initCLI() { } initCache() - if iface == "auto" { - iface = defaultIfaceName() - } - if daemon { exe, err := os.Executable() if err != nil { @@ -173,8 +169,6 @@ func initCLI() { runCmd.Flags().StringVarP(&cdUID, "cd", "", "", "Control D resolver uid") runCmd.Flags().StringVarP(&homedir, "homedir", "", "", "") _ = runCmd.Flags().MarkHidden("homedir") - runCmd.Flags().StringVarP(&iface, "iface", "", "", `Update DNS setting for iface, "auto" means the default interface gateway`) - _ = runCmd.Flags().MarkHidden("iface") rootCmd.AddCommand(runCmd) @@ -214,7 +208,8 @@ func initCLI() { } } - s, err := service.New(&prog{}, sc) + prog := &prog{} + s, err := service.New(prog, sc) if err != nil { stderrMsg(err.Error()) return @@ -226,12 +221,12 @@ func initCLI() { {s.Start, true}, } if doTasks(tasks) { - stdoutMsg("Service started") - return + mainLog.Info().Msg("Service started") + prog.setDNS() } }, } - // Keep these flags in sync with runCmd above, except for "-d". + // Keep these flags in sync with runCmd above, except for "-d", "--iface". startCmd.Flags().StringVarP(&configPath, "config", "c", "", "Path to config file") startCmd.Flags().StringVarP(&configBase64, "base64_config", "", "", "Base64 encoded config") startCmd.Flags().StringVarP(&listenAddress, "listen", "", "", "Listener address and port, in format: address:port") @@ -249,13 +244,16 @@ func initCLI() { Short: "Stop the ctrld service", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - s, err := service.New(&prog{}, svcConfig) + prog := &prog{} + s, err := service.New(prog, svcConfig) if err != nil { stderrMsg(err.Error()) return } + initLogging() if doTasks([]task{{s.Stop, true}}) { - stdoutMsg("Service stopped") + mainLog.Info().Msg("Service stopped") + prog.resetDNS() } }, } @@ -272,6 +270,7 @@ func initCLI() { stderrMsg(err.Error()) return } + initLogging() if doTasks([]task{{s.Restart, true}}) { stdoutMsg("Service restarted") } @@ -310,7 +309,8 @@ func initCLI() { Short: "Uninstall the ctrld service", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - s, err := service.New(&prog{}, svcConfig) + prog := &prog{} + s, err := service.New(prog, svcConfig) if err != nil { stderrMsg(err.Error()) return @@ -319,8 +319,10 @@ func initCLI() { {s.Stop, false}, {s.Uninstall, true}, } + initLogging() if doTasks(tasks) { - stdoutMsg("Service uninstalled") + mainLog.Info().Msg("Service uninstalled") + prog.resetDNS() return } }, @@ -391,9 +393,7 @@ func initCLI() { Use: "start", Short: "Quick start service and configure DNS on interface", Run: func(cmd *cobra.Command, args []string) { - if !cmd.Flags().Changed("iface") { - os.Args = append(os.Args, "--iface="+ifaceStartStop) - } + iface = ifaceStartStop startCmd.Run(cmd, args) }, } @@ -405,9 +405,7 @@ func initCLI() { Use: "stop", Short: "Quick stop service and remove DNS from interface", Run: func(cmd *cobra.Command, args []string) { - if !cmd.Flags().Changed("iface") { - os.Args = append(os.Args, "--iface="+ifaceStartStop) - } + iface = ifaceStartStop stopCmd.Run(cmd, args) }, } diff --git a/cmd/ctrld/main.go b/cmd/ctrld/main.go index ff6a4ad..c336a2b 100644 --- a/cmd/ctrld/main.go +++ b/cmd/ctrld/main.go @@ -3,7 +3,6 @@ package main import ( "fmt" "io" - "net" "os" "path/filepath" "time" @@ -36,7 +35,6 @@ var ( cdUID string iface string - netIface *net.Interface ifaceStartStop string ) diff --git a/cmd/ctrld/os_linux.go b/cmd/ctrld/os_linux.go index a8ff7f7..4439bd9 100644 --- a/cmd/ctrld/os_linux.go +++ b/cmd/ctrld/os_linux.go @@ -3,16 +3,18 @@ package main import ( "bufio" "bytes" + "context" + "fmt" "net" "net/netip" "os/exec" "strings" "syscall" + "time" - "github.com/insomniacslk/dhcp/dhcpv4" - "github.com/insomniacslk/dhcp/dhcpv4/client4" + "github.com/insomniacslk/dhcp/dhcpv4/nclient4" "github.com/insomniacslk/dhcp/dhcpv6" - "github.com/insomniacslk/dhcp/dhcpv6/client6" + "github.com/insomniacslk/dhcp/dhcpv6/nclient6" "tailscale.com/net/dns" "tailscale.com/util/dnsname" @@ -63,40 +65,56 @@ func setDNS(iface *net.Interface, nameservers []string) error { func resetDNS(iface *net.Interface) error { var ns []string - c := client4.NewClient() - conversation, err := c.Exchange(iface.Name) + c, err := nclient4.New(iface.Name) if err != nil { - return err + return fmt.Errorf("nclient4.New: %w", err) } - for _, packet := range conversation { - if packet.MessageType() == dhcpv4.MessageTypeAck { - nameservers := packet.DNS() - for _, nameserver := range nameservers { - ns = append(ns, nameserver.String()) - } + defer c.Close() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + lease, err := c.Request(ctx) + if err != nil { + return fmt.Errorf("nclient4.Request: %w", err) + } + for _, nameserver := range lease.ACK.DNS() { + if nameserver.Equal(net.IPv4zero) { + continue } + ns = append(ns, nameserver.String()) } if supportsIPv6() { - c := client6.NewClient() - conversation, err := c.Exchange(iface.Name) + c, err := nclient6.New(iface.Name) if err != nil { - mainLog.Warn().Err(err).Msg("could not exchange DHCPv6") + mainLog.Warn().Err(err).Msg("could not create DHCPv6 client") + return nil } - for _, packet := range conversation { - if packet.Type() == dhcpv6.MessageTypeReply { - msg, err := packet.GetInnerMessage() - if err != nil { - return err - } - nameservers := msg.Options.DNS() - for _, nameserver := range nameservers { - ns = append(ns, nameserver.String()) - } - } - } - } + defer c.Close() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + solicit, err := dhcpv6.NewSolicit(iface.HardwareAddr) + if err != nil { + return fmt.Errorf("dhcpv6.NewSolicit: %w", err) + } + advertise, err := dhcpv6.NewAdvertiseFromSolicit(solicit) + if err != nil { + return fmt.Errorf("dhcpv6.NewAdvertiseFromSolicit: %w", err) + } + msg, err := c.Request(ctx, advertise) + if err != nil { + return fmt.Errorf("nclient6.Request: %w", err) + } + nameservers := msg.Options.DNS() + for _, nameserver := range nameservers { + if nameserver.Equal(net.IPv6zero) { + continue + } + ns = append(ns, nameserver.String()) + } + + } return ignoringEINTR(func() error { return setDNS(iface, ns) }) diff --git a/cmd/ctrld/prog.go b/cmd/ctrld/prog.go index b0bde48..b17b94c 100644 --- a/cmd/ctrld/prog.go +++ b/cmd/ctrld/prog.go @@ -35,7 +35,6 @@ func (p *prog) Start(s service.Service) error { } func (p *prog) run() { - p.setDNS() if p.cfg.Service.CacheEnable { cacher, err := dnscache.NewLRUCache(p.cfg.Service.CacheSize) if err != nil { @@ -170,7 +169,6 @@ func (p *prog) Stop(s service.Service) error { mainLog.Error().Err(err).Msg("de-allocate ip failed") return err } - p.resetDNS() mainLog.Info().Msg("Service stopped") return nil } @@ -195,18 +193,23 @@ func (p *prog) deAllocateIP() error { } func (p *prog) setDNS() { + if cfg.Listener == nil || cfg.Listener["0"] == nil { + return + } if iface == "" { return } + if iface == "auto" { + iface = defaultIfaceName() + } logger := mainLog.With().Str("iface", iface).Logger() - var err error - netIface, err = netInterface(iface) + netIface, err := netInterface(iface) if err != nil { logger.Error().Err(err).Msg("could not get interface") return } logger.Debug().Msg("setting DNS for interface") - if err := setDNS(netIface, []string{p.cfg.Listener["0"].IP}); err != nil { + if err := setDNS(netIface, []string{cfg.Listener["0"].IP}); err != nil { logger.Error().Err(err).Msgf("could not set DNS for interface") return } @@ -214,10 +217,18 @@ func (p *prog) setDNS() { } func (p *prog) resetDNS() { - if netIface == nil { + if iface == "" { return } + if iface == "auto" { + iface = defaultIfaceName() + } logger := mainLog.With().Str("iface", iface).Logger() + netIface, err := netInterface(iface) + if err != nil { + logger.Error().Err(err).Msg("could not get interface") + return + } logger.Debug().Msg("Restoring DNS for interface") if err := resetDNS(netIface); err != nil { logger.Error().Err(err).Msgf("could not reset DNS") diff --git a/go.mod b/go.mod index f2d5684..1e6f67c 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( github.com/go-playground/validator/v10 v10.11.1 github.com/hashicorp/golang-lru/v2 v2.0.1 + github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e github.com/kardianos/service v1.2.1 github.com/lucas-clemente/quic-go v0.29.1 github.com/miekg/dns v1.1.50 @@ -14,6 +15,7 @@ require ( github.com/spf13/viper v1.14.0 github.com/stretchr/testify v1.8.1 golang.org/x/sys v0.4.0 + golang.zx2c4.com/wireguard/windows v0.5.3 tailscale.com v1.34.1 ) @@ -35,7 +37,6 @@ require ( github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect github.com/illarion/gonotify v1.0.1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e // indirect github.com/josharian/native v1.0.0 // indirect github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b // indirect github.com/leodido/go-urn v1.2.1 // indirect @@ -45,8 +46,10 @@ require ( github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 // indirect github.com/mdlayher/genetlink v1.2.0 // indirect github.com/mdlayher/netlink v1.6.0 // indirect + github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 // indirect github.com/mdlayher/socket v0.2.3 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/nxadm/tail v1.4.8 // indirect @@ -72,7 +75,6 @@ require ( golang.org/x/tools v0.1.12 // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect - golang.zx2c4.com/wireguard/windows v0.5.3 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index c965e25..2a4fe13 100644 --- a/go.sum +++ b/go.sum @@ -164,6 +164,7 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU= github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3/go.mod h1:5PC6ZNPde8bBqU/ewGZig35+UIZtw9Ytxez8/q5ZyFE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -212,6 +213,7 @@ github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZb github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 h1:lez6TS6aAau+8wXUP3G9I3TGlmPFEq2CTxBaRqY6AGE= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU= github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ= @@ -222,6 +224,7 @@ github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZ github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0= github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= +github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 h1:aFkJ6lx4FPip+S+Uw4aTegFMct9shDvP+79PsSxpm3w= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM= @@ -275,6 +278,7 @@ github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=