2020-05-26 16:24:31 +03:00
package querylog
import (
2020-11-16 19:45:31 +03:00
"bytes"
2020-12-21 17:48:07 +03:00
"encoding/base64"
2024-11-21 20:19:39 +03:00
"log/slog"
2020-12-21 17:48:07 +03:00
"net"
2023-08-23 16:58:24 +03:00
"net/netip"
2020-05-26 16:24:31 +03:00
"testing"
2020-12-21 17:48:07 +03:00
"time"
2020-05-26 16:24:31 +03:00
2021-05-21 16:15:47 +03:00
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
2024-11-21 20:19:39 +03:00
"github.com/AdguardTeam/golibs/logutil/slogutil"
2023-08-23 16:58:24 +03:00
"github.com/AdguardTeam/golibs/netutil"
2024-11-21 20:19:39 +03:00
"github.com/AdguardTeam/golibs/testutil"
2020-12-21 17:48:07 +03:00
"github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns"
2020-05-26 16:24:31 +03:00
"github.com/stretchr/testify/assert"
2021-10-22 11:58:18 +03:00
"github.com/stretchr/testify/require"
2020-05-26 16:24:31 +03:00
)
2024-11-21 20:19:39 +03:00
// Common constants for tests.
const testTimeout = 1 * time . Second
2020-12-21 17:48:07 +03:00
func TestDecodeLogEntry ( t * testing . T ) {
2020-11-16 19:45:31 +03:00
logOutput := & bytes . Buffer { }
2024-11-21 20:19:39 +03:00
l := & queryLog {
logger : slog . New ( slog . NewTextHandler ( logOutput , & slog . HandlerOptions {
Level : slog . LevelDebug ,
ReplaceAttr : slogutil . RemoveTime ,
} ) ) ,
}
2020-11-16 19:45:31 +03:00
2024-11-21 20:19:39 +03:00
ctx := testutil . ContextWithTimeout ( t , testTimeout )
2020-11-16 19:45:31 +03:00
2020-12-21 17:48:07 +03:00
t . Run ( "success" , func ( t * testing . T ) {
const ansStr = ` Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA== `
const data = ` { "IP":"127.0.0.1", ` +
2021-01-27 18:32:13 +03:00
` "CID":"cli42", ` +
2020-12-21 17:48:07 +03:00
` "T":"2020-11-25T18:55:56.519796+03:00", ` +
` "QH":"an.yandex.ru", ` +
` "QT":"A", ` +
` "QC":"IN", ` +
` "CP":"", ` +
2022-03-03 17:52:11 +03:00
` "ECS":"1.2.3.0/24", ` +
2020-12-21 17:48:07 +03:00
` "Answer":" ` + ansStr + ` ", ` +
2021-12-07 17:43:51 +03:00
` "Cached":true, ` +
2021-12-13 18:06:01 +03:00
` "AD":true, ` +
2020-12-21 17:48:07 +03:00
` "Result": { ` +
` "IsFiltered":true, ` +
` "Reason":3, ` +
` "IPList":["127.0.0.2"], ` +
` "Rules":[ { "FilterListID":42,"Text":"||an.yandex.ru","IP":"127.0.0.2"}, ` +
` { "FilterListID":43,"Text":"||an2.yandex.ru","IP":"127.0.0.3"}], ` +
` "CanonName":"example.com", ` +
` "ServiceName":"example.org", ` +
` "DNSRewriteResult": { "RCode":0,"Response": { "1":["127.0.0.2"]}}}, ` +
2021-12-07 17:43:51 +03:00
` "Upstream":"https://some.upstream", ` +
2020-12-21 17:48:07 +03:00
` "Elapsed":837429} `
ans , err := base64 . StdEncoding . DecodeString ( ansStr )
2021-10-22 11:58:18 +03:00
require . NoError ( t , err )
2020-12-21 17:48:07 +03:00
want := & logEntry {
2021-01-20 17:27:53 +03:00
IP : net . IPv4 ( 127 , 0 , 0 , 1 ) ,
2020-12-21 17:48:07 +03:00
Time : time . Date ( 2020 , 11 , 25 , 15 , 55 , 56 , 519796000 , time . UTC ) ,
QHost : "an.yandex.ru" ,
QType : "A" ,
QClass : "IN" ,
2021-01-27 18:32:13 +03:00
ClientID : "cli42" ,
2020-12-21 17:48:07 +03:00
ClientProto : "" ,
2022-03-03 17:52:11 +03:00
ReqECS : "1.2.3.0/24" ,
2020-12-21 17:48:07 +03:00
Answer : ans ,
2021-12-07 17:43:51 +03:00
Cached : true ,
2021-05-21 16:15:47 +03:00
Result : filtering . Result {
2022-09-02 14:52:19 +03:00
DNSRewriteResult : & filtering . DNSRewriteResult {
RCode : dns . RcodeSuccess ,
Response : filtering . DNSRewriteResultResponse {
dns . TypeA : [ ] rules . RRValue { net . IPv4 ( 127 , 0 , 0 , 2 ) } ,
} ,
} ,
CanonName : "example.com" ,
ServiceName : "example.org" ,
2023-08-23 16:58:24 +03:00
IPList : [ ] netip . Addr { netip . AddrFrom4 ( [ 4 ] byte { 127 , 0 , 0 , 2 } ) } ,
2021-05-21 16:15:47 +03:00
Rules : [ ] * filtering . ResultRule { {
2020-12-21 17:48:07 +03:00
FilterListID : 42 ,
Text : "||an.yandex.ru" ,
2023-08-23 16:58:24 +03:00
IP : netip . AddrFrom4 ( [ 4 ] byte { 127 , 0 , 0 , 2 } ) ,
2020-12-21 17:48:07 +03:00
} , {
FilterListID : 43 ,
Text : "||an2.yandex.ru" ,
2023-08-23 16:58:24 +03:00
IP : netip . AddrFrom4 ( [ 4 ] byte { 127 , 0 , 0 , 3 } ) ,
2020-12-21 17:48:07 +03:00
} } ,
2022-09-02 14:52:19 +03:00
Reason : filtering . FilteredBlockList ,
IsFiltered : true ,
2020-12-21 17:48:07 +03:00
} ,
2021-12-13 18:06:01 +03:00
Upstream : "https://some.upstream" ,
Elapsed : 837429 ,
AuthenticatedData : true ,
2020-12-21 17:48:07 +03:00
}
got := & logEntry { }
2024-11-21 20:19:39 +03:00
l . decodeLogEntry ( ctx , got , data )
2020-12-21 17:48:07 +03:00
s := logOutput . String ( )
2021-01-13 16:56:05 +03:00
assert . Empty ( t , s )
2020-12-21 17:48:07 +03:00
// Correct for time zones.
got . Time = got . Time . UTC ( )
assert . Equal ( t , want , got )
} )
2020-11-16 19:45:31 +03:00
testCases := [ ] struct {
name string
log string
want string
} { {
2020-12-21 17:48:07 +03:00
name : "all_right_old_rule" ,
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,"Rule":"||an.yandex.","FilterID":1,"ReverseHosts":["example.com"],"IPList":["127.0.0.1"]},"Elapsed":837429} ` ,
want : "" ,
2020-11-27 12:33:25 +03:00
} , {
2020-12-21 17:48:07 +03:00
name : "bad_filter_id_old_rule" ,
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,"FilterID":1.5},"Elapsed":837429} ` ,
2024-11-21 20:19:39 +03:00
want : ` level=DEBUG msg="decoding result; handler" err="strconv.ParseInt: parsing \"1.5\": invalid syntax" ` ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_is_filtered" ,
2020-12-21 17:48:07 +03:00
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} ` ,
2024-11-21 20:19:39 +03:00
want : ` level=DEBUG msg="decoding log entry; token" err="invalid character 'o' in literal true (expecting 'u')" ` ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_elapsed" ,
2020-12-21 17:48:07 +03:00
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} ` ,
want : "" ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_ip" ,
2020-12-21 17:48:07 +03:00
log : ` { "IP":127001,"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":837429} ` ,
want : "" ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_time" ,
2020-12-21 17:48:07 +03:00
log : ` { "IP":"127.0.0.1","T":"12/09/1998T15:00:00.000000+05:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result": { "IsFiltered":true,"Reason":3},"Elapsed":837429} ` ,
2024-11-21 20:19:39 +03:00
want : ` level=DEBUG msg="decoding log entry; handler" err="parsing time \"12/09/1998T15:00:00.000000+05:00\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"12/09/1998T15:00:00.000000+05:00\" as \"2006\"" ` ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_host" ,
2020-12-21 17:48:07 +03:00
log : ` { "IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":6,"QT":"A","QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result": { "IsFiltered":true,"Reason":3},"Elapsed":837429} ` ,
want : "" ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_type" ,
2020-12-21 17:48:07 +03:00
log : ` { "IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":true,"QC":"IN","CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result": { "IsFiltered":true,"Reason":3},"Elapsed":837429} ` ,
want : "" ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_class" ,
2020-12-21 17:48:07 +03:00
log : ` { "IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":false,"CP":"","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result": { "IsFiltered":true,"Reason":3},"Elapsed":837429} ` ,
want : "" ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_client_proto" ,
2020-12-21 17:48:07 +03:00
log : ` { "IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":8,"Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result": { "IsFiltered":true,"Reason":3},"Elapsed":837429} ` ,
want : "" ,
2020-11-27 12:33:25 +03:00
} , {
name : "very_bad_client_proto" ,
2020-12-21 17:48:07 +03:00
log : ` { "IP":"127.0.0.1","T":"2020-11-25T18:55:56.519796+03:00","QH":"an.yandex.ru","QT":"A","QC":"IN","CP":"dog","Answer":"Qz+BgAABAAEAAAAAAmFuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result": { "IsFiltered":true,"Reason":3},"Elapsed":837429} ` ,
2024-11-21 20:19:39 +03:00
want : ` level=DEBUG msg="decoding log entry; handler" err="invalid client proto: \"dog\"" ` ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_answer" ,
2020-12-21 17:48:07 +03:00
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":0.9,"Result": { "IsFiltered":true,"Reason":3},"Elapsed":837429} ` ,
want : "" ,
2020-11-27 12:33:25 +03:00
} , {
name : "very_bad_answer" ,
2020-12-21 17:48:07 +03:00
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+BgAABAAEAAAAAAmuBnlhbmRleAJydQAAAQABwAwAAQABAAAACgAEAAAAAA==","Result": { "IsFiltered":true,"Reason":3},"Elapsed":837429} ` ,
2024-11-21 20:19:39 +03:00
want : ` level=DEBUG msg="decoding log entry; handler" err="illegal base64 data at input byte 61" ` ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_rule" ,
2020-12-21 17:48:07 +03:00
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,"Rule":false},"Elapsed":837429} ` ,
want : "" ,
2020-11-27 12:33:25 +03:00
} , {
name : "bad_reason" ,
2020-12-21 17:48:07 +03:00
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":true},"Elapsed":837429} ` ,
want : "" ,
} , {
name : "bad_reverse_hosts" ,
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,"ReverseHosts":[ { }]},"Elapsed":837429} ` ,
2024-11-21 20:19:39 +03:00
want : ` level=DEBUG msg="decoding result reverse hosts" err="unexpected delimiter: \" { \"" ` ,
2020-12-21 17:48:07 +03:00
} , {
name : "bad_ip_list" ,
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,"ReverseHosts":["example.net"],"IPList":[ { }]},"Elapsed":837429} ` ,
2024-11-21 20:19:39 +03:00
want : ` level=DEBUG msg="decoding result ip list" err="unexpected delimiter: \" { \"" ` ,
2020-11-16 19:45:31 +03:00
} }
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
2024-11-21 20:19:39 +03:00
l . decodeLogEntry ( ctx , new ( logEntry ) , tc . log )
got := logOutput . String ( )
2020-12-21 17:48:07 +03:00
if tc . want == "" {
2024-11-21 20:19:39 +03:00
assert . Empty ( t , got )
2020-12-21 17:48:07 +03:00
} else {
2024-11-21 20:19:39 +03:00
require . NotEmpty ( t , got )
// Remove newline.
got = got [ : len ( got ) - 1 ]
assert . Equal ( t , tc . want , got )
2020-12-21 17:48:07 +03:00
}
2020-11-16 19:45:31 +03:00
logOutput . Reset ( )
} )
}
}
2021-11-23 18:01:48 +03:00
func TestDecodeLogEntry_backwardCompatability ( t * testing . T ) {
var (
2023-08-23 16:58:24 +03:00
a1 = netutil . IPv4Localhost ( )
a2 = a1 . Next ( )
aaaa1 = netutil . IPv6Localhost ( )
aaaa2 = aaaa1 . Next ( )
2021-11-23 18:01:48 +03:00
)
2024-11-21 20:19:39 +03:00
l := & queryLog {
logger : slogutil . NewDiscardLogger ( ) ,
}
ctx := testutil . ContextWithTimeout ( t , testTimeout )
2021-11-23 18:01:48 +03:00
testCases := [ ] struct {
want * logEntry
entry string
name string
} { {
entry : ` { "Result": { "ReverseHosts":["example.net","example.org"]} ` ,
want : & logEntry {
Result : filtering . Result { DNSRewriteResult : & filtering . DNSRewriteResult {
RCode : dns . RcodeSuccess ,
Response : filtering . DNSRewriteResultResponse {
dns . TypePTR : [ ] rules . RRValue { "example.net." , "example.org." } ,
} ,
} } ,
} ,
name : "reverse_hosts" ,
} , {
entry : ` { "Result": { "IPList":["127.0.0.1","127.0.0.2","::1","::2"],"Reason":10}} ` ,
want : & logEntry {
Result : filtering . Result {
DNSRewriteResult : & filtering . DNSRewriteResult {
RCode : dns . RcodeSuccess ,
Response : filtering . DNSRewriteResultResponse {
dns . TypeA : [ ] rules . RRValue { a1 , a2 } ,
dns . TypeAAAA : [ ] rules . RRValue { aaaa1 , aaaa2 } ,
} ,
} ,
Reason : filtering . RewrittenAutoHosts ,
} ,
} ,
name : "iplist_autohosts" ,
} , {
entry : ` { "Result": { "IPList":["127.0.0.1","127.0.0.2","::1","::2"],"Reason":9}} ` ,
want : & logEntry {
Result : filtering . Result {
2023-08-23 16:58:24 +03:00
IPList : [ ] netip . Addr {
2021-11-23 18:01:48 +03:00
a1 ,
a2 ,
aaaa1 ,
aaaa2 ,
} ,
Reason : filtering . Rewritten ,
} ,
} ,
name : "iplist_rewritten" ,
} }
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
e := & logEntry { }
2024-11-21 20:19:39 +03:00
l . decodeLogEntry ( ctx , e , tc . entry )
2021-11-23 18:01:48 +03:00
assert . Equal ( t , tc . want , e )
} )
}
}
2021-12-06 17:26:43 +03:00
2021-12-07 14:12:59 +03:00
// anonymizeIPSlow masks ip to anonymize the client if the ip is a valid one.
// It only exists in purposes of benchmark comparison, see BenchmarkAnonymizeIP.
func anonymizeIPSlow ( ip net . IP ) {
if ip4 := ip . To4 ( ) ; ip4 != nil {
copy ( ip4 [ net . IPv4len - 2 : net . IPv4len ] , [ ] byte { 0 , 0 } )
} else if len ( ip ) == net . IPv6len {
copy ( ip [ net . IPv6len - 10 : net . IPv6len ] , [ ] byte { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } )
}
}
2021-12-06 17:26:43 +03:00
func BenchmarkAnonymizeIP ( b * testing . B ) {
benchCases := [ ] struct {
name string
ip net . IP
want net . IP
} { {
name : "v4" ,
ip : net . IP { 1 , 2 , 3 , 4 } ,
want : net . IP { 1 , 2 , 0 , 0 } ,
} , {
name : "v4_mapped" ,
ip : net . IP { 1 , 2 , 3 , 4 } . To16 ( ) ,
want : net . IP { 1 , 2 , 0 , 0 } . To16 ( ) ,
} , {
name : "v6" ,
ip : net . IP {
0xa , 0xb , 0x0 , 0x0 ,
0x0 , 0xb , 0xa , 0x9 ,
0x8 , 0x7 , 0x6 , 0x5 ,
0x4 , 0x3 , 0x2 , 0x1 ,
} ,
want : net . IP {
0xa , 0xb , 0x0 , 0x0 ,
0x0 , 0xb , 0x0 , 0x0 ,
0x0 , 0x0 , 0x0 , 0x0 ,
0x0 , 0x0 , 0x0 , 0x0 ,
} ,
} , {
name : "invalid" ,
ip : net . IP { 1 , 2 , 3 } ,
want : net . IP { 1 , 2 , 3 } ,
} }
for _ , bc := range benchCases {
b . Run ( bc . name , func ( b * testing . B ) {
b . ReportAllocs ( )
2024-04-04 15:52:39 +03:00
for range b . N {
2021-12-06 17:26:43 +03:00
AnonymizeIP ( bc . ip )
}
assert . Equal ( b , bc . want , bc . ip )
} )
b . Run ( bc . name + "_slow" , func ( b * testing . B ) {
b . ReportAllocs ( )
2024-04-04 15:52:39 +03:00
for range b . N {
2021-12-06 17:26:43 +03:00
anonymizeIPSlow ( bc . ip )
}
assert . Equal ( b , bc . want , bc . ip )
} )
}
}