Skip to content

Commit

Permalink
Merge pull request #37 from Control-D-Inc/release-branch-v1.1.4
Browse files Browse the repository at this point in the history
Release branch v1.1.4
  • Loading branch information
yegors authored Apr 3, 2023
2 parents 9f7bfc7 + a77a924 commit 9c22701
Show file tree
Hide file tree
Showing 23 changed files with 584 additions and 139 deletions.
2 changes: 2 additions & 0 deletions .goreleaser-darwin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ builds:
- -trimpath
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
goos:
- darwin
goarch:
Expand Down
2 changes: 2 additions & 0 deletions .goreleaser-qf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ builds:
- -trimpath
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
goos:
- darwin
- linux
Expand Down
2 changes: 2 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ builds:
- -trimpath
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
goos:
- linux
- freebsd
Expand Down
132 changes: 95 additions & 37 deletions cmd/ctrld/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import (
"runtime"
"strconv"
"strings"
"sync"
"time"

"github.com/fsnotify/fsnotify"

"github.com/cuonglm/osinfo"
"github.com/go-playground/validator/v10"
"github.com/kardianos/service"
"github.com/miekg/dns"
Expand All @@ -35,6 +37,11 @@ import (

const selfCheckFQDN = "verify.controld.com"

var (
version = "dev"
commit = "none"
)

var (
v = viper.NewWithOptions(viper.KeyDelimiter("::"))
defaultConfigWritten = false
Expand All @@ -61,17 +68,28 @@ _/ ___\ __\_ __ \ | / __ |
\/ dns forwarding proxy \/
`

var rootCmd = &cobra.Command{
Use: "ctrld",
Short: strings.TrimLeft(rootShortDesc, "\n"),
Version: curVersion(),
}

func curVersion() string {
if version != "dev" {
version = "v" + version
}
if len(commit) > 7 {
commit = commit[:7]
}
return fmt.Sprintf("%s-%s", version, commit)
}

func initCLI() {
// Enable opening via explorer.exe on Windows.
// See: https://github.com/spf13/cobra/issues/844.
cobra.MousetrapHelpText = ""
cobra.EnableCommandSorting = false

rootCmd := &cobra.Command{
Use: "ctrld",
Short: strings.TrimLeft(rootShortDesc, "\n"),
Version: "1.1.3",
}
rootCmd.PersistentFlags().CountVarP(
&verbose,
"verbose",
Expand All @@ -89,6 +107,35 @@ func initCLI() {
if daemon && runtime.GOOS == "windows" {
log.Fatal("Cannot run in daemon mode. Please install a Windows service.")
}

waitCh := make(chan struct{})
stopCh := make(chan struct{})
if !daemon {
// We need to call s.Run() as soon as possible to response to the OS manager, so it
// can see ctrld is running and don't mark ctrld as failed service.
go func() {
p := &prog{
waitCh: waitCh,
stopCh: stopCh,
}
s, err := service.New(p, svcConfig)
if err != nil {
mainLog.Fatal().Err(err).Msg("failed create new service")
}
serviceLogger, err := s.Logger(nil)
if err != nil {
mainLog.Error().Err(err).Msg("failed to get service logger")
return
}

if err := s.Run(); err != nil {
if sErr := serviceLogger.Error(err); sErr != nil {
mainLog.Error().Err(sErr).Msg("failed to write service log")
}
mainLog.Error().Err(err).Msg("failed to start service")
}
}()
}
noConfigStart := isNoConfigStart(cmd)
writeDefaultConfig := !noConfigStart && configBase64 == ""
configs := []struct {
Expand All @@ -112,7 +159,11 @@ func initCLI() {
if err := v.Unmarshal(&cfg); err != nil {
log.Fatalf("failed to unmarshal config: %v", err)
}
fmt.Println("starting ctrld...")

log.Printf("starting ctrld %s\n", curVersion())
oi := osinfo.New()
log.Printf("os: %s\n", oi.String())

// Wait for network up.
if !ctrldnet.Up() {
log.Fatal("network is not up yet")
Expand Down Expand Up @@ -149,22 +200,8 @@ func initCLI() {
os.Exit(0)
}

s, err := service.New(&prog{}, svcConfig)
if err != nil {
mainLog.Fatal().Err(err).Msg("failed create new service")
}
serviceLogger, err := s.Logger(nil)
if err != nil {
mainLog.Error().Err(err).Msg("failed to get service logger")
return
}

if err := s.Run(); err != nil {
if sErr := serviceLogger.Error(err); sErr != nil {
mainLog.Error().Err(sErr).Msg("failed to write service log")
}
mainLog.Error().Err(err).Msg("failed to start service")
}
close(waitCh)
<-stopCh
},
}
runCmd.Flags().BoolVarP(&daemon, "daemon", "d", false, "Run as daemon")
Expand Down Expand Up @@ -346,6 +383,10 @@ func initCLI() {
}
},
}
if runtime.GOOS == "darwin" {
// On darwin, running status command without privileges may return wrong information.
statusCmd.PreRun = checkHasElevatedPrivilege
}

uninstallCmd := &cobra.Command{
PreRun: checkHasElevatedPrivilege,
Expand Down Expand Up @@ -506,15 +547,8 @@ func readConfigFile(writeDefaultConfig bool) bool {
// If err == nil, there's a config supplied via `--config`, no default config written.
err := v.ReadInConfig()
if err == nil {
fmt.Println("loading config file from:", v.ConfigFileUsed())
log.Println("loading config file from:", v.ConfigFileUsed())
defaultConfigFile = v.ConfigFileUsed()
v.OnConfigChange(func(in fsnotify.Event) {
if err := v.UnmarshalKey("listener", &cfg.Listener); err != nil {
log.Printf("failed to unmarshal listener config: %v", err)
return
}
})
v.WatchConfig()
return true
}

Expand All @@ -527,7 +561,7 @@ func readConfigFile(writeDefaultConfig bool) bool {
if err := writeConfigFile(); err != nil {
log.Fatalf("failed to write default config file: %v", err)
} else {
fmt.Println("writing default config file to: " + defaultConfigFile)
log.Println("writing default config file to: " + defaultConfigFile)
}
defaultConfigWritten = true
return false
Expand Down Expand Up @@ -559,18 +593,24 @@ func processNoConfigFlags(noConfigStart bool) {
}
processListenFlag()

endpointAndTyp := func(endpoint string) (string, string) {
typ := ctrld.ResolverTypeFromEndpoint(endpoint)
return strings.TrimPrefix(endpoint, "quic://"), typ
}
pEndpoint, pType := endpointAndTyp(primaryUpstream)
upstream := map[string]*ctrld.UpstreamConfig{
"0": {
Name: primaryUpstream,
Endpoint: primaryUpstream,
Type: ctrld.ResolverTypeDOH,
Name: pEndpoint,
Endpoint: pEndpoint,
Type: pType,
},
}
if secondaryUpstream != "" {
sEndpoint, sType := endpointAndTyp(secondaryUpstream)
upstream["1"] = &ctrld.UpstreamConfig{
Name: secondaryUpstream,
Endpoint: secondaryUpstream,
Type: ctrld.ResolverTypeLegacy,
Name: sEndpoint,
Endpoint: sEndpoint,
Type: sType,
}
rules := make([]ctrld.Rule, 0, len(domains))
for _, domain := range domains {
Expand Down Expand Up @@ -727,8 +767,26 @@ func selfCheckStatus(status service.Status) service.Status {
err := errors.New("query failed")
maxAttempts := 20
mainLog.Debug().Msg("Performing self-check")
var (
lcChanged map[string]*ctrld.ListenerConfig
mu sync.Mutex
)
v.OnConfigChange(func(in fsnotify.Event) {
mu.Lock()
defer mu.Unlock()
if err := v.UnmarshalKey("listener", &lcChanged); err != nil {
log.Printf("failed to unmarshal listener config: %v", err)
return
}
})
v.WatchConfig()
for i := 0; i < maxAttempts; i++ {
lc := cfg.Listener["0"]
mu.Lock()
if lcChanged != nil {
lc = lcChanged["0"]
}
mu.Unlock()
m := new(dns.Msg)
m.SetQuestion(selfCheckFQDN+".", dns.TypeA)
m.RecursionDesired = true
Expand Down
10 changes: 9 additions & 1 deletion cmd/ctrld/dns_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (p *prog) serveDNS(listenerNum string) error {
}
})

g := new(errgroup.Group)
g, ctx := errgroup.WithContext(context.Background())
for _, proto := range []string{"udp", "tcp"} {
proto := proto
// On Windows, there's no easy way for disabling/removing IPv6 DNS resolver, so we check whether we can
Expand All @@ -68,6 +68,10 @@ func (p *prog) serveDNS(listenerNum string) error {
Net: proto,
Handler: handler,
}
go func() {
<-ctx.Done()
_ = s.Shutdown()
}()
if err := s.ListenAndServe(); err != nil {
mainLog.Error().Err(err).Msg("could not serving on ::1")
}
Expand All @@ -80,6 +84,10 @@ func (p *prog) serveDNS(listenerNum string) error {
Net: proto,
Handler: handler,
}
go func() {
<-ctx.Done()
_ = s.Shutdown()
}()
if err := s.ListenAndServe(); err != nil {
mainLog.Error().Err(err).Msgf("could not listen and serve on: %s", s.Addr)
return err
Expand Down
1 change: 0 additions & 1 deletion cmd/ctrld/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ func initLogging() {
}
writers = append(writers, logFile)
}
zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs
consoleWriter := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
w.TimeFormat = time.StampMilli
})
Expand Down
13 changes: 13 additions & 0 deletions cmd/ctrld/prog.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ var svcConfig = &service.Config{
}

type prog struct {
mu sync.Mutex
waitCh chan struct{}
stopCh chan struct{}

cfg *ctrld.Config
cache dnscache.Cacher
}
Expand All @@ -39,6 +43,8 @@ func (p *prog) Start(s service.Service) error {
}

func (p *prog) run() {
// Wait the caller to signal that we can do our logic.
<-p.waitCh
p.preRun()
if p.cfg.Service.CacheEnable {
cacher, err := dnscache.NewLRUCache(p.cfg.Service.CacheSize)
Expand Down Expand Up @@ -106,7 +112,9 @@ func (p *prog) run() {
} else {
mainLog.Info().Msg("writing config file to: " + defaultConfigFile)
}
p.mu.Lock()
p.cfg.Service.AllocateIP = true
p.mu.Unlock()
p.preRun()
mainLog.Info().Msgf("Starting DNS server on listener.%s: %s", listenerNum, net.JoinHostPort(ip, strconv.Itoa(port)))
if err := p.serveDNS(listenerNum); err != nil {
Expand All @@ -128,17 +136,22 @@ func (p *prog) Stop(s service.Service) error {
return err
}
mainLog.Info().Msg("Service stopped")
close(p.stopCh)
return nil
}

func (p *prog) allocateIP(ip string) error {
p.mu.Lock()
defer p.mu.Unlock()
if !p.cfg.Service.AllocateIP {
return nil
}
return allocateIP(ip)
}

func (p *prog) deAllocateIP() error {
p.mu.Lock()
defer p.mu.Unlock()
if !p.cfg.Service.AllocateIP {
return nil
}
Expand Down
Loading

0 comments on commit 9c22701

Please sign in to comment.