badguardhome/internal/querylog/decode.go
Eugene Burkov 434032f8e3 Pull request: 3772 cache upstreams
Merge in DNS/adguard-home from 3772-cache-upstreams to master

Updates #3772.

Squashed commit of the following:

commit 809e874fe8847fdbd7f8a7221f18393509f41339
Merge: 5774798c 2d328ea8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Tue Dec 7 17:15:59 2021 +0300

    Merge branch 'master' into 3772-cache-upstreams

commit 5774798ce1897bf292be49410794ce06cd084f93
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Dec 7 16:48:38 2021 +0300

    client: fix formatting

commit 4b4bb668c762c9b4d47cf40a007f34fe47a6e260
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Dec 7 16:47:18 2021 +0300

    client: remove comment

commit a2a9f904a6ab52194bb811a2a83967660a25f2cc
Author: Ildar Kamalov <ik@adguard.com>
Date:   Tue Dec 7 16:45:31 2021 +0300

    client: handle cached upstream

commit a56804c4910da838cb10c8ef0af2a525e9722b1c
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 26 15:33:43 2021 +0300

    querylog: rm todo

commit 16c0a62ad2dfc7fcb5a6adbc868b296b7840fd27
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Fri Nov 26 15:31:53 2021 +0300

    all: revise log of changes

commit 9f4b793205f17b6e85d74e83126093ed09eb24e3
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 25 17:23:42 2021 +0300

    WIP

commit 7fbc6d596295570d2c61c3e507160f65c9adccb8
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date:   Thu Nov 25 17:03:42 2021 +0300

    all: imp upstream info in querylog
2021-12-07 17:43:51 +03:00

662 lines
12 KiB
Go

package querylog
import (
"encoding/base64"
"encoding/json"
"io"
"net"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
)
type logEntryHandler (func(t json.Token, ent *logEntry) error)
var logEntryHandlers = map[string]logEntryHandler{
"CID": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
ent.ClientID = v
return nil
},
"IP": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
if ent.IP == nil {
ent.IP = net.ParseIP(v)
}
return nil
},
"T": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
var err error
ent.Time, err = time.Parse(time.RFC3339, v)
return err
},
"QH": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
ent.QHost = v
return nil
},
"QT": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
ent.QType = v
return nil
},
"QC": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
ent.QClass = v
return nil
},
"CP": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
var err error
ent.ClientProto, err = NewClientProto(v)
return err
},
"Answer": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
var err error
ent.Answer, err = base64.StdEncoding.DecodeString(v)
return err
},
"OrigAnswer": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
var err error
ent.OrigAnswer, err = base64.StdEncoding.DecodeString(v)
return err
},
"Cached": func(t json.Token, ent *logEntry) error {
v, ok := t.(bool)
if !ok {
return nil
}
ent.Cached = v
return nil
},
"Upstream": func(t json.Token, ent *logEntry) error {
v, ok := t.(string)
if !ok {
return nil
}
ent.Upstream = v
return nil
},
"Elapsed": func(t json.Token, ent *logEntry) error {
v, ok := t.(json.Number)
if !ok {
return nil
}
i, err := v.Int64()
if err != nil {
return err
}
ent.Elapsed = time.Duration(i)
return nil
},
}
var resultHandlers = map[string]logEntryHandler{
"IsFiltered": func(t json.Token, ent *logEntry) error {
v, ok := t.(bool)
if !ok {
return nil
}
ent.Result.IsFiltered = v
return nil
},
"Rule": func(t json.Token, ent *logEntry) error {
s, ok := t.(string)
if !ok {
return nil
}
l := len(ent.Result.Rules)
if l == 0 {
ent.Result.Rules = []*filtering.ResultRule{{}}
l++
}
ent.Result.Rules[l-1].Text = s
return nil
},
"FilterID": func(t json.Token, ent *logEntry) error {
n, ok := t.(json.Number)
if !ok {
return nil
}
i, err := n.Int64()
if err != nil {
return err
}
l := len(ent.Result.Rules)
if l == 0 {
ent.Result.Rules = []*filtering.ResultRule{{}}
l++
}
ent.Result.Rules[l-1].FilterListID = i
return nil
},
"Reason": func(t json.Token, ent *logEntry) error {
v, ok := t.(json.Number)
if !ok {
return nil
}
i, err := v.Int64()
if err != nil {
return err
}
ent.Result.Reason = filtering.Reason(i)
return nil
},
"ServiceName": func(t json.Token, ent *logEntry) error {
s, ok := t.(string)
if !ok {
return nil
}
ent.Result.ServiceName = s
return nil
},
"CanonName": func(t json.Token, ent *logEntry) error {
s, ok := t.(string)
if !ok {
return nil
}
ent.Result.CanonName = s
return nil
},
}
func decodeResultRuleKey(key string, i int, dec *json.Decoder, ent *logEntry) {
switch key {
case "FilterListID":
vToken, err := dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultRuleKey %s err: %s", key, err)
}
return
}
if len(ent.Result.Rules) < i+1 {
ent.Result.Rules = append(ent.Result.Rules, &filtering.ResultRule{})
}
if n, ok := vToken.(json.Number); ok {
ent.Result.Rules[i].FilterListID, _ = n.Int64()
}
case "IP":
vToken, err := dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultRuleKey %s err: %s", key, err)
}
return
}
if len(ent.Result.Rules) < i+1 {
ent.Result.Rules = append(ent.Result.Rules, &filtering.ResultRule{})
}
if ipStr, ok := vToken.(string); ok {
ent.Result.Rules[i].IP = net.ParseIP(ipStr)
}
case "Text":
vToken, err := dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultRuleKey %s err: %s", key, err)
}
return
}
if len(ent.Result.Rules) < i+1 {
ent.Result.Rules = append(ent.Result.Rules, &filtering.ResultRule{})
}
if s, ok := vToken.(string); ok {
ent.Result.Rules[i].Text = s
}
default:
// Go on.
}
}
func decodeResultRules(dec *json.Decoder, ent *logEntry) {
for {
delimToken, err := dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultRules err: %s", err)
}
return
}
if d, ok := delimToken.(json.Delim); ok {
if d != '[' {
log.Debug("decodeResultRules: unexpected delim %q", d)
}
} else {
return
}
i := 0
for {
var keyToken json.Token
keyToken, err = dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultRules err: %s", err)
}
return
}
if d, ok := keyToken.(json.Delim); ok {
switch d {
case '}':
i++
case ']':
return
default:
// Go on.
}
continue
}
key, ok := keyToken.(string)
if !ok {
log.Debug("decodeResultRules: keyToken is %T (%[1]v) and not string", keyToken)
return
}
decodeResultRuleKey(key, i, dec, ent)
}
}
}
// decodeResultReverseHosts parses the dec's tokens into ent interpreting it as
// 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.
func decodeResultReverseHosts(dec *json.Decoder, ent *logEntry) {
for {
itemToken, err := dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultReverseHosts err: %s", err)
}
return
}
switch v := itemToken.(type) {
case json.Delim:
if v == '[' {
continue
} else if v == ']' {
return
}
log.Debug("decodeResultReverseHosts: unexpected delim %q", v)
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)
}
default:
continue
}
}
}
func decodeResultIPList(dec *json.Decoder, ent *logEntry) {
for {
itemToken, err := dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultIPList err: %s", err)
}
return
}
switch v := itemToken.(type) {
case json.Delim:
if v == '[' {
continue
} else if v == ']' {
return
}
log.Debug("decodeResultIPList: unexpected delim %q", v)
return
case string:
ip := net.ParseIP(v)
if ip != nil {
ent.Result.IPList = append(ent.Result.IPList, ip)
}
default:
continue
}
}
}
func decodeResultDNSRewriteResultKey(key string, dec *json.Decoder, ent *logEntry) {
var err error
switch key {
case "RCode":
var vToken json.Token
vToken, err = dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultDNSRewriteResultKey err: %s", err)
}
return
}
if ent.Result.DNSRewriteResult == nil {
ent.Result.DNSRewriteResult = &filtering.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{}
}
// 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
// decoding and correct the values.
err = dec.Decode(&ent.Result.DNSRewriteResult.Response)
if err != nil {
log.Debug("decodeResultDNSRewriteResultKey response err: %s", err)
}
for rrType, rrValues := range ent.Result.DNSRewriteResult.Response {
switch rrType {
case
dns.TypeA,
dns.TypeAAAA:
for i, v := range rrValues {
s, _ := v.(string)
rrValues[i] = net.ParseIP(s)
}
default:
// Go on.
}
}
default:
// Go on.
}
}
func decodeResultDNSRewriteResult(dec *json.Decoder, ent *logEntry) {
for {
keyToken, err := dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResultDNSRewriteResult err: %s", err)
}
return
}
if d, ok := keyToken.(json.Delim); ok {
if d == '}' {
return
}
continue
}
key, ok := keyToken.(string)
if !ok {
log.Debug("decodeResultDNSRewriteResult: keyToken is %T (%[1]v) and not string", keyToken)
return
}
decodeResultDNSRewriteResultKey(key, dec, ent)
}
}
// translateResult converts some fields of the ent.Result to the format
// consistent with current implementation.
func translateResult(ent *logEntry) {
res := &ent.Result
if res.Reason != filtering.RewrittenAutoHosts || len(res.IPList) == 0 {
return
}
if res.DNSRewriteResult == nil {
res.DNSRewriteResult = &filtering.DNSRewriteResult{
RCode: dns.RcodeSuccess,
}
}
if res.DNSRewriteResult.Response == nil {
res.DNSRewriteResult.Response = filtering.DNSRewriteResultResponse{}
}
resp := res.DNSRewriteResult.Response
for _, ip := range res.IPList {
qType := dns.TypeAAAA
if ip.To4() != nil {
qType = dns.TypeA
}
resp[qType] = append(resp[qType], ip)
}
res.IPList = nil
}
func decodeResult(dec *json.Decoder, ent *logEntry) {
defer translateResult(ent)
for {
keyToken, err := dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeResult err: %s", err)
}
return
}
if d, ok := keyToken.(json.Delim); ok {
if d == '}' {
return
}
continue
}
key, ok := keyToken.(string)
if !ok {
log.Debug("decodeResult: keyToken is %T (%[1]v) and not string", keyToken)
return
}
switch key {
case "ReverseHosts":
decodeResultReverseHosts(dec, ent)
continue
case "IPList":
decodeResultIPList(dec, ent)
continue
case "Rules":
decodeResultRules(dec, ent)
continue
case "DNSRewriteResult":
decodeResultDNSRewriteResult(dec, ent)
continue
default:
// Go on.
}
handler, ok := resultHandlers[key]
if !ok {
continue
}
val, err := dec.Token()
if err != nil {
return
}
if err = handler(val, ent); err != nil {
log.Debug("decodeResult handler err: %s", err)
return
}
}
}
func decodeLogEntry(ent *logEntry, str string) {
dec := json.NewDecoder(strings.NewReader(str))
dec.UseNumber()
for {
keyToken, err := dec.Token()
if err != nil {
if err != io.EOF {
log.Debug("decodeLogEntry err: %s", err)
}
return
}
if _, ok := keyToken.(json.Delim); ok {
continue
}
key, ok := keyToken.(string)
if !ok {
log.Debug("decodeLogEntry: keyToken is %T (%[1]v) and not string", keyToken)
return
}
if key == "Result" {
decodeResult(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 {
log.Debug("decodeLogEntry handler err: %s", err)
return
}
}
}