package main import ( crypto_rand "crypto/rand" "encoding/binary" "flag" "fmt" "math/rand" "os" "runtime" "sync" "github.com/jedisct1/dlog" "github.com/kardianos/service" ) const ( AppVersion = "2.1.5" DefaultConfigFileName = "dnscrypt-proxy.toml" ) type App struct { wg sync.WaitGroup quit chan struct{} proxy *Proxy flags *ConfigFlags } func main() { tzErr := TimezoneSetup() dlog.Init("dnscrypt-proxy", dlog.SeverityNotice, "DAEMON") if tzErr != nil { dlog.Warnf("Timezone setup failed: [%v]", tzErr) } runtime.MemProfileRate = 0 seed := make([]byte, 8) if _, err := crypto_rand.Read(seed); err != nil { dlog.Fatal(err) } rand.Seed(int64(binary.LittleEndian.Uint64(seed))) pwd, err := os.Getwd() if err != nil { dlog.Fatal("Unable to find the path to the current directory") } svcFlag := flag.String("service", "", fmt.Sprintf("Control the system service: %q", service.ControlAction)) version := flag.Bool("version", false, "print current proxy version") flags := ConfigFlags{} flags.Resolve = flag.String("resolve", "", "resolve a DNS name (string can be or ,)") flags.List = flag.Bool("list", false, "print the list of available resolvers for the enabled filters") flags.ListAll = flag.Bool("list-all", false, "print the complete list of available resolvers, ignoring filters") flags.IncludeRelays = flag.Bool("include-relays", false, "include the list of available relays in the output of -list and -list-all") flags.JSONOutput = flag.Bool("json", false, "output list as JSON") flags.Check = flag.Bool("check", false, "check the configuration file and exit") flags.ConfigFile = flag.String("config", DefaultConfigFileName, "Path to the configuration file") flags.Child = flag.Bool("child", false, "Invokes program as a child process") flags.NetprobeTimeoutOverride = flag.Int("netprobe-timeout", 60, "Override the netprobe timeout") flags.ShowCerts = flag.Bool("show-certs", false, "print DoH certificate chain hashes") flag.Parse() if *version { fmt.Println(AppVersion) os.Exit(0) } if fullexecpath, err := os.Executable(); err == nil { WarnIfMaybeWritableByOtherUsers(fullexecpath) } app := &App{ flags: &flags, } svcConfig := &service.Config{ Name: "dnscrypt-proxy", DisplayName: "DNSCrypt client proxy", Description: "Encrypted/authenticated DNS proxy", WorkingDirectory: pwd, Arguments: []string{"-config", *flags.ConfigFile}, } svc, err := service.New(app, svcConfig) if err != nil { svc = nil dlog.Debug(err) } app.proxy = NewProxy() _ = ServiceManagerStartNotify() if len(*svcFlag) != 0 { if svc == nil { dlog.Fatal("Built-in service installation is not supported on this platform") } if err := service.Control(svc, *svcFlag); err != nil { dlog.Fatal(err) } if *svcFlag == "install" { dlog.Notice("Installed as a service. Use `-service start` to start") } else if *svcFlag == "uninstall" { dlog.Notice("Service uninstalled") } else if *svcFlag == "start" { dlog.Notice("Service started") } else if *svcFlag == "stop" { dlog.Notice("Service stopped") } else if *svcFlag == "restart" { dlog.Notice("Service restarted") } return } if svc != nil { if err := svc.Run(); err != nil { dlog.Fatal(err) } } else { app.Start(nil) } } func (app *App) Start(service service.Service) error { if service != nil { go func() { app.AppMain() }() } else { app.AppMain() } return nil } func (app *App) AppMain() { if err := ConfigLoad(app.proxy, app.flags); err != nil { dlog.Fatal(err) } if err := PidFileCreate(); err != nil { dlog.Errorf("Unable to create the PID file: [%v]", err) } if err := app.proxy.InitPluginsGlobals(); err != nil { dlog.Fatal(err) } app.quit = make(chan struct{}) app.wg.Add(1) app.proxy.StartProxy() runtime.GC() <-app.quit dlog.Notice("Quit signal received...") app.wg.Done() } func (app *App) Stop(service service.Service) error { if err := PidFileRemove(); err != nil { dlog.Warnf("Failed to remove the PID file: [%v]", err) } dlog.Notice("Stopped.") return nil }