diff --git a/home/auth.go b/home/auth.go index 36e56d05..efdbf1c8 100644 --- a/home/auth.go +++ b/home/auth.go @@ -489,7 +489,7 @@ func (a *Auth) GetCurrentUser(r *http.Request) User { // there's no Cookie, check Basic authentication user, pass, ok := r.BasicAuth() if ok { - u := Context.auth.UserFind(user, pass) + u := a.UserFind(user, pass) return u } return User{} diff --git a/home/context.go b/home/context.go new file mode 100644 index 00000000..19794adb --- /dev/null +++ b/home/context.go @@ -0,0 +1,102 @@ +package home + +import ( + "crypto/x509" + "net/http" + "os" + "path/filepath" + "sync" + + "github.com/AdguardTeam/AdGuardHome/dhcpd" + "github.com/AdguardTeam/AdGuardHome/dnsfilter" + "github.com/AdguardTeam/AdGuardHome/dnsforward" + "github.com/AdguardTeam/AdGuardHome/querylog" + "github.com/AdguardTeam/AdGuardHome/stats" + "github.com/AdguardTeam/AdGuardHome/update" + "github.com/AdguardTeam/AdGuardHome/util" + + "github.com/AdguardTeam/golibs/log" +) + +// Global context +type homeContext struct { + // Modules + // -- + + clients clientsContainer // per-client-settings module + stats stats.Stats // statistics module + queryLog querylog.QueryLog // query log module + dnsServer *dnsforward.Server // DNS module + rdns *RDNS // rDNS module + whois *Whois // WHOIS module + dnsFilter *dnsfilter.Dnsfilter // DNS filtering module + dhcpServer *dhcpd.Server // DHCP module + auth *Auth // HTTP authentication module + filters Filtering // DNS filtering module + web *Web // Web (HTTP, HTTPS) module + tls *TLSMod // TLS module + autoHosts util.AutoHosts // IP-hostname pairs taken from system configuration (e.g. /etc/hosts) files + updater *update.Updater + + // Runtime properties + // -- + + controlLock sync.Mutex + configFilename string // Config filename (can be overridden via the command line arguments) + workDir string // Location of our directory, used to protect against CWD being somewhere else + firstRun bool // if set to true, don't run any services except HTTP web inteface, and serve only first-run html + pidFileName string // PID file name. Empty if no PID file was created. + disableUpdate bool // If set, don't check for updates + tlsRoots *x509.CertPool // list of root CAs for TLSv1.2 + tlsCiphers []uint16 // list of TLS ciphers to use + transport *http.Transport + client *http.Client + appSignalChannel chan os.Signal // Channel for receiving OS signals by the console app + // runningAsService flag is set to true when options are passed from the service runner + runningAsService bool +} + +// getDataDir returns path to the directory where we store databases and filters +func (c *homeContext) getDataDir() string { + return filepath.Join(c.workDir, dataDir) +} + +// Context - a global context object +var Context homeContext + +func (c *homeContext) cleanup() { + log.Info("Stopping AdGuard Home") + + if c.web != nil { + c.web.Close() + c.web = nil + } + if c.auth != nil { + c.auth.Close() + c.auth = nil + } + + err := c.stopDNSServer() + if err != nil { + log.Error("Couldn't stop DNS server: %s", err) + } + + if c.dhcpServer != nil { + c.dhcpServer.Stop() + } + + c.autoHosts.Close() + + if c.tls != nil { + c.tls.Close() + c.tls = nil + } +} + +// This function is called before application exits +func (c *homeContext) cleanupAlways() { + if len(c.pidFileName) != 0 { + _ = os.Remove(c.pidFileName) + } + log.Info("Stopped") +} diff --git a/home/control.go b/home/control.go index e8b2fa8a..62408213 100644 --- a/home/control.go +++ b/home/control.go @@ -47,10 +47,10 @@ func handleStatus(w http.ResponseWriter, r *http.Request) { Context.dnsServer.WriteDiskConfig(&c) } data := map[string]interface{}{ - "dns_addresses": getDNSAddresses(), + "dns_addresses": Context.getDNSAddresses(), "http_port": config.BindPort, "dns_port": config.DNS.Port, - "running": isRunning(), + "running": Context.isRunning(), "version": versionString, "language": config.Language, diff --git a/home/control_update.go b/home/control_update.go index fb160900..e2b98163 100644 --- a/home/control_update.go +++ b/home/control_update.go @@ -119,8 +119,8 @@ func getVersionResp(info update.VersionInfo) []byte { // Complete an update procedure func finishUpdate() { log.Info("Stopping all tasks") - cleanup() - cleanupAlways() + Context.cleanup() + Context.cleanupAlways() exeName := "AdGuardHome" if runtime.GOOS == "windows" { diff --git a/home/dns.go b/home/dns.go index 2d647f94..36c38513 100644 --- a/home/dns.go +++ b/home/dns.go @@ -23,9 +23,9 @@ func onConfigModified() { // initDNSServer creates an instance of the dnsforward.Server // Please note that we must do it even if we don't start it // so that we had access to the query log and the stats -func initDNSServer() error { +func (c *homeContext) initDNSServer() error { var err error - baseDir := Context.getDataDir() + baseDir := c.getDataDir() statsConf := stats.Config{ Filename: filepath.Join(baseDir, "stats.db"), @@ -34,7 +34,7 @@ func initDNSServer() error { ConfigModified: onConfigModified, HTTPRegister: httpRegister, } - Context.stats, err = stats.New(statsConf) + c.stats, err = stats.New(statsConf) if err != nil { return fmt.Errorf("couldn't initialize statistics module") } @@ -48,7 +48,7 @@ func initDNSServer() error { ConfigModified: onConfigModified, HTTPRegister: httpRegister, } - Context.queryLog = querylog.New(conf) + c.queryLog = querylog.New(conf) filterConf := config.DNS.DnsfilterConf bindhost := config.DNS.BindHost @@ -56,93 +56,39 @@ func initDNSServer() error { bindhost = "127.0.0.1" } filterConf.ResolverAddress = fmt.Sprintf("%s:%d", bindhost, config.DNS.Port) - filterConf.AutoHosts = &Context.autoHosts + filterConf.AutoHosts = &c.autoHosts filterConf.ConfigModified = onConfigModified filterConf.HTTPRegister = httpRegister - Context.dnsFilter = dnsfilter.New(&filterConf, nil) + c.dnsFilter = dnsfilter.New(&filterConf, nil) p := dnsforward.DNSCreateParams{ - DNSFilter: Context.dnsFilter, - Stats: Context.stats, - QueryLog: Context.queryLog, + DNSFilter: c.dnsFilter, + Stats: c.stats, + QueryLog: c.queryLog, } - if Context.dhcpServer != nil { - p.DHCPServer = Context.dhcpServer + if c.dhcpServer != nil { + p.DHCPServer = c.dhcpServer } - Context.dnsServer = dnsforward.NewServer(p) - dnsConfig := generateServerConfig() - err = Context.dnsServer.Prepare(&dnsConfig) + c.dnsServer = dnsforward.NewServer(p) + dnsConfig := c.generateServerConfig() + err = c.dnsServer.Prepare(&dnsConfig) if err != nil { - closeDNSServer() + c.closeDNSServer() return fmt.Errorf("dnsServer.Prepare: %s", err) } - Context.rdns = InitRDNS(Context.dnsServer, &Context.clients) - Context.whois = initWhois(&Context.clients) + c.rdns = InitRDNS(c.dnsServer, &c.clients) + c.whois = initWhois(&c.clients) - Context.filters.Init() + c.filters.Init() return nil } -func isRunning() bool { - return Context.dnsServer != nil && Context.dnsServer.IsRunning() +func (c *homeContext) isRunning() bool { + return c.dnsServer != nil && c.dnsServer.IsRunning() } -// nolint (gocyclo) -// Return TRUE if IP is within public Internet IP range -func isPublicIP(ip net.IP) bool { - ip4 := ip.To4() - if ip4 != nil { - switch ip4[0] { - case 0: - return false //software - case 10: - return false //private network - case 127: - return false //loopback - case 169: - if ip4[1] == 254 { - return false //link-local - } - case 172: - if ip4[1] >= 16 && ip4[1] <= 31 { - return false //private network - } - case 192: - if (ip4[1] == 0 && ip4[2] == 0) || //private network - (ip4[1] == 0 && ip4[2] == 2) || //documentation - (ip4[1] == 88 && ip4[2] == 99) || //reserved - (ip4[1] == 168) { //private network - return false - } - case 198: - if (ip4[1] == 18 || ip4[2] == 19) || //private network - (ip4[1] == 51 || ip4[2] == 100) { //documentation - return false - } - case 203: - if ip4[1] == 0 && ip4[2] == 113 { //documentation - return false - } - case 224: - if ip4[1] == 0 && ip4[2] == 0 { //multicast - return false - } - case 255: - if ip4[1] == 255 && ip4[2] == 255 && ip4[3] == 255 { //subnet - return false - } - } - } else { - if ip.IsLoopback() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() { - return false - } - } - - return true -} - -func onDNSRequest(d *proxy.DNSContext) { +func (c *homeContext) onDNSRequest(d *proxy.DNSContext) { ip := dnsforward.GetIPString(d.Addr) if ip == "" { // This would be quite weird if we get here @@ -151,25 +97,25 @@ func onDNSRequest(d *proxy.DNSContext) { ipAddr := net.ParseIP(ip) if !ipAddr.IsLoopback() { - Context.rdns.Begin(ip) + c.rdns.Begin(ip) } - if isPublicIP(ipAddr) { - Context.whois.Begin(ip) + if util.IsPublicIP(ipAddr) { + c.whois.Begin(ip) } } -func generateServerConfig() dnsforward.ServerConfig { +func (c *homeContext) generateServerConfig() dnsforward.ServerConfig { newconfig := dnsforward.ServerConfig{ UDPListenAddr: &net.UDPAddr{IP: net.ParseIP(config.DNS.BindHost), Port: config.DNS.Port}, TCPListenAddr: &net.TCPAddr{IP: net.ParseIP(config.DNS.BindHost), Port: config.DNS.Port}, FilteringConfig: config.DNS.FilteringConfig, ConfigModified: onConfigModified, HTTPRegister: httpRegister, - OnDNSRequest: onDNSRequest, + OnDNSRequest: c.onDNSRequest, } tlsConf := tlsConfigSettings{} - Context.tls.WriteDiskConfig(&tlsConf) + c.tls.WriteDiskConfig(&tlsConf) if tlsConf.Enabled { newconfig.TLSConfig = tlsConf.TLSConfig if tlsConf.PortDNSOverTLS != 0 { @@ -179,17 +125,17 @@ func generateServerConfig() dnsforward.ServerConfig { } } } - newconfig.TLSv12Roots = Context.tlsRoots - newconfig.TLSCiphers = Context.tlsCiphers + newconfig.TLSv12Roots = c.tlsRoots + newconfig.TLSCiphers = c.tlsCiphers newconfig.TLSAllowUnencryptedDOH = tlsConf.AllowUnencryptedDOH - newconfig.FilterHandler = applyAdditionalFiltering - newconfig.GetCustomUpstreamByClient = Context.clients.FindUpstreams + newconfig.FilterHandler = c.applyAdditionalFiltering + newconfig.GetCustomUpstreamByClient = c.clients.FindUpstreams return newconfig } // Get the list of DNS addresses the server is listening on -func getDNSAddresses() []string { +func (c *homeContext) getDNSAddresses() []string { dnsAddresses := []string{} if config.DNS.BindHost == "0.0.0.0" { @@ -209,7 +155,7 @@ func getDNSAddresses() []string { } tlsConf := tlsConfigSettings{} - Context.tls.WriteDiskConfig(&tlsConf) + c.tls.WriteDiskConfig(&tlsConf) if tlsConf.Enabled && len(tlsConf.ServerName) != 0 { if tlsConf.PortHTTPS != 0 { @@ -231,75 +177,75 @@ func getDNSAddresses() []string { } // If a client has his own settings, apply them -func applyAdditionalFiltering(clientAddr string, setts *dnsfilter.RequestFilteringSettings) { - Context.dnsFilter.ApplyBlockedServices(setts, nil, true) +func (c *homeContext) applyAdditionalFiltering(clientAddr string, setts *dnsfilter.RequestFilteringSettings) { + c.dnsFilter.ApplyBlockedServices(setts, nil, true) if len(clientAddr) == 0 { return } setts.ClientIP = clientAddr - c, ok := Context.clients.Find(clientAddr) + cl, ok := c.clients.Find(clientAddr) if !ok { return } - log.Debug("Using settings for client %s with IP %s", c.Name, clientAddr) + log.Debug("Using settings for client %s with IP %s", cl.Name, clientAddr) - if c.UseOwnBlockedServices { - Context.dnsFilter.ApplyBlockedServices(setts, c.BlockedServices, false) + if cl.UseOwnBlockedServices { + c.dnsFilter.ApplyBlockedServices(setts, cl.BlockedServices, false) } - setts.ClientName = c.Name - setts.ClientTags = c.Tags + setts.ClientName = cl.Name + setts.ClientTags = cl.Tags - if !c.UseOwnSettings { + if !cl.UseOwnSettings { return } - setts.FilteringEnabled = c.FilteringEnabled - setts.SafeSearchEnabled = c.SafeSearchEnabled - setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled - setts.ParentalEnabled = c.ParentalEnabled + setts.FilteringEnabled = cl.FilteringEnabled + setts.SafeSearchEnabled = cl.SafeSearchEnabled + setts.SafeBrowsingEnabled = cl.SafeBrowsingEnabled + setts.ParentalEnabled = cl.ParentalEnabled } -func startDNSServer() error { - if isRunning() { +func (c *homeContext) startDNSServer() error { + if c.isRunning() { return fmt.Errorf("unable to start forwarding DNS server: Already running") } enableFilters(false) - Context.clients.Start() + c.clients.Start() - err := Context.dnsServer.Start() + err := c.dnsServer.Start() if err != nil { return errorx.Decorate(err, "Couldn't start forwarding DNS server") } - Context.dnsFilter.Start() - Context.filters.Start() - Context.stats.Start() - Context.queryLog.Start() + c.dnsFilter.Start() + c.filters.Start() + c.stats.Start() + c.queryLog.Start() const topClientsNumber = 100 // the number of clients to get - topClients := Context.stats.GetTopClientsIP(topClientsNumber) + topClients := c.stats.GetTopClientsIP(topClientsNumber) for _, ip := range topClients { ipAddr := net.ParseIP(ip) if !ipAddr.IsLoopback() { - Context.rdns.Begin(ip) + c.rdns.Begin(ip) } - if isPublicIP(ipAddr) { - Context.whois.Begin(ip) + if util.IsPublicIP(ipAddr) { + c.whois.Begin(ip) } } return nil } -func reconfigureDNSServer() error { - newconfig := generateServerConfig() - err := Context.dnsServer.Reconfigure(&newconfig) +func (c *homeContext) reconfigureDNSServer() error { + newconfig := c.generateServerConfig() + err := c.dnsServer.Reconfigure(&newconfig) if err != nil { return errorx.Decorate(err, "Couldn't start forwarding DNS server") } @@ -307,43 +253,42 @@ func reconfigureDNSServer() error { return nil } -func stopDNSServer() error { - if !isRunning() { +func (c *homeContext) stopDNSServer() error { + if !c.isRunning() { return nil } - err := Context.dnsServer.Stop() + err := c.dnsServer.Stop() if err != nil { return errorx.Decorate(err, "Couldn't stop forwarding DNS server") } - closeDNSServer() + c.closeDNSServer() return nil } -func closeDNSServer() { +func (c *homeContext) closeDNSServer() { // DNS forward module must be closed BEFORE stats or queryLog because it depends on them - if Context.dnsServer != nil { - Context.dnsServer.Close() - Context.dnsServer = nil + if c.dnsServer != nil { + c.dnsServer.Close() + c.dnsServer = nil } - if Context.dnsFilter != nil { - Context.dnsFilter.Close() - Context.dnsFilter = nil + if c.dnsFilter != nil { + c.dnsFilter.Close() + c.dnsFilter = nil } - if Context.stats != nil { - Context.stats.Close() - Context.stats = nil + if c.stats != nil { + c.stats.Close() + c.stats = nil } - if Context.queryLog != nil { - Context.queryLog.Close() - Context.queryLog = nil + if c.queryLog != nil { + c.queryLog.Close() + c.queryLog = nil } - Context.filters.Close() - + c.filters.Close() log.Debug("Closed all DNS modules") } diff --git a/home/home.go b/home/home.go index b9d7a760..a9d86114 100644 --- a/home/home.go +++ b/home/home.go @@ -3,7 +3,6 @@ package home import ( "context" "crypto/tls" - "crypto/x509" "fmt" "io/ioutil" "net" @@ -14,7 +13,6 @@ import ( "path/filepath" "runtime" "strconv" - "sync" "syscall" "time" @@ -29,9 +27,6 @@ import ( "github.com/AdguardTeam/AdGuardHome/dhcpd" "github.com/AdguardTeam/AdGuardHome/dnsfilter" - "github.com/AdguardTeam/AdGuardHome/dnsforward" - "github.com/AdguardTeam/AdGuardHome/querylog" - "github.com/AdguardTeam/AdGuardHome/stats" "github.com/AdguardTeam/golibs/log" ) @@ -48,52 +43,6 @@ var ( ARMVersion = "" ) -// Global context -type homeContext struct { - // Modules - // -- - - clients clientsContainer // per-client-settings module - stats stats.Stats // statistics module - queryLog querylog.QueryLog // query log module - dnsServer *dnsforward.Server // DNS module - rdns *RDNS // rDNS module - whois *Whois // WHOIS module - dnsFilter *dnsfilter.Dnsfilter // DNS filtering module - dhcpServer *dhcpd.Server // DHCP module - auth *Auth // HTTP authentication module - filters Filtering // DNS filtering module - web *Web // Web (HTTP, HTTPS) module - tls *TLSMod // TLS module - autoHosts util.AutoHosts // IP-hostname pairs taken from system configuration (e.g. /etc/hosts) files - updater *update.Updater - - // Runtime properties - // -- - - configFilename string // Config filename (can be overridden via the command line arguments) - workDir string // Location of our directory, used to protect against CWD being somewhere else - firstRun bool // if set to true, don't run any services except HTTP web inteface, and serve only first-run html - pidFileName string // PID file name. Empty if no PID file was created. - disableUpdate bool // If set, don't check for updates - controlLock sync.Mutex - tlsRoots *x509.CertPool // list of root CAs for TLSv1.2 - tlsCiphers []uint16 // list of TLS ciphers to use - transport *http.Transport - client *http.Client - appSignalChannel chan os.Signal // Channel for receiving OS signals by the console app - // runningAsService flag is set to true when options are passed from the service runner - runningAsService bool -} - -// getDataDir returns path to the directory where we store databases and filters -func (c *homeContext) getDataDir() string { - return filepath.Join(c.workDir, dataDir) -} - -// Context - a global context object -var Context homeContext - // Main is the entry point func Main(version string, channel string, armVer string) { // Init update-related global variables @@ -118,8 +67,8 @@ func Main(version string, channel string, armVer string) { Context.tls.Reload() default: - cleanup() - cleanupAlways() + Context.cleanup() + Context.cleanupAlways() os.Exit(0) } } @@ -304,7 +253,7 @@ func run(args options) { } if !Context.firstRun { - err := initDNSServer() + err := Context.initDNSServer() if err != nil { log.Fatalf("%s", err) } @@ -312,7 +261,7 @@ func run(args options) { Context.autoHosts.Start() go func() { - err := startDNSServer() + err := Context.startDNSServer() if err != nil { log.Fatal(err) } @@ -331,16 +280,16 @@ func run(args options) { // StartMods - initialize and start DNS after installation func StartMods() error { - err := initDNSServer() + err := Context.initDNSServer() if err != nil { return err } Context.tls.Start() - err = startDNSServer() + err = Context.startDNSServer() if err != nil { - closeDNSServer() + Context.closeDNSServer() return err } return nil @@ -489,43 +438,6 @@ func configureLogger(args options) { } } -func cleanup() { - log.Info("Stopping AdGuard Home") - - if Context.web != nil { - Context.web.Close() - Context.web = nil - } - if Context.auth != nil { - Context.auth.Close() - Context.auth = nil - } - - err := stopDNSServer() - if err != nil { - log.Error("Couldn't stop DNS server: %s", err) - } - - if Context.dhcpServer != nil { - Context.dhcpServer.Stop() - } - - Context.autoHosts.Close() - - if Context.tls != nil { - Context.tls.Close() - Context.tls = nil - } -} - -// This function is called before application exits -func cleanupAlways() { - if len(Context.pidFileName) != 0 { - _ = os.Remove(Context.pidFileName) - } - log.Info("Stopped") -} - // command-line arguments type options struct { verbose bool // is verbose logging enabled @@ -734,7 +646,7 @@ func customDialContext(ctx context.Context, network, addr string) (net.Conn, err return nil, errorx.DecorateMany(fmt.Sprintf("couldn't dial to %s", addr), dialErrs...) } -func getHTTPProxy(req *http.Request) (*url.URL, error) { +func getHTTPProxy(*http.Request) (*url.URL, error) { if len(config.ProxyURL) == 0 { return nil, nil } diff --git a/home/home_test.go b/home/home_test.go index 0868cb71..828ad335 100644 --- a/home/home_test.go +++ b/home/home_test.go @@ -182,6 +182,7 @@ func TestHome(t *testing.T) { time.Sleep(1 * time.Second) } - cleanup() - cleanupAlways() + ctx := &Context + ctx.cleanup() + ctx.cleanupAlways() } diff --git a/home/tls.go b/home/tls.go index 157cda2a..92c08095 100644 --- a/home/tls.go +++ b/home/tls.go @@ -134,7 +134,7 @@ func (t *TLSMod) Reload() { t.certLastMod = fi.ModTime().UTC() - _ = reconfigureDNSServer() + _ = Context.reconfigureDNSServer() Context.web.TLSConfigChanged(tlsConf) } @@ -277,7 +277,7 @@ func (t *TLSMod) handleTLSConfigure(w http.ResponseWriter, r *http.Request) { t.confLock.Unlock() t.setCertFileTime() onConfigModified() - err = reconfigureDNSServer() + err = Context.reconfigureDNSServer() if err != nil { httpError(w, http.StatusInternalServerError, "%s", err) return diff --git a/home/web.go b/home/web.go index 0d9881c2..60abf2c4 100644 --- a/home/web.go +++ b/home/web.go @@ -144,7 +144,7 @@ func (web *Web) Start() { } err := web.httpServer.ListenAndServe() if err != http.ErrServerClosed { - cleanupAlways() + Context.cleanupAlways() log.Fatal(err) } // We use ErrServerClosed as a sign that we need to rebind on new address, so go back to the start of the loop @@ -202,7 +202,7 @@ func (web *Web) tlsServerLoop() { printHTTPAddresses("https") err := web.httpsServer.server.ListenAndServeTLS("", "") if err != http.ErrServerClosed { - cleanupAlways() + Context.cleanupAlways() log.Fatal(err) } } diff --git a/util/net.go b/util/net.go index e6d4d58e..f35e772c 100644 --- a/util/net.go +++ b/util/net.go @@ -5,6 +5,60 @@ import ( "net" ) +// nolint (gocyclo) +// Return TRUE if IP is within public Internet IP range +func IsPublicIP(ip net.IP) bool { + ip4 := ip.To4() + if ip4 != nil { + switch ip4[0] { + case 0: + return false //software + case 10: + return false //private network + case 127: + return false //loopback + case 169: + if ip4[1] == 254 { + return false //link-local + } + case 172: + if ip4[1] >= 16 && ip4[1] <= 31 { + return false //private network + } + case 192: + if (ip4[1] == 0 && ip4[2] == 0) || //private network + (ip4[1] == 0 && ip4[2] == 2) || //documentation + (ip4[1] == 88 && ip4[2] == 99) || //reserved + (ip4[1] == 168) { //private network + return false + } + case 198: + if (ip4[1] == 18 || ip4[2] == 19) || //private network + (ip4[1] == 51 || ip4[2] == 100) { //documentation + return false + } + case 203: + if ip4[1] == 0 && ip4[2] == 113 { //documentation + return false + } + case 224: + if ip4[1] == 0 && ip4[2] == 0 { //multicast + return false + } + case 255: + if ip4[1] == 255 && ip4[2] == 255 && ip4[3] == 255 { //subnet + return false + } + } + } else { + if ip.IsLoopback() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() { + return false + } + } + + return true +} + // CanBindPort - checks if we can bind to this port or not func CanBindPort(port int) (bool, error) { addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", port))