all: sync with master; upd chlog
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
@@ -141,7 +142,7 @@ func (clients *clientsContainer) handleHostsUpdates() {
|
||||
}
|
||||
}
|
||||
|
||||
// webHandlersRegistered prevents a [clientsContainer] from regisering its web
|
||||
// webHandlersRegistered prevents a [clientsContainer] from registering its web
|
||||
// handlers more than once.
|
||||
//
|
||||
// TODO(a.garipov): Refactor HTTP handler registration logic.
|
||||
@@ -743,11 +744,9 @@ func (clients *clientsContainer) Update(prev, c *Client) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setWHOISInfo sets the WHOIS information for a client.
|
||||
// setWHOISInfo sets the WHOIS information for a client. clients.lock is
|
||||
// expected to be locked.
|
||||
func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
|
||||
clients.lock.Lock()
|
||||
defer clients.lock.Unlock()
|
||||
|
||||
_, ok := clients.findLocked(ip.String())
|
||||
if ok {
|
||||
log.Debug("clients: client for %s is already created, ignore whois info", ip)
|
||||
@@ -774,9 +773,11 @@ func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
|
||||
rc.WHOIS = wi
|
||||
}
|
||||
|
||||
// AddHost adds a new IP-hostname pairing. The priorities of the sources are
|
||||
// addHost adds a new IP-hostname pairing. The priorities of the sources are
|
||||
// taken into account. ok is true if the pairing was added.
|
||||
func (clients *clientsContainer) AddHost(
|
||||
//
|
||||
// TODO(a.garipov): Only used in internal tests. Consider removing.
|
||||
func (clients *clientsContainer) addHost(
|
||||
ip netip.Addr,
|
||||
host string,
|
||||
src clientSource,
|
||||
@@ -787,6 +788,32 @@ func (clients *clientsContainer) AddHost(
|
||||
return clients.addHostLocked(ip, host, src)
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ client.AddressUpdater = (*clientsContainer)(nil)
|
||||
|
||||
// UpdateAddress implements the [client.AddressUpdater] interface for
|
||||
// *clientsContainer
|
||||
func (clients *clientsContainer) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
|
||||
// Common fast path optimization.
|
||||
if host == "" && info == nil {
|
||||
return
|
||||
}
|
||||
|
||||
clients.lock.Lock()
|
||||
defer clients.lock.Unlock()
|
||||
|
||||
if host != "" {
|
||||
ok := clients.addHostLocked(ip, host, ClientSourceRDNS)
|
||||
if !ok {
|
||||
log.Debug("clients: host for client %q already set with higher priority source", ip)
|
||||
}
|
||||
}
|
||||
|
||||
if info != nil {
|
||||
clients.setWHOISInfo(ip, info)
|
||||
}
|
||||
}
|
||||
|
||||
// addHostLocked adds a new IP-hostname pairing. clients.lock is expected to be
|
||||
// locked.
|
||||
func (clients *clientsContainer) addHostLocked(
|
||||
|
||||
@@ -168,13 +168,13 @@ func TestClients(t *testing.T) {
|
||||
|
||||
t.Run("addhost_success", func(t *testing.T) {
|
||||
ip := netip.MustParseAddr("1.1.1.1")
|
||||
ok := clients.AddHost(ip, "host", ClientSourceARP)
|
||||
ok := clients.addHost(ip, "host", ClientSourceARP)
|
||||
assert.True(t, ok)
|
||||
|
||||
ok = clients.AddHost(ip, "host2", ClientSourceARP)
|
||||
ok = clients.addHost(ip, "host2", ClientSourceARP)
|
||||
assert.True(t, ok)
|
||||
|
||||
ok = clients.AddHost(ip, "host3", ClientSourceHostsFile)
|
||||
ok = clients.addHost(ip, "host3", ClientSourceHostsFile)
|
||||
assert.True(t, ok)
|
||||
|
||||
assert.Equal(t, clients.clientSource(ip), ClientSourceHostsFile)
|
||||
@@ -182,18 +182,18 @@ func TestClients(t *testing.T) {
|
||||
|
||||
t.Run("dhcp_replaces_arp", func(t *testing.T) {
|
||||
ip := netip.MustParseAddr("1.2.3.4")
|
||||
ok := clients.AddHost(ip, "from_arp", ClientSourceARP)
|
||||
ok := clients.addHost(ip, "from_arp", ClientSourceARP)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, clients.clientSource(ip), ClientSourceARP)
|
||||
|
||||
ok = clients.AddHost(ip, "from_dhcp", ClientSourceDHCP)
|
||||
ok = clients.addHost(ip, "from_dhcp", ClientSourceDHCP)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, clients.clientSource(ip), ClientSourceDHCP)
|
||||
})
|
||||
|
||||
t.Run("addhost_fail", func(t *testing.T) {
|
||||
ip := netip.MustParseAddr("1.1.1.1")
|
||||
ok := clients.AddHost(ip, "host1", ClientSourceRDNS)
|
||||
ok := clients.addHost(ip, "host1", ClientSourceRDNS)
|
||||
assert.False(t, ok)
|
||||
})
|
||||
}
|
||||
@@ -216,7 +216,7 @@ func TestClientsWHOIS(t *testing.T) {
|
||||
|
||||
t.Run("existing_auto-client", func(t *testing.T) {
|
||||
ip := netip.MustParseAddr("1.1.1.1")
|
||||
ok := clients.AddHost(ip, "host", ClientSourceRDNS)
|
||||
ok := clients.addHost(ip, "host", ClientSourceRDNS)
|
||||
assert.True(t, ok)
|
||||
|
||||
clients.setWHOISInfo(ip, whois)
|
||||
@@ -259,7 +259,7 @@ func TestClientsAddExisting(t *testing.T) {
|
||||
assert.True(t, ok)
|
||||
|
||||
// Now add an auto-client with the same IP.
|
||||
ok = clients.AddHost(ip, "test", ClientSourceRDNS)
|
||||
ok = clients.addHost(ip, "test", ClientSourceRDNS)
|
||||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/google/renameio/maybe"
|
||||
"github.com/google/renameio/v2/maybe"
|
||||
"golang.org/x/exp/slices"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -590,7 +590,13 @@ func (c *configuration) write() (err error) {
|
||||
s.WriteDiskConfig(&c)
|
||||
dns := &config.DNS
|
||||
dns.FilteringConfig = c
|
||||
dns.LocalPTRResolvers, config.Clients.Sources.RDNS, dns.UsePrivateRDNS = s.RDNSSettings()
|
||||
|
||||
dns.LocalPTRResolvers = s.LocalPTRResolvers()
|
||||
|
||||
addrProcConf := s.AddrProcConfig()
|
||||
config.Clients.Sources.RDNS = addrProcConf.UseRDNS
|
||||
config.Clients.Sources.WHOIS = addrProcConf.UseWHOIS
|
||||
dns.UsePrivateRDNS = addrProcConf.UsePrivateRDNS
|
||||
}
|
||||
|
||||
if Context.dhcpServer != nil {
|
||||
|
||||
@@ -176,12 +176,16 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
// ------------------------
|
||||
// registration of handlers
|
||||
// ------------------------
|
||||
func registerControlHandlers() {
|
||||
func registerControlHandlers(web *webAPI) {
|
||||
Context.mux.HandleFunc(
|
||||
"/control/version.json",
|
||||
postInstall(optionalAuth(web.handleVersionJSON)),
|
||||
)
|
||||
httpRegister(http.MethodPost, "/control/update", web.handleUpdate)
|
||||
|
||||
httpRegister(http.MethodGet, "/control/status", handleStatus)
|
||||
httpRegister(http.MethodPost, "/control/i18n/change_language", handleI18nChangeLanguage)
|
||||
httpRegister(http.MethodGet, "/control/i18n/current_language", handleI18nCurrentLanguage)
|
||||
Context.mux.HandleFunc("/control/version.json", postInstall(optionalAuth(handleVersionJSON)))
|
||||
httpRegister(http.MethodPost, "/control/update", handleUpdate)
|
||||
httpRegister(http.MethodGet, "/control/profile", handleGetProfile)
|
||||
httpRegister(http.MethodPut, "/control/profile/update", handlePutProfile)
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@ func (web *webAPI) handleInstallConfigure(w http.ResponseWriter, r *http.Request
|
||||
web.conf.BindHost = req.Web.IP
|
||||
web.conf.BindPort = req.Web.Port
|
||||
|
||||
registerControlHandlers()
|
||||
registerControlHandlers(web)
|
||||
|
||||
aghhttp.OK(w)
|
||||
if f, ok := w.(http.Flusher); ok {
|
||||
|
||||
@@ -29,9 +29,9 @@ type temporaryError interface {
|
||||
// handleVersionJSON is the handler for the POST /control/version.json HTTP API.
|
||||
//
|
||||
// TODO(a.garipov): Find out if this API used with a GET method by anyone.
|
||||
func handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
func (web *webAPI) handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
resp := &versionResponse{}
|
||||
if Context.disableUpdate {
|
||||
if web.conf.disableUpdate {
|
||||
resp.Disabled = true
|
||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||
|
||||
@@ -52,7 +52,7 @@ func handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
err = requestVersionInfo(resp, req.Recheck)
|
||||
err = web.requestVersionInfo(resp, req.Recheck)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
aghhttp.Error(r, w, http.StatusBadGateway, "%s", err)
|
||||
@@ -73,9 +73,10 @@ func handleVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// requestVersionInfo sets the VersionInfo field of resp if it can reach the
|
||||
// update server.
|
||||
func requestVersionInfo(resp *versionResponse, recheck bool) (err error) {
|
||||
func (web *webAPI) requestVersionInfo(resp *versionResponse, recheck bool) (err error) {
|
||||
updater := web.conf.updater
|
||||
for i := 0; i != 3; i++ {
|
||||
resp.VersionInfo, err = Context.updater.VersionInfo(recheck)
|
||||
resp.VersionInfo, err = updater.VersionInfo(recheck)
|
||||
if err != nil {
|
||||
var terr temporaryError
|
||||
if errors.As(err, &terr) && terr.Temporary() {
|
||||
@@ -95,7 +96,7 @@ func requestVersionInfo(resp *versionResponse, recheck bool) (err error) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
vcu := Context.updater.VersionCheckURL()
|
||||
vcu := updater.VersionCheckURL()
|
||||
|
||||
return fmt.Errorf("getting version info from %s: %w", vcu, err)
|
||||
}
|
||||
@@ -104,8 +105,9 @@ func requestVersionInfo(resp *versionResponse, recheck bool) (err error) {
|
||||
}
|
||||
|
||||
// handleUpdate performs an update to the latest available version procedure.
|
||||
func handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
if Context.updater.NewVersion() == "" {
|
||||
func (web *webAPI) handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
updater := web.conf.updater
|
||||
if updater.NewVersion() == "" {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "/update request isn't allowed now")
|
||||
|
||||
return
|
||||
@@ -122,7 +124,7 @@ func handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err = Context.updater.Update(false)
|
||||
err = updater.Update(false)
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusInternalServerError, "%s", err)
|
||||
|
||||
@@ -137,7 +139,7 @@ func handleUpdate(w http.ResponseWriter, r *http.Request) {
|
||||
// The background context is used because the underlying functions wrap it
|
||||
// with timeout and shut down the server, which handles current request. It
|
||||
// also should be done in a separate goroutine for the same reason.
|
||||
go finishUpdate(context.Background(), execPath)
|
||||
go finishUpdate(context.Background(), execPath, web.conf.runningAsService)
|
||||
}
|
||||
|
||||
// versionResponse is the response for /control/version.json endpoint.
|
||||
@@ -178,7 +180,7 @@ func tlsConfUsesPrivilegedPorts(c *tlsConfigSettings) (ok bool) {
|
||||
}
|
||||
|
||||
// finishUpdate completes an update procedure.
|
||||
func finishUpdate(ctx context.Context, execPath string) {
|
||||
func finishUpdate(ctx context.Context, execPath string, runningAsService bool) {
|
||||
var err error
|
||||
|
||||
log.Info("stopping all tasks")
|
||||
@@ -187,7 +189,7 @@ func finishUpdate(ctx context.Context, execPath string) {
|
||||
cleanupAlways()
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
if Context.runningAsService {
|
||||
if runningAsService {
|
||||
// NOTE: We can't restart the service via "kardianos/service"
|
||||
// package, because it kills the process first we can't start a new
|
||||
// instance, because Windows doesn't allow it.
|
||||
|
||||
@@ -13,14 +13,12 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
@@ -135,7 +133,7 @@ func initDNSServer(
|
||||
return fmt.Errorf("preparing set of private subnets: %w", err)
|
||||
}
|
||||
|
||||
p := dnsforward.DNSCreateParams{
|
||||
Context.dnsServer, err = dnsforward.NewServer(dnsforward.DNSCreateParams{
|
||||
DNSFilter: filters,
|
||||
Stats: sts,
|
||||
QueryLog: qlog,
|
||||
@@ -143,9 +141,7 @@ func initDNSServer(
|
||||
Anonymizer: anonymizer,
|
||||
LocalDomain: config.DHCP.LocalDomainName,
|
||||
DHCPServer: dhcpSrv,
|
||||
}
|
||||
|
||||
Context.dnsServer, err = dnsforward.NewServer(p)
|
||||
})
|
||||
if err != nil {
|
||||
closeDNSServer()
|
||||
|
||||
@@ -154,134 +150,23 @@ func initDNSServer(
|
||||
|
||||
Context.clients.dnsServer = Context.dnsServer
|
||||
|
||||
dnsConf, err := generateServerConfig(tlsConf, httpReg)
|
||||
dnsConf, err := newServerConfig(tlsConf, httpReg)
|
||||
if err != nil {
|
||||
closeDNSServer()
|
||||
|
||||
return fmt.Errorf("generateServerConfig: %w", err)
|
||||
return fmt.Errorf("newServerConfig: %w", err)
|
||||
}
|
||||
|
||||
err = Context.dnsServer.Prepare(&dnsConf)
|
||||
err = Context.dnsServer.Prepare(dnsConf)
|
||||
if err != nil {
|
||||
closeDNSServer()
|
||||
|
||||
return fmt.Errorf("dnsServer.Prepare: %w", err)
|
||||
}
|
||||
|
||||
initRDNS()
|
||||
initWHOIS()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
// defaultQueueSize is the size of queue of IPs for rDNS and WHOIS
|
||||
// processing.
|
||||
defaultQueueSize = 255
|
||||
|
||||
// defaultCacheSize is the maximum size of the cache for rDNS and WHOIS
|
||||
// processing. It must be greater than zero.
|
||||
defaultCacheSize = 10_000
|
||||
|
||||
// defaultIPTTL is the Time to Live duration for IP addresses cached by
|
||||
// rDNS and WHOIS.
|
||||
defaultIPTTL = 1 * time.Hour
|
||||
)
|
||||
|
||||
// initRDNS initializes the rDNS.
|
||||
func initRDNS() {
|
||||
Context.rdnsCh = make(chan netip.Addr, defaultQueueSize)
|
||||
|
||||
// TODO(s.chzhen): Add ability to disable it on dns server configuration
|
||||
// update in [dnsforward] package.
|
||||
r := rdns.New(&rdns.Config{
|
||||
Exchanger: Context.dnsServer,
|
||||
CacheSize: defaultCacheSize,
|
||||
CacheTTL: defaultIPTTL,
|
||||
})
|
||||
|
||||
go processRDNS(r)
|
||||
}
|
||||
|
||||
// processRDNS processes reverse DNS lookup queries. It is intended to be used
|
||||
// as a goroutine.
|
||||
func processRDNS(r rdns.Interface) {
|
||||
defer log.OnPanic("rdns")
|
||||
|
||||
for ip := range Context.rdnsCh {
|
||||
ok := Context.dnsServer.ShouldResolveClient(ip)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
host, changed := r.Process(ip)
|
||||
if host == "" || !changed {
|
||||
continue
|
||||
}
|
||||
|
||||
ok = Context.clients.AddHost(ip, host, ClientSourceRDNS)
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug(
|
||||
"dns: can't set rdns info for client %q: already set with higher priority source",
|
||||
ip,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// initWHOIS initializes the WHOIS.
|
||||
//
|
||||
// TODO(s.chzhen): Consider making configurable.
|
||||
func initWHOIS() {
|
||||
const (
|
||||
// defaultTimeout is the timeout for WHOIS requests.
|
||||
defaultTimeout = 5 * time.Second
|
||||
|
||||
// defaultMaxConnReadSize is an upper limit in bytes for reading from
|
||||
// net.Conn.
|
||||
defaultMaxConnReadSize = 64 * 1024
|
||||
|
||||
// defaultMaxRedirects is the maximum redirects count.
|
||||
defaultMaxRedirects = 5
|
||||
|
||||
// defaultMaxInfoLen is the maximum length of whois.Info fields.
|
||||
defaultMaxInfoLen = 250
|
||||
)
|
||||
|
||||
Context.whoisCh = make(chan netip.Addr, defaultQueueSize)
|
||||
|
||||
var w whois.Interface
|
||||
|
||||
if config.Clients.Sources.WHOIS {
|
||||
w = whois.New(&whois.Config{
|
||||
DialContext: customDialContext,
|
||||
ServerAddr: whois.DefaultServer,
|
||||
Port: whois.DefaultPort,
|
||||
Timeout: defaultTimeout,
|
||||
CacheSize: defaultCacheSize,
|
||||
MaxConnReadSize: defaultMaxConnReadSize,
|
||||
MaxRedirects: defaultMaxRedirects,
|
||||
MaxInfoLen: defaultMaxInfoLen,
|
||||
CacheTTL: defaultIPTTL,
|
||||
})
|
||||
} else {
|
||||
w = whois.Empty{}
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer log.OnPanic("whois")
|
||||
|
||||
for ip := range Context.whoisCh {
|
||||
info, changed := w.Process(context.Background(), ip)
|
||||
if info != nil && changed {
|
||||
Context.clients.setWHOISInfo(ip, info)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// parseSubnetSet parses a slice of subnets. If the slice is empty, it returns
|
||||
// a subnet set that matches all locally served networks, see
|
||||
// [netutil.IsLocallyServed].
|
||||
@@ -312,17 +197,6 @@ func isRunning() bool {
|
||||
return Context.dnsServer != nil && Context.dnsServer.IsRunning()
|
||||
}
|
||||
|
||||
func onDNSRequest(pctx *proxy.DNSContext) {
|
||||
ip := netutil.NetAddrToAddrPort(pctx.Addr).Addr()
|
||||
if ip == (netip.Addr{}) {
|
||||
// This would be quite weird if we get here.
|
||||
return
|
||||
}
|
||||
|
||||
Context.rdnsCh <- ip
|
||||
Context.whoisCh <- ip
|
||||
}
|
||||
|
||||
func ipsToTCPAddrs(ips []netip.Addr, port int) (tcpAddrs []*net.TCPAddr) {
|
||||
if ips == nil {
|
||||
return nil
|
||||
@@ -349,23 +223,41 @@ func ipsToUDPAddrs(ips []netip.Addr, port int) (udpAddrs []*net.UDPAddr) {
|
||||
return udpAddrs
|
||||
}
|
||||
|
||||
func generateServerConfig(
|
||||
func newServerConfig(
|
||||
tlsConf *tlsConfigSettings,
|
||||
httpReg aghhttp.RegisterFunc,
|
||||
) (newConf dnsforward.ServerConfig, err error) {
|
||||
) (newConf *dnsforward.ServerConfig, err error) {
|
||||
dnsConf := config.DNS
|
||||
hosts := aghalg.CoalesceSlice(dnsConf.BindHosts, []netip.Addr{netutil.IPv4Localhost()})
|
||||
newConf = dnsforward.ServerConfig{
|
||||
|
||||
newConf = &dnsforward.ServerConfig{
|
||||
UDPListenAddrs: ipsToUDPAddrs(hosts, dnsConf.Port),
|
||||
TCPListenAddrs: ipsToTCPAddrs(hosts, dnsConf.Port),
|
||||
FilteringConfig: dnsConf.FilteringConfig,
|
||||
ConfigModified: onConfigModified,
|
||||
HTTPRegister: httpReg,
|
||||
OnDNSRequest: onDNSRequest,
|
||||
UseDNS64: config.DNS.UseDNS64,
|
||||
DNS64Prefixes: config.DNS.DNS64Prefixes,
|
||||
}
|
||||
|
||||
var initialAddresses []netip.Addr
|
||||
// Context.stats may be nil here if initDNSServer is called from
|
||||
// [cmdlineUpdate].
|
||||
if sts := Context.stats; sts != nil {
|
||||
const initialClientsNum = 100
|
||||
initialAddresses = Context.stats.TopClientsIP(initialClientsNum)
|
||||
}
|
||||
|
||||
// Do not set DialContext, PrivateSubnets, and UsePrivateRDNS, because they
|
||||
// are set by [dnsforward.Server.Prepare].
|
||||
newConf.AddrProcConf = &client.DefaultAddrProcConfig{
|
||||
Exchanger: Context.dnsServer,
|
||||
AddressUpdater: &Context.clients,
|
||||
InitialAddresses: initialAddresses,
|
||||
UseRDNS: config.Clients.Sources.RDNS,
|
||||
UseWHOIS: config.Clients.Sources.WHOIS,
|
||||
}
|
||||
|
||||
if tlsConf.Enabled {
|
||||
newConf.TLSConfig = tlsConf.TLSConfig
|
||||
newConf.TLSConfig.ServerName = tlsConf.ServerName
|
||||
@@ -385,9 +277,9 @@ func generateServerConfig(
|
||||
if tlsConf.PortDNSCrypt != 0 {
|
||||
newConf.DNSCryptConfig, err = newDNSCrypt(hosts, *tlsConf)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's already
|
||||
// wrapped by newDNSCrypt.
|
||||
return dnsforward.ServerConfig{}, err
|
||||
// Don't wrap the error, because it's already wrapped by
|
||||
// newDNSCrypt.
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,7 +293,6 @@ func generateServerConfig(
|
||||
newConf.LocalPTRResolvers = dnsConf.LocalPTRResolvers
|
||||
newConf.UpstreamTimeout = dnsConf.UpstreamTimeout.Duration
|
||||
|
||||
newConf.ResolveClients = config.Clients.Sources.RDNS
|
||||
newConf.UsePrivateRDNS = dnsConf.UsePrivateRDNS
|
||||
newConf.ServeHTTP3 = dnsConf.ServeHTTP3
|
||||
newConf.UseHTTP3Upstreams = dnsConf.UseHTTP3Upstreams
|
||||
@@ -556,27 +447,19 @@ func startDNSServer() error {
|
||||
Context.stats.Start()
|
||||
Context.queryLog.Start()
|
||||
|
||||
const topClientsNumber = 100 // the number of clients to get
|
||||
for _, ip := range Context.stats.TopClientsIP(topClientsNumber) {
|
||||
Context.rdnsCh <- ip
|
||||
Context.whoisCh <- ip
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func reconfigureDNSServer() (err error) {
|
||||
var newConf dnsforward.ServerConfig
|
||||
|
||||
tlsConf := &tlsConfigSettings{}
|
||||
Context.tls.WriteDiskConfig(tlsConf)
|
||||
|
||||
newConf, err = generateServerConfig(tlsConf, httpRegister)
|
||||
newConf, err := newServerConfig(tlsConf, httpRegister)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating forwarding dns server config: %w", err)
|
||||
}
|
||||
|
||||
err = Context.dnsServer.Reconfigure(&newConf)
|
||||
err = Context.dnsServer.Reconfigure(newConf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("starting forwarding dns server: %w", err)
|
||||
}
|
||||
|
||||
@@ -3,14 +3,12 @@ package home
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
@@ -66,40 +64,24 @@ type homeContext struct {
|
||||
// configuration files, for example /etc/hosts.
|
||||
etcHosts *aghnet.HostsContainer
|
||||
|
||||
updater *updater.Updater
|
||||
|
||||
// mux is our custom http.ServeMux.
|
||||
mux *http.ServeMux
|
||||
|
||||
// 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
|
||||
pidFileName string // PID file name. Empty if no PID file was created.
|
||||
controlLock sync.Mutex
|
||||
tlsRoots *x509.CertPool // list of root CAs for TLSv1.2
|
||||
client *http.Client
|
||||
appSignalChannel chan os.Signal // Channel for receiving OS signals by the console app
|
||||
|
||||
// rdnsCh is the channel for receiving IPs for rDNS processing.
|
||||
rdnsCh chan netip.Addr
|
||||
|
||||
// whoisCh is the channel for receiving IPs for WHOIS processing.
|
||||
whoisCh chan netip.Addr
|
||||
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
|
||||
pidFileName string // PID file name. Empty if no PID file was created.
|
||||
controlLock sync.Mutex
|
||||
tlsRoots *x509.CertPool // list of root CAs for TLSv1.2
|
||||
|
||||
// tlsCipherIDs are the ID of the cipher suites that AdGuard Home must use.
|
||||
tlsCipherIDs []uint16
|
||||
|
||||
// disableUpdate, if true, tells AdGuard Home to not check for updates.
|
||||
disableUpdate bool
|
||||
|
||||
// firstRun, if true, tells AdGuard Home to only start the web interface
|
||||
// service, and only serve the first-run APIs.
|
||||
firstRun bool
|
||||
|
||||
// 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
|
||||
@@ -122,11 +104,11 @@ func Main(clientBuildFS fs.FS) {
|
||||
// package flag.
|
||||
opts := loadCmdLineOpts()
|
||||
|
||||
Context.appSignalChannel = make(chan os.Signal)
|
||||
signal.Notify(Context.appSignalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
||||
go func() {
|
||||
for {
|
||||
sig := <-Context.appSignalChannel
|
||||
sig := <-signals
|
||||
log.Info("Received signal %q", sig)
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
@@ -141,7 +123,7 @@ func Main(clientBuildFS fs.FS) {
|
||||
}()
|
||||
|
||||
if opts.serviceControlAction != "" {
|
||||
handleServiceControlAction(opts, clientBuildFS)
|
||||
handleServiceControlAction(opts, clientBuildFS, signals)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -153,74 +135,48 @@ func Main(clientBuildFS fs.FS) {
|
||||
// setupContext initializes [Context] fields. It also reads and upgrades
|
||||
// config file if necessary.
|
||||
func setupContext(opts options) (err error) {
|
||||
setupContextFlags(opts)
|
||||
Context.firstRun = detectFirstRun()
|
||||
|
||||
Context.tlsRoots = aghtls.SystemRootCAs()
|
||||
Context.client = &http.Client{
|
||||
Timeout: time.Minute * 5,
|
||||
Transport: &http.Transport{
|
||||
DialContext: customDialContext,
|
||||
Proxy: getHTTPProxy,
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: Context.tlsRoots,
|
||||
CipherSuites: Context.tlsCipherIDs,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Context.mux = http.NewServeMux()
|
||||
|
||||
if !Context.firstRun {
|
||||
// Do the upgrade if necessary.
|
||||
err = upgradeConfig()
|
||||
if Context.firstRun {
|
||||
log.Info("This is the first time AdGuard Home is launched")
|
||||
checkPermissions()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do the upgrade if necessary.
|
||||
err = upgradeConfig()
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
if err = parseConfig(); err != nil {
|
||||
log.Error("parsing configuration file: %s", err)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if opts.checkConfig {
|
||||
log.Info("configuration file is ok")
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if !opts.noEtcHosts && config.Clients.Sources.HostsFile {
|
||||
err = setupHostsContainer()
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
if err = parseConfig(); err != nil {
|
||||
log.Error("parsing configuration file: %s", err)
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if opts.checkConfig {
|
||||
log.Info("configuration file is ok")
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if !opts.noEtcHosts && config.Clients.Sources.HostsFile {
|
||||
err = setupHostsContainer()
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupContextFlags sets global flags and prints their status to the log.
|
||||
func setupContextFlags(opts options) {
|
||||
Context.firstRun = detectFirstRun()
|
||||
if Context.firstRun {
|
||||
log.Info("This is the first time AdGuard Home is launched")
|
||||
checkPermissions()
|
||||
}
|
||||
|
||||
Context.runningAsService = opts.runningAsService
|
||||
// Don't print the runningAsService flag, since that has already been done
|
||||
// in [run].
|
||||
|
||||
Context.disableUpdate = opts.disableUpdate || version.Channel() == version.ChannelDevelopment
|
||||
if Context.disableUpdate {
|
||||
log.Info("AdGuard Home updates are disabled")
|
||||
}
|
||||
}
|
||||
|
||||
// logIfUnsupported logs a formatted warning if the error is one of the
|
||||
// unsupported errors and returns nil. If err is nil, logIfUnsupported returns
|
||||
// nil. Otherwise, it returns err.
|
||||
@@ -325,7 +281,7 @@ func initContextClients() (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
//lint:ignore SA1019 Migration is not over.
|
||||
//lint:ignore SA1019 Migration is not over.
|
||||
config.DHCP.WorkDir = Context.workDir
|
||||
config.DHCP.DataDir = Context.getDataDir()
|
||||
config.DHCP.HTTPRegister = httpRegister
|
||||
@@ -340,18 +296,6 @@ func initContextClients() (err error) {
|
||||
return fmt.Errorf("initing dhcp: %w", err)
|
||||
}
|
||||
|
||||
Context.updater = updater.NewUpdater(&updater.Config{
|
||||
Client: Context.client,
|
||||
Version: version.Version(),
|
||||
Channel: version.Channel(),
|
||||
GOARCH: runtime.GOARCH,
|
||||
GOOS: runtime.GOOS,
|
||||
GOARM: version.GOARM(),
|
||||
GOMIPS: version.GOMIPS(),
|
||||
WorkDir: Context.workDir,
|
||||
ConfName: config.getConfigFilename(),
|
||||
})
|
||||
|
||||
var arpdb aghnet.ARPDB
|
||||
if config.Clients.Sources.ARP {
|
||||
arpdb = aghnet.NewARPDB()
|
||||
@@ -433,7 +377,7 @@ func setupDNSFilteringConf(conf *filtering.Config) (err error) {
|
||||
conf.Filters = slices.Clone(config.Filters)
|
||||
conf.WhitelistFilters = slices.Clone(config.WhitelistFilters)
|
||||
conf.UserRules = slices.Clone(config.UserRules)
|
||||
conf.HTTPClient = Context.client
|
||||
conf.HTTPClient = httpClient()
|
||||
|
||||
cacheTime := time.Duration(conf.CacheTime) * time.Minute
|
||||
|
||||
@@ -515,7 +459,7 @@ func checkPorts() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func initWeb(opts options, clientBuildFS fs.FS) (web *webAPI, err error) {
|
||||
func initWeb(opts options, clientBuildFS fs.FS, upd *updater.Updater) (web *webAPI, err error) {
|
||||
var clientFS fs.FS
|
||||
if opts.localFrontend {
|
||||
log.Info("warning: using local frontend files")
|
||||
@@ -528,8 +472,16 @@ func initWeb(opts options, clientBuildFS fs.FS) (web *webAPI, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
webConf := webConfig{
|
||||
firstRun: Context.firstRun,
|
||||
disableUpdate := opts.disableUpdate || version.Channel() == version.ChannelDevelopment
|
||||
if disableUpdate {
|
||||
log.Info("AdGuard Home updates are disabled")
|
||||
}
|
||||
|
||||
webConf := &webConfig{
|
||||
updater: upd,
|
||||
|
||||
clientFS: clientFS,
|
||||
|
||||
BindHost: config.HTTPConfig.Address.Addr(),
|
||||
BindPort: int(config.HTTPConfig.Address.Port()),
|
||||
|
||||
@@ -537,12 +489,13 @@ func initWeb(opts options, clientBuildFS fs.FS) (web *webAPI, err error) {
|
||||
ReadHeaderTimeout: readHdrTimeout,
|
||||
WriteTimeout: writeTimeout,
|
||||
|
||||
clientFS: clientFS,
|
||||
|
||||
serveHTTP3: config.DNS.ServeHTTP3,
|
||||
firstRun: Context.firstRun,
|
||||
disableUpdate: disableUpdate,
|
||||
runningAsService: opts.runningAsService,
|
||||
serveHTTP3: config.DNS.ServeHTTP3,
|
||||
}
|
||||
|
||||
web = newWebAPI(&webConf)
|
||||
web = newWebAPI(webConf)
|
||||
if web == nil {
|
||||
return nil, fmt.Errorf("initializing web: %w", err)
|
||||
}
|
||||
@@ -593,9 +546,21 @@ func run(opts options, clientBuildFS fs.FS) {
|
||||
err = setupOpts(opts)
|
||||
fatalOnError(err)
|
||||
|
||||
upd := updater.NewUpdater(&updater.Config{
|
||||
Client: config.DNS.DnsfilterConf.HTTPClient,
|
||||
Version: version.Version(),
|
||||
Channel: version.Channel(),
|
||||
GOARCH: runtime.GOARCH,
|
||||
GOOS: runtime.GOOS,
|
||||
GOARM: version.GOARM(),
|
||||
GOMIPS: version.GOMIPS(),
|
||||
WorkDir: Context.workDir,
|
||||
ConfName: config.getConfigFilename(),
|
||||
})
|
||||
|
||||
// TODO(e.burkov): This could be made earlier, probably as the option's
|
||||
// effect.
|
||||
cmdlineUpdate(opts)
|
||||
cmdlineUpdate(opts, upd)
|
||||
|
||||
if !Context.firstRun {
|
||||
// Save the updated config.
|
||||
@@ -624,7 +589,7 @@ func run(opts options, clientBuildFS fs.FS) {
|
||||
onConfigModified()
|
||||
}
|
||||
|
||||
Context.web, err = initWeb(opts, clientBuildFS)
|
||||
Context.web, err = initWeb(opts, clientBuildFS, upd)
|
||||
fatalOnError(err)
|
||||
|
||||
if !Context.firstRun {
|
||||
@@ -634,10 +599,10 @@ func run(opts options, clientBuildFS fs.FS) {
|
||||
Context.tls.start()
|
||||
|
||||
go func() {
|
||||
sErr := startDNSServer()
|
||||
if sErr != nil {
|
||||
startErr := startDNSServer()
|
||||
if startErr != nil {
|
||||
closeDNSServer()
|
||||
fatalOnError(sErr)
|
||||
fatalOnError(startErr)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -996,62 +961,6 @@ func detectFirstRun() bool {
|
||||
return errors.Is(err, os.ErrNotExist)
|
||||
}
|
||||
|
||||
// Connect to a remote server resolving hostname using our own DNS server.
|
||||
//
|
||||
// TODO(e.burkov): This messy logic should be decomposed and clarified.
|
||||
//
|
||||
// TODO(a.garipov): Support network.
|
||||
func customDialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
|
||||
log.Debug("home: customdial: dialing addr %q for network %s", addr, network)
|
||||
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dialer := &net.Dialer{
|
||||
Timeout: time.Minute * 5,
|
||||
}
|
||||
|
||||
if net.ParseIP(host) != nil || config.DNS.Port == 0 {
|
||||
return dialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
|
||||
addrs, err := Context.dnsServer.Resolve(host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolving %q: %w", host, err)
|
||||
}
|
||||
|
||||
log.Debug("dnsServer.Resolve: %q: %v", host, addrs)
|
||||
|
||||
if len(addrs) == 0 {
|
||||
return nil, fmt.Errorf("couldn't lookup host: %q", host)
|
||||
}
|
||||
|
||||
var dialErrs []error
|
||||
for _, a := range addrs {
|
||||
addr = net.JoinHostPort(a.String(), port)
|
||||
conn, err = dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
dialErrs = append(dialErrs, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
return conn, err
|
||||
}
|
||||
|
||||
return nil, errors.List(fmt.Sprintf("couldn't dial to %s", addr), dialErrs...)
|
||||
}
|
||||
|
||||
func getHTTPProxy(_ *http.Request) (*url.URL, error) {
|
||||
if config.ProxyURL == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return url.Parse(config.ProxyURL)
|
||||
}
|
||||
|
||||
// jsonError is a generic JSON error response.
|
||||
//
|
||||
// TODO(a.garipov): Merge together with the implementations in [dhcpd] and other
|
||||
@@ -1062,7 +971,7 @@ type jsonError struct {
|
||||
}
|
||||
|
||||
// cmdlineUpdate updates current application and exits.
|
||||
func cmdlineUpdate(opts options) {
|
||||
func cmdlineUpdate(opts options, upd *updater.Updater) {
|
||||
if !opts.performUpdate {
|
||||
return
|
||||
}
|
||||
@@ -1077,10 +986,9 @@ func cmdlineUpdate(opts options) {
|
||||
|
||||
log.Info("cmdline update: performing update")
|
||||
|
||||
updater := Context.updater
|
||||
info, err := updater.VersionInfo(true)
|
||||
info, err := upd.VersionInfo(true)
|
||||
if err != nil {
|
||||
vcu := updater.VersionCheckURL()
|
||||
vcu := upd.VersionCheckURL()
|
||||
log.Error("getting version info from %s: %s", vcu, err)
|
||||
|
||||
os.Exit(1)
|
||||
@@ -1092,7 +1000,7 @@ func cmdlineUpdate(opts options) {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
err = updater.Update(Context.firstRun)
|
||||
err = upd.Update(Context.firstRun)
|
||||
fatalOnError(err)
|
||||
|
||||
err = restartService()
|
||||
|
||||
47
internal/home/httpclient.go
Normal file
47
internal/home/httpclient.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// httpClient returns a new HTTP client that uses the AdGuard Home's own DNS
|
||||
// server for resolving hostnames. The resulting client should not be used
|
||||
// until [Context.dnsServer] is initialized.
|
||||
//
|
||||
// TODO(a.garipov, e.burkov): This is rather messy. Refactor.
|
||||
func httpClient() (c *http.Client) {
|
||||
// Do not use Context.dnsServer.DialContext directly in the struct literal
|
||||
// below, since Context.dnsServer may be nil when this function is called.
|
||||
dialContext := func(ctx context.Context, network, addr string) (conn net.Conn, err error) {
|
||||
return Context.dnsServer.DialContext(ctx, network, addr)
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
// TODO(a.garipov): Make configurable.
|
||||
Timeout: time.Minute * 5,
|
||||
Transport: &http.Transport{
|
||||
DialContext: dialContext,
|
||||
Proxy: httpProxy,
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: Context.tlsRoots,
|
||||
CipherSuites: Context.tlsCipherIDs,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// httpProxy returns parses and returns an HTTP proxy URL from the config, if
|
||||
// any.
|
||||
func httpProxy(_ *http.Request) (u *url.URL, err error) {
|
||||
if config.ProxyURL == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return url.Parse(config.ProxyURL)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package home
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"runtime"
|
||||
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// startPprof launches the debug and profiling server on addr.
|
||||
func startPprof(addr string) {
|
||||
runtime.SetBlockProfileRate(1)
|
||||
runtime.SetMutexProfileFraction(1)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
|
||||
// See profileSupportsDelta in src/net/http/pprof/pprof.go.
|
||||
mux.Handle("/debug/pprof/allocs", pprof.Handler("allocs"))
|
||||
mux.Handle("/debug/pprof/block", pprof.Handler("block"))
|
||||
mux.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
|
||||
mux.Handle("/debug/pprof/heap", pprof.Handler("heap"))
|
||||
mux.Handle("/debug/pprof/mutex", pprof.Handler("mutex"))
|
||||
mux.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
|
||||
|
||||
go func() {
|
||||
defer log.OnPanic("pprof server")
|
||||
|
||||
log.Info("pprof: listening on %q", addr)
|
||||
err := http.ListenAndServe(addr, mux)
|
||||
log.Info("pprof server errors: %v", err)
|
||||
}()
|
||||
}
|
||||
@@ -33,9 +33,13 @@ const (
|
||||
// daemon.
|
||||
type program struct {
|
||||
clientBuildFS fs.FS
|
||||
signals chan os.Signal
|
||||
opts options
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ service.Interface = (*program)(nil)
|
||||
|
||||
// Start implements service.Interface interface for *program.
|
||||
func (p *program) Start(_ service.Service) (err error) {
|
||||
// Start should not block. Do the actual work async.
|
||||
@@ -48,14 +52,14 @@ func (p *program) Start(_ service.Service) (err error) {
|
||||
}
|
||||
|
||||
// Stop implements service.Interface interface for *program.
|
||||
func (p *program) Stop(_ service.Service) error {
|
||||
// Stop should not block. Return with a few seconds.
|
||||
if Context.appSignalChannel == nil {
|
||||
os.Exit(0)
|
||||
func (p *program) Stop(_ service.Service) (err error) {
|
||||
select {
|
||||
case p.signals <- syscall.SIGINT:
|
||||
// Go on.
|
||||
default:
|
||||
// Stop should not block.
|
||||
}
|
||||
|
||||
Context.appSignalChannel <- syscall.SIGINT
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -194,7 +198,7 @@ func restartService() (err error) {
|
||||
// - run: This is a special command that is not supposed to be used directly
|
||||
// it is specified when we register a service, and it indicates to the app
|
||||
// that it is being run as a service/daemon.
|
||||
func handleServiceControlAction(opts options, clientBuildFS fs.FS) {
|
||||
func handleServiceControlAction(opts options, clientBuildFS fs.FS, signals chan os.Signal) {
|
||||
// Call chooseSystem explicitly to introduce OpenBSD support for service
|
||||
// package. It's a noop for other GOOS values.
|
||||
chooseSystem()
|
||||
@@ -226,7 +230,11 @@ func handleServiceControlAction(opts options, clientBuildFS fs.FS) {
|
||||
}
|
||||
configureService(svcConfig)
|
||||
|
||||
s, err := service.New(&program{clientBuildFS: clientBuildFS, opts: runOpts}, svcConfig)
|
||||
s, err := service.New(&program{
|
||||
clientBuildFS: clientBuildFS,
|
||||
signals: signals,
|
||||
opts: runOpts,
|
||||
}, svcConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("service: initializing service: %s", err)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/google/renameio/maybe"
|
||||
"github.com/google/renameio/v2/maybe"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -6,16 +6,18 @@ import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/updater"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/pprofutil"
|
||||
"github.com/NYTimes/gziphandler"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
@@ -33,6 +35,8 @@ const (
|
||||
)
|
||||
|
||||
type webConfig struct {
|
||||
updater *updater.Updater
|
||||
|
||||
clientFS fs.FS
|
||||
|
||||
BindHost netip.Addr
|
||||
@@ -52,6 +56,13 @@ type webConfig struct {
|
||||
|
||||
firstRun bool
|
||||
|
||||
// disableUpdate, if true, tells AdGuard Home to not check for updates.
|
||||
disableUpdate bool
|
||||
|
||||
// runningAsService flag is set to true when options are passed from the
|
||||
// service runner.
|
||||
runningAsService bool
|
||||
|
||||
serveHTTP3 bool
|
||||
}
|
||||
|
||||
@@ -102,7 +113,7 @@ func newWebAPI(conf *webConfig) (w *webAPI) {
|
||||
Context.mux.Handle("/install.html", preInstallHandler(clientFS))
|
||||
w.registerInstallHandlers()
|
||||
} else {
|
||||
registerControlHandlers()
|
||||
registerControlHandlers(w)
|
||||
}
|
||||
|
||||
w.httpsServer.cond = sync.NewCond(&w.httpsServer.condLock)
|
||||
@@ -295,8 +306,27 @@ func (web *webAPI) mustStartHTTP3(address string) {
|
||||
|
||||
log.Debug("web: starting http/3 server")
|
||||
err := web.httpsServer.server3.ListenAndServe()
|
||||
if !errors.Is(err, quic.ErrServerClosed) {
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
cleanupAlways()
|
||||
log.Fatalf("web: http3: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// startPprof launches the debug and profiling server on addr.
|
||||
func startPprof(addr string) {
|
||||
runtime.SetBlockProfileRate(1)
|
||||
runtime.SetMutexProfileFraction(1)
|
||||
|
||||
mux := http.NewServeMux()
|
||||
pprofutil.RoutePprof(mux)
|
||||
|
||||
go func() {
|
||||
defer log.OnPanic("pprof server")
|
||||
|
||||
log.Info("pprof: listening on %q", addr)
|
||||
err := http.ListenAndServe(addr, mux)
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Error("pprof: shutting down: %s", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user