mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2026-06-28 11:51:22 +00:00
Pull request 2638: AGDNS-3945-imp-querylog
Squashed commit of the following:
commit 9ca94b03ee255cf8810c72ffcf967ae348d796fd
Merge: 5516a95d0 44dfffc83
Author: Ainar Garipov <a.garipov@adguard.com>
Date: Fri Apr 24 15:44:56 2026 +0300
Merge branch 'master' into AGDNS-3945-imp-querylog
commit 5516a95d082dbe8acc85efb0833c63f7a7bde220
Author: Ainar Garipov <a.garipov@adguard.com>
Date: Thu Apr 23 17:10:12 2026 +0300
all: imp doc, names
commit 6e8ab1387a0d7e20cffca8dbc99f08a9acb440c1
Author: Ainar Garipov <a.garipov@adguard.com>
Date: Wed Apr 22 21:51:04 2026 +0300
all: imp go.mod, names, errors
commit 20f5e335c1f3c21d7cc6ec6dd57389507627ba3d
Author: Ainar Garipov <a.garipov@adguard.com>
Date: Wed Apr 22 21:02:13 2026 +0300
all: modernize code; imp querylog
This commit is contained in:
parent
44dfffc832
commit
c22183c6f0
15 changed files with 431 additions and 313 deletions
2
go.mod
2
go.mod
|
|
@ -36,7 +36,6 @@ require (
|
|||
// TODO(e.burkov): Update to a stable tag.
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.4.0.20260405193028-802e24f4fbcc
|
||||
golang.org/x/crypto v0.50.0
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
|
||||
golang.org/x/net v0.53.0
|
||||
golang.org/x/sys v0.43.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
|
|
@ -91,6 +90,7 @@ require (
|
|||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20260410095643-746e56fc9e2f // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@
|
|||
package aghalg
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// CoalesceSlice returns the first non-zero value. It is named after function
|
||||
|
|
@ -26,7 +25,7 @@ func CoalesceSlice[E any, S []E](values ...S) (res S) {
|
|||
//
|
||||
// TODO(a.garipov): The Ordered constraint is only really necessary in Validate.
|
||||
// Consider ways of making this constraint comparable instead.
|
||||
type UniqChecker[T constraints.Ordered] map[T]int64
|
||||
type UniqChecker[T cmp.Ordered] map[T]int64
|
||||
|
||||
// Add adds a value to the validator. v must not be nil.
|
||||
func (uc UniqChecker[T]) Add(elems ...T) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
|
|
@ -255,10 +256,8 @@ func InterfaceByIP(ip netip.Addr) (ifaceName string) {
|
|||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
for _, addr := range iface.Addresses {
|
||||
if ip == addr {
|
||||
return iface.Name
|
||||
}
|
||||
if slices.Contains(iface.Addresses, ip) {
|
||||
return iface.Name
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ func addQUICPort(ups string, port int) (withPort string) {
|
|||
|
||||
var doms string
|
||||
withPort = ups
|
||||
if strings.HasPrefix(ups, "[/") {
|
||||
domsAndUps := strings.Split(strings.TrimPrefix(ups, "[/"), "/]")
|
||||
if after, ok := strings.CutPrefix(ups, "[/"); ok {
|
||||
domsAndUps := strings.Split(after, "/]")
|
||||
if len(domsAndUps) != 2 {
|
||||
return ups
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,18 +42,32 @@ func (s *Server) filterDNSRequest(
|
|||
|
||||
// TODO(a.garipov): Make CheckHost return a pointer.
|
||||
res = &resVal
|
||||
switch {
|
||||
case isRewrittenCNAME(res):
|
||||
|
||||
checkReason := true
|
||||
if isRewrittenCNAME(res) {
|
||||
// Resolve the new canonical name, not the original host name. The
|
||||
// original question is readded in processFilteringAfterResponse.
|
||||
dctx.origQuestion = q
|
||||
req.Question[0].Name = dns.Fqdn(res.CanonName)
|
||||
case res.IsFiltered:
|
||||
checkReason = false
|
||||
} else if res.IsFiltered {
|
||||
l.DebugContext(ctx, "host is filtered", "reason", res.Reason)
|
||||
pctx.Res = s.genDNSFilterMessage(ctx, l, pctx, res)
|
||||
case res.Reason.In(filtering.Rewritten, filtering.FilteredSafeSearch):
|
||||
checkReason = false
|
||||
}
|
||||
|
||||
if !checkReason {
|
||||
return res, err
|
||||
}
|
||||
|
||||
switch res.Reason {
|
||||
case
|
||||
filtering.FilteredSafeSearch,
|
||||
filtering.Rewritten:
|
||||
pctx.Res = s.getCNAMEWithIPs(ctx, req, res.IPList, res.CanonName)
|
||||
case res.Reason.In(filtering.RewrittenRule, filtering.RewrittenAutoHosts):
|
||||
case
|
||||
filtering.RewrittenAutoHosts,
|
||||
filtering.RewrittenRule:
|
||||
if err = s.filterDNSRewrite(ctx, req, res, pctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -65,12 +79,15 @@ func (s *Server) filterDNSRequest(
|
|||
// isRewrittenCNAME returns true if the request considered to be rewritten with
|
||||
// CNAME and has no resolved IPs.
|
||||
func isRewrittenCNAME(res *filtering.Result) (ok bool) {
|
||||
return res.Reason.In(
|
||||
switch res.Reason {
|
||||
case
|
||||
filtering.FilteredSafeSearch,
|
||||
filtering.Rewritten,
|
||||
filtering.RewrittenRule,
|
||||
filtering.FilteredSafeSearch) &&
|
||||
res.CanonName != "" &&
|
||||
len(res.IPList) == 0
|
||||
filtering.RewrittenRule:
|
||||
return res.CanonName != "" && len(res.IPList) == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// checkHostRules checks the host against filters. It is safe for concurrent
|
||||
|
|
@ -161,9 +178,12 @@ func removeIPv6Hints(rr *dns.HTTPS) {
|
|||
}
|
||||
|
||||
// filterHTTPSRecords filters HTTPS answers information through all rule list
|
||||
// filters of the server filters. Removes IPv6 hints if IPv6 resolving is
|
||||
// disabled.
|
||||
func (s *Server) filterHTTPSRecords(rr *dns.HTTPS, setts *filtering.Settings) (r *filtering.Result, err error) {
|
||||
// filters of the server filters. It removes IPv6 hints if IPv6 resolving is
|
||||
// disabled. All arguments must not be nil.
|
||||
func (s *Server) filterHTTPSRecords(
|
||||
rr *dns.HTTPS,
|
||||
setts *filtering.Settings,
|
||||
) (r *filtering.Result, err error) {
|
||||
if s.conf.AAAADisabled {
|
||||
removeIPv6Hints(rr)
|
||||
}
|
||||
|
|
|
|||
85
internal/filtering/reason.go
Normal file
85
internal/filtering/reason.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package filtering
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Reason holds an enum detailing why it was filtered, allowed, or rewritten.
|
||||
type Reason uint8
|
||||
|
||||
const (
|
||||
// NotFilteredNotFound: the host was not find in any checks, default value
|
||||
// for results.
|
||||
NotFilteredNotFound Reason = iota
|
||||
|
||||
// NotFilteredAllowList: the host is explicitly allowed.
|
||||
NotFilteredAllowList
|
||||
|
||||
// NotFilteredError is returned when there was an error during checking.
|
||||
// Reserved, currently unused.
|
||||
NotFilteredError
|
||||
|
||||
// FilteredBlockList: the host was matched to be advertising host.
|
||||
FilteredBlockList
|
||||
|
||||
// FilteredSafeBrowsing: the host was matched to be malicious/phishing.
|
||||
FilteredSafeBrowsing
|
||||
|
||||
// FilteredParental: the host was matched to be outside of parental control
|
||||
// settings.
|
||||
FilteredParental
|
||||
|
||||
// FilteredInvalid: the request was invalid and was not processed.
|
||||
FilteredInvalid
|
||||
|
||||
// FilteredSafeSearch: the host was replaced with safesearch variant.
|
||||
FilteredSafeSearch
|
||||
|
||||
// FilteredBlockedService: the host is blocked by the blocked services
|
||||
// feature.
|
||||
FilteredBlockedService
|
||||
|
||||
// Rewritten is returned when there was a rewrite by a legacy DNS rewrite
|
||||
// rule.
|
||||
Rewritten
|
||||
|
||||
// RewrittenAutoHosts is returned when there was a rewrite by /etc/hosts.
|
||||
RewrittenAutoHosts
|
||||
|
||||
// RewrittenRule is returned when a $dnsrewrite filter rule was applied.
|
||||
//
|
||||
// TODO(a.garipov): Remove [Rewritten] and [RewrittenAutoHosts] by merging
|
||||
// their functionality into RewrittenRule.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/2499.
|
||||
RewrittenRule
|
||||
)
|
||||
|
||||
// TODO(a.garipov): Resync with actual code names or replace completely in the
|
||||
// next version of HTTP API.
|
||||
var reasonNames = []string{
|
||||
NotFilteredNotFound: "NotFilteredNotFound",
|
||||
NotFilteredAllowList: "NotFilteredWhiteList",
|
||||
NotFilteredError: "NotFilteredError",
|
||||
|
||||
FilteredBlockList: "FilteredBlackList",
|
||||
FilteredSafeBrowsing: "FilteredSafeBrowsing",
|
||||
FilteredParental: "FilteredParental",
|
||||
FilteredInvalid: "FilteredInvalid",
|
||||
FilteredSafeSearch: "FilteredSafeSearch",
|
||||
FilteredBlockedService: "FilteredBlockedService",
|
||||
|
||||
Rewritten: "Rewrite",
|
||||
RewrittenAutoHosts: "RewriteEtcHosts",
|
||||
RewrittenRule: "RewriteRule",
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ fmt.Stringer = NotFilteredNotFound
|
||||
|
||||
// String implements the [fmt.Stringer] interface for Reason.
|
||||
func (r Reason) String() (s string) {
|
||||
if int(r) >= len(reasonNames) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return reasonNames[r]
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
package filtering
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
|
|
@ -65,88 +63,3 @@ func NewResultRule(r rules.Rule) (rr *ResultRule) {
|
|||
Text: r.Text(),
|
||||
}
|
||||
}
|
||||
|
||||
// Reason holds an enum detailing why it was filtered or not filtered
|
||||
type Reason int
|
||||
|
||||
const (
|
||||
// NotFilteredNotFound: the host was not find in any checks, default value
|
||||
// for results.
|
||||
NotFilteredNotFound Reason = iota
|
||||
|
||||
// NotFilteredAllowList: the host is explicitly allowed.
|
||||
NotFilteredAllowList
|
||||
|
||||
// NotFilteredError is returned when there was an error during checking.
|
||||
// Reserved, currently unused.
|
||||
NotFilteredError
|
||||
|
||||
// FilteredBlockList: the host was matched to be advertising host.
|
||||
FilteredBlockList
|
||||
|
||||
// FilteredSafeBrowsing: the host was matched to be malicious/phishing.
|
||||
FilteredSafeBrowsing
|
||||
|
||||
// FilteredParental: the host was matched to be outside of parental control
|
||||
// settings.
|
||||
FilteredParental
|
||||
|
||||
// FilteredInvalid: the request was invalid and was not processed.
|
||||
FilteredInvalid
|
||||
|
||||
// FilteredSafeSearch: the host was replaced with safesearch variant.
|
||||
FilteredSafeSearch
|
||||
|
||||
// FilteredBlockedService: the host is blocked by the blocked services
|
||||
// feature.
|
||||
FilteredBlockedService
|
||||
|
||||
// Rewritten is returned when there was a rewrite by a legacy DNS rewrite
|
||||
// rule.
|
||||
Rewritten
|
||||
|
||||
// RewrittenAutoHosts is returned when there was a rewrite by /etc/hosts.
|
||||
RewrittenAutoHosts
|
||||
|
||||
// RewrittenRule is returned when a $dnsrewrite filter rule was applied.
|
||||
//
|
||||
// TODO(a.garipov): Remove [Rewritten] and [RewrittenAutoHosts] by merging
|
||||
// their functionality into RewrittenRule.
|
||||
//
|
||||
// See https://github.com/AdguardTeam/AdGuardHome/issues/2499.
|
||||
RewrittenRule
|
||||
)
|
||||
|
||||
// TODO(a.garipov): Resync with actual code names or replace completely in HTTP
|
||||
// API v1.
|
||||
var reasonNames = []string{
|
||||
NotFilteredNotFound: "NotFilteredNotFound",
|
||||
NotFilteredAllowList: "NotFilteredWhiteList",
|
||||
NotFilteredError: "NotFilteredError",
|
||||
|
||||
FilteredBlockList: "FilteredBlackList",
|
||||
FilteredSafeBrowsing: "FilteredSafeBrowsing",
|
||||
FilteredParental: "FilteredParental",
|
||||
FilteredInvalid: "FilteredInvalid",
|
||||
FilteredSafeSearch: "FilteredSafeSearch",
|
||||
FilteredBlockedService: "FilteredBlockedService",
|
||||
|
||||
Rewritten: "Rewrite",
|
||||
RewrittenAutoHosts: "RewriteEtcHosts",
|
||||
RewrittenRule: "RewriteRule",
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ fmt.Stringer = NotFilteredNotFound
|
||||
|
||||
// String implements the [fmt.Stringer] interface for Reason.
|
||||
func (r Reason) String() (s string) {
|
||||
if r < 0 || int(r) >= len(reasonNames) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return reasonNames[r]
|
||||
}
|
||||
|
||||
// In returns true if reasons include r.
|
||||
func (r Reason) In(reasons ...Reason) (ok bool) { return slices.Contains(reasons, r) }
|
||||
|
|
|
|||
|
|
@ -15,12 +15,13 @@ import (
|
|||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// logEntryHandler represents a handler for decoding json token to the logEntry
|
||||
// struct.
|
||||
// struct. ent must not be nil.
|
||||
type logEntryHandler func(t json.Token, ent *logEntry) error
|
||||
|
||||
// logEntryHandlers is the map of log entry decode handlers for various keys.
|
||||
|
|
@ -175,42 +176,47 @@ var logEntryHandlers = map[string]logEntryHandler{
|
|||
}
|
||||
|
||||
// decodeResultRuleKey decodes the token of "Rules" type to logEntry struct.
|
||||
// dec and ent must not be nil.
|
||||
func (l *queryLog) decodeResultRuleKey(
|
||||
ctx context.Context,
|
||||
key string,
|
||||
i int,
|
||||
idx int,
|
||||
dec *json.Decoder,
|
||||
ent *logEntry,
|
||||
) {
|
||||
var vToken json.Token
|
||||
switch key {
|
||||
case "FilterListID":
|
||||
ent.Result.Rules, vToken = l.decodeVTokenAndAddRule(ctx, key, i, dec, ent.Result.Rules)
|
||||
ent.Result.Rules, vToken = l.decodeVTokenAndAddRule(ctx, key, idx, dec, ent.Result.Rules)
|
||||
if n, ok := vToken.(json.Number); ok {
|
||||
id, _ := n.Int64()
|
||||
ent.Result.Rules[i].FilterListID = rulelist.APIID(id)
|
||||
ent.Result.Rules[idx].FilterListID = rulelist.APIID(id)
|
||||
}
|
||||
case "IP":
|
||||
ent.Result.Rules, vToken = l.decodeVTokenAndAddRule(ctx, key, i, dec, ent.Result.Rules)
|
||||
ent.Result.Rules, vToken = l.decodeVTokenAndAddRule(ctx, key, idx, dec, ent.Result.Rules)
|
||||
if ipStr, ok := vToken.(string); ok {
|
||||
if ip, err := netip.ParseAddr(ipStr); err == nil {
|
||||
ent.Result.Rules[i].IP = ip
|
||||
} else {
|
||||
ip, err := netip.ParseAddr(ipStr)
|
||||
if err != nil {
|
||||
l.logger.DebugContext(ctx, "decoding ip", "value", ipStr, slogutil.KeyError, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ent.Result.Rules[idx].IP = ip
|
||||
}
|
||||
case "Text":
|
||||
ent.Result.Rules, vToken = l.decodeVTokenAndAddRule(ctx, key, i, dec, ent.Result.Rules)
|
||||
ent.Result.Rules, vToken = l.decodeVTokenAndAddRule(ctx, key, idx, dec, ent.Result.Rules)
|
||||
if s, ok := vToken.(string); ok {
|
||||
ent.Result.Rules[i].Text = s
|
||||
ent.Result.Rules[idx].Text = s
|
||||
}
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
}
|
||||
|
||||
// decodeVTokenAndAddRule decodes the "Rules" toke as [filtering.ResultRule]
|
||||
// and then adds the decoded object to the slice of result rules.
|
||||
// decodeVTokenAndAddRule decodes the "Rules" toke as [filtering.ResultRule] and
|
||||
// then adds the decoded object to the slice of result rules. dec must not be
|
||||
// nil.
|
||||
func (l *queryLog) decodeVTokenAndAddRule(
|
||||
ctx context.Context,
|
||||
key string,
|
||||
|
|
@ -242,16 +248,19 @@ func (l *queryLog) decodeVTokenAndAddRule(
|
|||
}
|
||||
|
||||
// decodeResultRules parses the dec's tokens into logEntry ent interpreting it
|
||||
// as a slice of the result rules.
|
||||
// as a slice of the result rules. All arguments must not be nil.
|
||||
func (l *queryLog) decodeResultRules(ctx context.Context, dec *json.Decoder, ent *logEntry) {
|
||||
const msgPrefix = "decoding result rules"
|
||||
|
||||
for {
|
||||
delimToken, err := dec.Token()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
// Go on.
|
||||
case io.EOF:
|
||||
return
|
||||
default:
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -267,10 +276,15 @@ func (l *queryLog) decodeResultRules(ctx context.Context, dec *json.Decoder, ent
|
|||
}
|
||||
|
||||
err = l.decodeResultRuleToken(ctx, dec, ent)
|
||||
if err != nil {
|
||||
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; rule token", slogutil.KeyError, err)
|
||||
}
|
||||
switch {
|
||||
case err == nil:
|
||||
continue
|
||||
case
|
||||
err == io.EOF,
|
||||
errors.Is(err, ErrEndOfToken):
|
||||
return
|
||||
default:
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; rule token", slogutil.KeyError, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -278,6 +292,7 @@ func (l *queryLog) decodeResultRules(ctx context.Context, dec *json.Decoder, ent
|
|||
}
|
||||
|
||||
// decodeResultRuleToken decodes the tokens of "Rules" type to the logEntry ent.
|
||||
// All arguments must not be nil.
|
||||
func (l *queryLog) decodeResultRuleToken(
|
||||
ctx context.Context,
|
||||
dec *json.Decoder,
|
||||
|
|
@ -318,16 +333,19 @@ func (l *queryLog) decodeResultRuleToken(
|
|||
// the result of hosts container's $dnsrewrite rule. It assumes there are no
|
||||
// other occurrences of DNSRewriteResult in the entry since hosts container's
|
||||
// rewrites currently has the highest priority along the entire filtering
|
||||
// pipeline.
|
||||
// pipeline. All arguments must not be nil.
|
||||
func (l *queryLog) decodeResultReverseHosts(ctx context.Context, dec *json.Decoder, ent *logEntry) {
|
||||
const msgPrefix = "decoding result reverse hosts"
|
||||
|
||||
for {
|
||||
itemToken, err := dec.Token()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
// Go on.
|
||||
case io.EOF:
|
||||
return
|
||||
default:
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -348,42 +366,52 @@ func (l *queryLog) decodeResultReverseHosts(ctx context.Context, dec *json.Decod
|
|||
|
||||
return
|
||||
case string:
|
||||
v = dns.Fqdn(v)
|
||||
if res := &ent.Result; res.DNSRewriteResult == nil {
|
||||
res.DNSRewriteResult = &filtering.DNSRewriteResult{
|
||||
RCode: dns.RcodeSuccess,
|
||||
Response: filtering.DNSRewriteResultResponse{
|
||||
dns.TypePTR: []rules.RRValue{v},
|
||||
},
|
||||
}
|
||||
|
||||
continue
|
||||
} else {
|
||||
res.DNSRewriteResult.RCode = dns.RcodeSuccess
|
||||
}
|
||||
|
||||
if rres := ent.Result.DNSRewriteResult; rres.Response == nil {
|
||||
rres.Response = filtering.DNSRewriteResultResponse{dns.TypePTR: []rules.RRValue{v}}
|
||||
} else {
|
||||
rres.Response[dns.TypePTR] = append(rres.Response[dns.TypePTR], v)
|
||||
}
|
||||
setPTRRewriteResult(v, ent)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setPTRRewriteResult sets ent.Result.DNSRewriteResult. ent must not be nil.
|
||||
func setPTRRewriteResult(v string, ent *logEntry) {
|
||||
v = dns.Fqdn(v)
|
||||
res := &ent.Result
|
||||
|
||||
if res.DNSRewriteResult == nil {
|
||||
res.DNSRewriteResult = &filtering.DNSRewriteResult{
|
||||
RCode: dns.RcodeSuccess,
|
||||
Response: filtering.DNSRewriteResultResponse{
|
||||
dns.TypePTR: []rules.RRValue{v},
|
||||
},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
res.DNSRewriteResult.RCode = dns.RcodeSuccess
|
||||
|
||||
if rres := ent.Result.DNSRewriteResult; rres.Response == nil {
|
||||
rres.Response = filtering.DNSRewriteResultResponse{dns.TypePTR: []rules.RRValue{v}}
|
||||
} else {
|
||||
rres.Response[dns.TypePTR] = append(rres.Response[dns.TypePTR], v)
|
||||
}
|
||||
}
|
||||
|
||||
// decodeResultIPList parses the dec's tokens into logEntry ent interpreting it
|
||||
// as the result IP addresses list.
|
||||
// as the result IP addresses list. All arguments must not be nil.
|
||||
func (l *queryLog) decodeResultIPList(ctx context.Context, dec *json.Decoder, ent *logEntry) {
|
||||
const msgPrefix = "decoding result ip list"
|
||||
|
||||
for {
|
||||
itemToken, err := dec.Token()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
// Go on.
|
||||
case io.EOF:
|
||||
return
|
||||
default:
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
@ -404,19 +432,27 @@ func (l *queryLog) decodeResultIPList(ctx context.Context, dec *json.Decoder, en
|
|||
|
||||
return
|
||||
case string:
|
||||
var ip netip.Addr
|
||||
ip, err = netip.ParseAddr(v)
|
||||
if err == nil {
|
||||
ent.Result.IPList = append(ent.Result.IPList, ip)
|
||||
}
|
||||
ent.Result.IPList = appendIfValidIP(ent.Result.IPList, v)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// appendIfValidIP appends a valid netip.Addr from s, if there is one, to orig
|
||||
// and returns it.
|
||||
func appendIfValidIP(orig []netip.Addr, s string) (res []netip.Addr) {
|
||||
res = orig
|
||||
|
||||
if !netutil.IsValidIPString(s) {
|
||||
return res
|
||||
}
|
||||
|
||||
return append(res, netip.MustParseAddr(s))
|
||||
}
|
||||
|
||||
// decodeResultDNSRewriteResultKey decodes the token of "DNSRewriteResult" type
|
||||
// to the logEntry struct.
|
||||
// to the logEntry struct. dec and ent must not be nil.
|
||||
func (l *queryLog) decodeResultDNSRewriteResultKey(
|
||||
ctx context.Context,
|
||||
key string,
|
||||
|
|
@ -431,30 +467,26 @@ func (l *queryLog) decodeResultDNSRewriteResultKey(
|
|||
case "RCode":
|
||||
var vToken json.Token
|
||||
vToken, err = dec.Token()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
// Go on.
|
||||
case io.EOF:
|
||||
return
|
||||
default:
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if ent.Result.DNSRewriteResult == nil {
|
||||
ent.Result.DNSRewriteResult = &filtering.DNSRewriteResult{}
|
||||
}
|
||||
ent.Result.DNSRewriteResult = ensureNonNil(ent.Result.DNSRewriteResult)
|
||||
|
||||
if n, ok := vToken.(json.Number); ok {
|
||||
rcode64, _ := n.Int64()
|
||||
ent.Result.DNSRewriteResult.RCode = rules.RCode(rcode64)
|
||||
}
|
||||
case "Response":
|
||||
if ent.Result.DNSRewriteResult == nil {
|
||||
ent.Result.DNSRewriteResult = &filtering.DNSRewriteResult{}
|
||||
}
|
||||
|
||||
if ent.Result.DNSRewriteResult.Response == nil {
|
||||
ent.Result.DNSRewriteResult.Response = filtering.DNSRewriteResultResponse{}
|
||||
}
|
||||
ent.Result.DNSRewriteResult = ensureNonNil(ent.Result.DNSRewriteResult)
|
||||
ent.Result.DNSRewriteResult.Response = ensureNonNilMap(ent.Result.DNSRewriteResult.Response)
|
||||
|
||||
// TODO(a.garipov): I give up. This whole file is a mess. Luckily, we
|
||||
// can assume that this field is relatively rare and just use the normal
|
||||
|
|
@ -470,8 +502,29 @@ func (l *queryLog) decodeResultDNSRewriteResultKey(
|
|||
}
|
||||
}
|
||||
|
||||
// ensureNonNil returns a new non-nil pointer if ptr is nil; otherwise, it
|
||||
// returns ptr.
|
||||
func ensureNonNil[T any](ptr *T) (res *T) {
|
||||
if ptr == nil {
|
||||
return new(T)
|
||||
}
|
||||
|
||||
return ptr
|
||||
}
|
||||
|
||||
// ensureNonNilMap returns a new non-nil map if m is nil; otherwise, it returns
|
||||
// m.
|
||||
func ensureNonNilMap[K comparable, V any](m map[K]V) (res map[K]V) {
|
||||
if m == nil {
|
||||
return map[K]V{}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// decodeResultDNSRewriteResult parses the dec's tokens into logEntry ent
|
||||
// interpreting it as the result DNSRewriteResult.
|
||||
// interpreting it as the result DNSRewriteResult. All arguments must not be
|
||||
// nil.
|
||||
func (l *queryLog) decodeResultDNSRewriteResult(
|
||||
ctx context.Context,
|
||||
dec *json.Decoder,
|
||||
|
|
@ -498,7 +551,7 @@ func (l *queryLog) decodeResultDNSRewriteResult(
|
|||
}
|
||||
|
||||
// translateResult converts some fields of the ent.Result to the format
|
||||
// consistent with current implementation.
|
||||
// consistent with current implementation. ent must not be nil.
|
||||
func translateResult(ent *logEntry) {
|
||||
res := &ent.Result
|
||||
if res.Reason != filtering.RewrittenAutoHosts || len(res.IPList) == 0 {
|
||||
|
|
@ -532,7 +585,7 @@ func translateResult(ent *logEntry) {
|
|||
// bracket is found.
|
||||
const ErrEndOfToken errors.Error = "end of token"
|
||||
|
||||
// parseKeyToken parses the dec's token key.
|
||||
// parseKeyToken parses the dec's token key. dec must not be nil.
|
||||
func parseKeyToken(dec *json.Decoder) (key string, err error) {
|
||||
keyToken, err := dec.Token()
|
||||
if err != nil {
|
||||
|
|
@ -555,49 +608,63 @@ func parseKeyToken(dec *json.Decoder) (key string, err error) {
|
|||
return key, nil
|
||||
}
|
||||
|
||||
// decodeResult decodes a token of "Result" type to logEntry struct.
|
||||
// decodeResult decodes a token of "Result" type to logEntry struct. All
|
||||
// arguments must not be nil.
|
||||
func (l *queryLog) decodeResult(ctx context.Context, dec *json.Decoder, ent *logEntry) {
|
||||
const msgPrefix = "decoding result"
|
||||
|
||||
defer translateResult(ent)
|
||||
|
||||
for {
|
||||
key, err := parseKeyToken(dec)
|
||||
if err != nil {
|
||||
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
ok := l.resultDecHandler(ctx, key, dec, ent)
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
handler, ok := resultHandlers[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := dec.Token()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = handler(val, ent); err != nil {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; handler", slogutil.KeyError, err)
|
||||
|
||||
return
|
||||
}
|
||||
for l.decodeResultKeyValue(ctx, dec, ent) {
|
||||
}
|
||||
}
|
||||
|
||||
// decodeResultKeyValue decodes a single entry key-value pair. If ok is true,
|
||||
// the decoding was successful. All arguments must not be nil.
|
||||
func (l *queryLog) decodeResultKeyValue(
|
||||
ctx context.Context,
|
||||
dec *json.Decoder,
|
||||
ent *logEntry,
|
||||
) (ok bool) {
|
||||
const msgPrefix = "decoding result"
|
||||
|
||||
key, err := parseKeyToken(dec)
|
||||
if err != nil {
|
||||
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
ok = l.resultDecHandler(ctx, key, dec, ent)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
|
||||
handler, ok := resultHandlers[key]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
val, err := dec.Token()
|
||||
if err != nil {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if err = handler(val, ent); err != nil {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; handler", slogutil.KeyError, err)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// resultHandlers is the map of log entry decode handlers for various keys.
|
||||
var resultHandlers = map[string]logEntryHandler{
|
||||
"IsFiltered": func(t json.Token, ent *logEntry) error {
|
||||
|
|
@ -684,7 +751,8 @@ var resultHandlers = map[string]logEntryHandler{
|
|||
},
|
||||
}
|
||||
|
||||
// resultDecHandlers calls a decode handler for key if there is one.
|
||||
// resultDecHandlers calls a decode handler for key if there is one. dec and
|
||||
// ent must not be nil.
|
||||
func (l *queryLog) resultDecHandler(
|
||||
ctx context.Context,
|
||||
name string,
|
||||
|
|
@ -708,59 +776,73 @@ func (l *queryLog) resultDecHandler(
|
|||
return ok
|
||||
}
|
||||
|
||||
// decodeLogEntry decodes string str to logEntry ent.
|
||||
// decodeLogEntry decodes string str to logEntry ent. ent must not be nil.
|
||||
func (l *queryLog) decodeLogEntry(ctx context.Context, ent *logEntry, str string) {
|
||||
const msgPrefix = "decoding log entry"
|
||||
|
||||
dec := json.NewDecoder(strings.NewReader(str))
|
||||
dec.UseNumber()
|
||||
|
||||
for {
|
||||
keyToken, err := dec.Token()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := keyToken.(json.Delim); ok {
|
||||
continue
|
||||
}
|
||||
|
||||
key, ok := keyToken.(string)
|
||||
if !ok {
|
||||
err = fmt.Errorf("%s: keyToken is %T (%[2]v) and not string", msgPrefix, keyToken)
|
||||
l.logger.DebugContext(ctx, msgPrefix, slogutil.KeyError, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if key == "Result" {
|
||||
l.decodeResult(ctx, dec, ent)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
handler, ok := logEntryHandlers[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := dec.Token()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = handler(val, ent); err != nil {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; handler", slogutil.KeyError, err)
|
||||
|
||||
return
|
||||
}
|
||||
for l.decodeLogEntryKeyValue(ctx, dec, ent) {
|
||||
}
|
||||
}
|
||||
|
||||
// decodeLogEntryKeyValue decodes a single entry key-value pair. If ok is true,
|
||||
// the decoding was successful. All arguments must not be nil.
|
||||
func (l *queryLog) decodeLogEntryKeyValue(
|
||||
ctx context.Context,
|
||||
dec *json.Decoder,
|
||||
ent *logEntry,
|
||||
) (ok bool) {
|
||||
const msgPrefix = "decoding log entry"
|
||||
|
||||
keyToken, err := dec.Token()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok = keyToken.(json.Delim)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
|
||||
key, ok := keyToken.(string)
|
||||
if !ok {
|
||||
err = fmt.Errorf("%s: keyToken is %T (%[2]v) and not string", msgPrefix, keyToken)
|
||||
l.logger.DebugContext(ctx, msgPrefix, slogutil.KeyError, err)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if key == "Result" {
|
||||
l.decodeResult(ctx, dec, ent)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
handler, ok := logEntryHandlers[key]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
val, err := dec.Token()
|
||||
if err != nil {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; token", slogutil.KeyError, err)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if err = handler(val, ent); err != nil {
|
||||
l.logger.DebugContext(ctx, msgPrefix+"; handler", slogutil.KeyError, err)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// newUnexpectedDelimiterError is a helper for creating informative errors.
|
||||
func newUnexpectedDelimiterError(d json.Delim) (err error) {
|
||||
return fmt.Errorf("unexpected delimiter: %q", d)
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ func TestQueryLog_DecodeLogEntry_success(t *testing.T) {
|
|||
assert.Equal(t, want, got)
|
||||
}
|
||||
|
||||
// TODO(a.garipov): Reformat.
|
||||
func TestQueryLog_DecodeLogEntry(t *testing.T) {
|
||||
logOutput := &bytes.Buffer{}
|
||||
l := &queryLog{
|
||||
|
|
@ -137,7 +138,8 @@ func TestQueryLog_DecodeLogEntry(t *testing.T) {
|
|||
}, {
|
||||
name: "bad_is_filtered",
|
||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":trooe,"Reason":3},"Elapsed":837429}`,
|
||||
want: `level=DEBUG msg="decoding log entry; token" err="invalid character 'o' in literal true (expecting 'u')"`,
|
||||
want: `level=DEBUG msg="decoding result; token" err="invalid character 'o' in literal true (expecting 'u')"` + "\n" +
|
||||
`level=DEBUG msg="decoding log entry; token" err="invalid character 'o' in literal true (expecting 'u')"`,
|
||||
}, {
|
||||
name: "bad_elapsed",
|
||||
log: `{"IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result":{"IsFiltered":true,"Reason":3},"Elapsed":-1}`,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -376,7 +375,7 @@ func (l *queryLog) parseSearchCriterion(
|
|||
asciiVal = ""
|
||||
}
|
||||
case ctFilteringStatus:
|
||||
if !slices.Contains(filteringStatusValues, val) {
|
||||
if !filteringStatusValues.Has(val) {
|
||||
return false, sc, fmt.Errorf("invalid value %s", val)
|
||||
}
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -280,10 +280,7 @@ func (q *qLogFile) SeekStart() (int64, error) {
|
|||
}
|
||||
|
||||
// Place the position to the very end of file.
|
||||
q.position = fileInfo.Size() - 1
|
||||
if q.position < 0 {
|
||||
q.position = 0
|
||||
}
|
||||
q.position = max(fileInfo.Size()-1, 0)
|
||||
|
||||
return q.position, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/golibs/container"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
|
|
@ -37,22 +39,27 @@ const (
|
|||
filteringStatusProcessed = "processed" // not blocked, not white-listed entries
|
||||
)
|
||||
|
||||
// filteringStatusValues -- array with all possible filteringStatus values
|
||||
var filteringStatusValues = []string{
|
||||
filteringStatusAll, filteringStatusFiltered, filteringStatusBlocked,
|
||||
filteringStatusBlockedService, filteringStatusBlockedSafebrowsing, filteringStatusBlockedParental,
|
||||
filteringStatusWhitelisted, filteringStatusRewritten, filteringStatusSafeSearch,
|
||||
// filteringStatusValues is the set of all possible [filteringStatus] values.
|
||||
var filteringStatusValues = container.NewMapSet(
|
||||
filteringStatusAll,
|
||||
filteringStatusBlocked,
|
||||
filteringStatusBlockedParental,
|
||||
filteringStatusBlockedSafebrowsing,
|
||||
filteringStatusBlockedService,
|
||||
filteringStatusFiltered,
|
||||
filteringStatusProcessed,
|
||||
}
|
||||
filteringStatusRewritten,
|
||||
filteringStatusSafeSearch,
|
||||
filteringStatusWhitelisted,
|
||||
)
|
||||
|
||||
// searchCriterion is a search criterion that is used to match a record.
|
||||
type searchCriterion struct {
|
||||
value string
|
||||
asciiVal string
|
||||
criterionType criterionType
|
||||
// strict, if true, means that the criterion must be applied to the
|
||||
// whole value rather than the part of it. That is, equality and not
|
||||
// containment.
|
||||
// strict, if true, means that the criterion must be applied to the whole
|
||||
// value rather than the part of it. That is, equality and not containment.
|
||||
strict bool
|
||||
}
|
||||
|
||||
|
|
@ -158,12 +165,7 @@ func (c *searchCriterion) ctFilteringStatusCase(
|
|||
case filteringStatusAll:
|
||||
return true
|
||||
case filteringStatusFiltered:
|
||||
return isFiltered || reason.In(
|
||||
filtering.NotFilteredAllowList,
|
||||
filtering.Rewritten,
|
||||
filtering.RewrittenAutoHosts,
|
||||
filtering.RewrittenRule,
|
||||
)
|
||||
return isFiltered || reason == filtering.NotFilteredAllowList || reasonIsRewrite(reason)
|
||||
case
|
||||
filteringStatusBlocked,
|
||||
filteringStatusBlockedParental,
|
||||
|
|
@ -174,34 +176,44 @@ func (c *searchCriterion) ctFilteringStatusCase(
|
|||
case filteringStatusWhitelisted:
|
||||
return reason == filtering.NotFilteredAllowList
|
||||
case filteringStatusRewritten:
|
||||
return reason.In(
|
||||
filtering.Rewritten,
|
||||
filtering.RewrittenAutoHosts,
|
||||
filtering.RewrittenRule,
|
||||
)
|
||||
return reasonIsRewrite(reason)
|
||||
case filteringStatusProcessed:
|
||||
return !reason.In(
|
||||
filtering.FilteredBlockList,
|
||||
filtering.FilteredBlockedService,
|
||||
filtering.NotFilteredAllowList,
|
||||
)
|
||||
return !reasonIsRuleList(reason)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// reasonIsRewrite returns true if r is one of:
|
||||
//
|
||||
// - [filtering.RewrittenAutoHosts]
|
||||
// - [filtering.RewrittenRule]
|
||||
// - [filtering.Rewritten]
|
||||
func reasonIsRewrite(r filtering.Reason) (ok bool) {
|
||||
return r == filtering.RewrittenAutoHosts ||
|
||||
r == filtering.RewrittenRule ||
|
||||
r == filtering.Rewritten
|
||||
}
|
||||
|
||||
// isFilteredWithReason returns true if reason matches the criterion value.
|
||||
// c.value must be one of:
|
||||
//
|
||||
// - filteringStatusBlocked
|
||||
// - filteringStatusBlockedParental
|
||||
// - filteringStatusBlockedSafebrowsing
|
||||
// - filteringStatusBlockedService
|
||||
// - filteringStatusSafeSearch
|
||||
// - [filteringStatusBlockedParental]
|
||||
// - [filteringStatusBlockedSafebrowsing]
|
||||
// - [filteringStatusBlockedService]
|
||||
// - [filteringStatusBlocked]
|
||||
// - [filteringStatusSafeSearch]
|
||||
func (c *searchCriterion) isFilteredWithReason(reason filtering.Reason) (matched bool) {
|
||||
switch c.value {
|
||||
case filteringStatusBlocked:
|
||||
return reason.In(filtering.FilteredBlockList, filtering.FilteredBlockedService)
|
||||
switch reason {
|
||||
case
|
||||
filtering.FilteredBlockList,
|
||||
filtering.FilteredBlockedService:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case filteringStatusBlockedParental:
|
||||
return reason == filtering.FilteredParental
|
||||
case filteringStatusBlockedSafebrowsing:
|
||||
|
|
@ -211,6 +223,17 @@ func (c *searchCriterion) isFilteredWithReason(reason filtering.Reason) (matched
|
|||
case filteringStatusSafeSearch:
|
||||
return reason == filtering.FilteredSafeSearch
|
||||
default:
|
||||
panic(fmt.Errorf("unexpected value %q", c.value))
|
||||
panic(fmt.Errorf("%w: %q", errors.ErrBadEnumValue, c.value))
|
||||
}
|
||||
}
|
||||
|
||||
// reasonIsRuleList returns true if r is one of:
|
||||
//
|
||||
// - [filtering.FilteredBlockList]
|
||||
// - [filtering.FilteredBlockedService]
|
||||
// - [filtering.NotFilteredAllowList]
|
||||
func reasonIsRuleList(r filtering.Reason) (ok bool) {
|
||||
return r == filtering.FilteredBlockList ||
|
||||
r == filtering.FilteredBlockedService ||
|
||||
r == filtering.NotFilteredAllowList
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ func TestLargeNumbers(t *testing.T) {
|
|||
|
||||
req := httptest.NewRequest(http.MethodGet, "/control/stats", nil)
|
||||
|
||||
for h := 0; h < hoursNum; h++ {
|
||||
for h := range hoursNum {
|
||||
atomic.AddUint32(&curHour, 1)
|
||||
|
||||
for i := range cliNumPerHour {
|
||||
|
|
|
|||
|
|
@ -159,8 +159,8 @@ func whoisParse(data []byte, maxLen int) (info map[string]string) {
|
|||
info = map[string]string{}
|
||||
|
||||
var orgname string
|
||||
lines := bytes.Split(data, []byte("\n"))
|
||||
for _, l := range lines {
|
||||
// TODO(a.garipov): Consider using [bytes.Lines].
|
||||
for l := range bytes.SplitSeq(data, []byte("\n")) {
|
||||
if isWHOISComment(l) {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,16 +186,13 @@ fi
|
|||
run_linter "$go" tool gocyclo --over 10 ./internal/ ./scripts/
|
||||
|
||||
# TODO(a.garipov): Enable 10 for all.
|
||||
run_linter "$go" tool gocognit --over='20' \
|
||||
./internal/querylog/ \
|
||||
;
|
||||
|
||||
run_linter "$go" tool gocognit --over='14' \
|
||||
./internal/dhcpd \
|
||||
;
|
||||
|
||||
run_linter "$go" tool gocognit --over='10' \
|
||||
./internal/aghalg/ \
|
||||
./internal/agh/ \
|
||||
./internal/aghhttp/ \
|
||||
./internal/aghnet/ \
|
||||
./internal/aghos/ \
|
||||
|
|
@ -213,6 +210,8 @@ run_linter "$go" tool gocognit --over='10' \
|
|||
./internal/ipset \
|
||||
./internal/next/ \
|
||||
./internal/ossvc/ \
|
||||
./internal/permcheck/ \
|
||||
./internal/querylog/ \
|
||||
./internal/rdns/ \
|
||||
./internal/schedule/ \
|
||||
./internal/stats/ \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue