"Fossies" - the Fresh Open Source Software Archive 
Member "AdGuardHome-0.104.3/internal/dnsfilter/dnsfilter_test.go" (19 Nov 2020, 17793 Bytes) of package /linux/misc/dns/AdGuardHome-0.104.3.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the latest
Fossies "Diffs" side-by-side code changes report for "dnsfilter_test.go":
0.104.1_vs_0.104.3.
1 package dnsfilter
2
3 import (
4 "bytes"
5 "fmt"
6 "net"
7 "strings"
8 "testing"
9
10 "github.com/AdguardTeam/AdGuardHome/internal/testutil"
11 "github.com/AdguardTeam/golibs/log"
12 "github.com/AdguardTeam/urlfilter/rules"
13 "github.com/miekg/dns"
14 "github.com/stretchr/testify/assert"
15 )
16
17 func TestMain(m *testing.M) {
18 testutil.DiscardLogOutput(m)
19 }
20
21 var setts RequestFilteringSettings
22
23 // HELPERS
24 // SAFE BROWSING
25 // SAFE SEARCH
26 // PARENTAL
27 // FILTERING
28 // BENCHMARKS
29
30 // HELPERS
31
32 func purgeCaches() {
33 if gctx.safebrowsingCache != nil {
34 gctx.safebrowsingCache.Clear()
35 }
36 if gctx.parentalCache != nil {
37 gctx.parentalCache.Clear()
38 }
39 if gctx.safeSearchCache != nil {
40 gctx.safeSearchCache.Clear()
41 }
42 }
43
44 func NewForTest(c *Config, filters []Filter) *Dnsfilter {
45 setts = RequestFilteringSettings{}
46 setts.FilteringEnabled = true
47 if c != nil {
48 c.SafeBrowsingCacheSize = 10000
49 c.ParentalCacheSize = 10000
50 c.SafeSearchCacheSize = 1000
51 c.CacheTime = 30
52 setts.SafeSearchEnabled = c.SafeSearchEnabled
53 setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled
54 setts.ParentalEnabled = c.ParentalEnabled
55 }
56 d := New(c, filters)
57 purgeCaches()
58 return d
59 }
60
61 func (d *Dnsfilter) checkMatch(t *testing.T, hostname string) {
62 t.Helper()
63 ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
64 if err != nil {
65 t.Errorf("Error while matching host %s: %s", hostname, err)
66 }
67 if !ret.IsFiltered {
68 t.Errorf("Expected hostname %s to match", hostname)
69 }
70 }
71
72 func (d *Dnsfilter) checkMatchIP(t *testing.T, hostname, ip string, qtype uint16) {
73 t.Helper()
74 ret, err := d.CheckHost(hostname, qtype, &setts)
75 if err != nil {
76 t.Errorf("Error while matching host %s: %s", hostname, err)
77 }
78 if !ret.IsFiltered {
79 t.Errorf("Expected hostname %s to match", hostname)
80 }
81 if ret.IP == nil || ret.IP.String() != ip {
82 t.Errorf("Expected ip %s to match, actual: %v", ip, ret.IP)
83 }
84 }
85
86 func (d *Dnsfilter) checkMatchEmpty(t *testing.T, hostname string) {
87 t.Helper()
88 ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
89 if err != nil {
90 t.Errorf("Error while matching host %s: %s", hostname, err)
91 }
92 if ret.IsFiltered {
93 t.Errorf("Expected hostname %s to not match", hostname)
94 }
95 }
96
97 func TestEtcHostsMatching(t *testing.T) {
98 addr := "216.239.38.120"
99 addr6 := "::1"
100 text := fmt.Sprintf(` %s google.com www.google.com # enforce google's safesearch
101 %s ipv6.com
102 0.0.0.0 block.com
103 0.0.0.1 host2
104 0.0.0.2 host2
105 ::1 host2
106 `,
107 addr, addr6)
108 filters := []Filter{{
109 ID: 0, Data: []byte(text),
110 }}
111 d := NewForTest(nil, filters)
112 defer d.Close()
113
114 d.checkMatchIP(t, "google.com", addr, dns.TypeA)
115 d.checkMatchIP(t, "www.google.com", addr, dns.TypeA)
116 d.checkMatchEmpty(t, "subdomain.google.com")
117 d.checkMatchEmpty(t, "example.org")
118
119 // IPv4
120 d.checkMatchIP(t, "block.com", "0.0.0.0", dns.TypeA)
121
122 // ...but empty IPv6
123 ret, err := d.CheckHost("block.com", dns.TypeAAAA, &setts)
124 assert.True(t, err == nil && ret.IsFiltered && ret.IP != nil && len(ret.IP) == 0)
125 assert.True(t, ret.Rule == "0.0.0.0 block.com")
126
127 // IPv6
128 d.checkMatchIP(t, "ipv6.com", addr6, dns.TypeAAAA)
129
130 // ...but empty IPv4
131 ret, err = d.CheckHost("ipv6.com", dns.TypeA, &setts)
132 assert.True(t, err == nil && ret.IsFiltered && ret.IP != nil && len(ret.IP) == 0)
133
134 // 2 IPv4 (return only the first one)
135 ret, err = d.CheckHost("host2", dns.TypeA, &setts)
136 assert.True(t, err == nil && ret.IsFiltered)
137 assert.True(t, ret.IP != nil && ret.IP.Equal(net.ParseIP("0.0.0.1")))
138
139 // ...and 1 IPv6 address
140 ret, err = d.CheckHost("host2", dns.TypeAAAA, &setts)
141 assert.True(t, err == nil && ret.IsFiltered)
142 assert.True(t, ret.IP != nil && ret.IP.Equal(net.ParseIP("::1")))
143 }
144
145 // SAFE BROWSING
146
147 func TestSafeBrowsing(t *testing.T) {
148 logOutput := &bytes.Buffer{}
149 testutil.ReplaceLogWriter(t, logOutput)
150 testutil.ReplaceLogLevel(t, log.DEBUG)
151
152 d := NewForTest(&Config{SafeBrowsingEnabled: true}, nil)
153 defer d.Close()
154 gctx.stats.Safebrowsing.Requests = 0
155 d.checkMatch(t, "wmconvirus.narod.ru")
156
157 assert.True(t, strings.Contains(logOutput.String(), "SafeBrowsing lookup for wmconvirus.narod.ru"))
158
159 d.checkMatch(t, "test.wmconvirus.narod.ru")
160 d.checkMatchEmpty(t, "yandex.ru")
161 d.checkMatchEmpty(t, "pornhub.com")
162
163 // test cached result
164 d.safeBrowsingServer = "127.0.0.1"
165 d.checkMatch(t, "wmconvirus.narod.ru")
166 d.checkMatchEmpty(t, "pornhub.com")
167 d.safeBrowsingServer = defaultSafebrowsingServer
168 }
169
170 func TestParallelSB(t *testing.T) {
171 d := NewForTest(&Config{SafeBrowsingEnabled: true}, nil)
172 defer d.Close()
173 t.Run("group", func(t *testing.T) {
174 for i := 0; i < 100; i++ {
175 t.Run(fmt.Sprintf("aaa%d", i), func(t *testing.T) {
176 t.Parallel()
177 d.checkMatch(t, "wmconvirus.narod.ru")
178 d.checkMatch(t, "test.wmconvirus.narod.ru")
179 d.checkMatchEmpty(t, "yandex.ru")
180 d.checkMatchEmpty(t, "pornhub.com")
181 })
182 }
183 })
184 }
185
186 // SAFE SEARCH
187
188 func TestSafeSearch(t *testing.T) {
189 d := NewForTest(&Config{SafeSearchEnabled: true}, nil)
190 defer d.Close()
191 val, ok := d.SafeSearchDomain("www.google.com")
192 if !ok {
193 t.Errorf("Expected safesearch to find result for www.google.com")
194 }
195 if val != "forcesafesearch.google.com" {
196 t.Errorf("Expected safesearch for google.com to be forcesafesearch.google.com")
197 }
198 }
199
200 func TestCheckHostSafeSearchYandex(t *testing.T) {
201 d := NewForTest(&Config{SafeSearchEnabled: true}, nil)
202 defer d.Close()
203
204 // Slice of yandex domains
205 yandex := []string{"yAndeX.ru", "YANdex.COM", "yandex.ua", "yandex.by", "yandex.kz", "www.yandex.com"}
206
207 // Check host for each domain
208 for _, host := range yandex {
209 result, err := d.CheckHost(host, dns.TypeA, &setts)
210 if err != nil {
211 t.Errorf("SafeSearch doesn't work for yandex domain `%s` cause %s", host, err)
212 }
213
214 if result.IP.String() != "213.180.193.56" {
215 t.Errorf("SafeSearch doesn't work for yandex domain `%s`", host)
216 }
217 }
218 }
219
220 func TestCheckHostSafeSearchGoogle(t *testing.T) {
221 d := NewForTest(&Config{SafeSearchEnabled: true}, nil)
222 defer d.Close()
223
224 // Slice of google domains
225 googleDomains := []string{"www.google.com", "www.google.im", "www.google.co.in", "www.google.iq", "www.google.is", "www.google.it", "www.google.je"}
226
227 // Check host for each domain
228 for _, host := range googleDomains {
229 result, err := d.CheckHost(host, dns.TypeA, &setts)
230 if err != nil {
231 t.Errorf("SafeSearch doesn't work for %s cause %s", host, err)
232 }
233
234 if result.IP == nil {
235 t.Errorf("SafeSearch doesn't work for %s", host)
236 }
237 }
238 }
239
240 func TestSafeSearchCacheYandex(t *testing.T) {
241 d := NewForTest(nil, nil)
242 defer d.Close()
243 domain := "yandex.ru"
244
245 var result Result
246 var err error
247
248 // Check host with disabled safesearch
249 result, err = d.CheckHost(domain, dns.TypeA, &setts)
250 if err != nil {
251 t.Fatalf("Cannot check host due to %s", err)
252 }
253 if result.IP != nil {
254 t.Fatalf("SafeSearch is not enabled but there is an answer for `%s` !", domain)
255 }
256
257 d = NewForTest(&Config{SafeSearchEnabled: true}, nil)
258 defer d.Close()
259
260 result, err = d.CheckHost(domain, dns.TypeA, &setts)
261 if err != nil {
262 t.Fatalf("CheckHost for safesearh domain %s failed cause %s", domain, err)
263 }
264
265 // Fir yandex we already know valid ip
266 if result.IP.String() != "213.180.193.56" {
267 t.Fatalf("Wrong IP for %s safesearch: %s", domain, result.IP.String())
268 }
269
270 // Check cache
271 cachedValue, isFound := getCachedResult(gctx.safeSearchCache, domain)
272
273 if !isFound {
274 t.Fatalf("Safesearch cache doesn't work for %s!", domain)
275 }
276
277 if cachedValue.IP.String() != "213.180.193.56" {
278 t.Fatalf("Wrong IP in cache for %s safesearch: %s", domain, cachedValue.IP.String())
279 }
280 }
281
282 func TestSafeSearchCacheGoogle(t *testing.T) {
283 d := NewForTest(nil, nil)
284 defer d.Close()
285 domain := "www.google.ru"
286 result, err := d.CheckHost(domain, dns.TypeA, &setts)
287 if err != nil {
288 t.Fatalf("Cannot check host due to %s", err)
289 }
290 if result.IP != nil {
291 t.Fatalf("SafeSearch is not enabled but there is an answer!")
292 }
293
294 d = NewForTest(&Config{SafeSearchEnabled: true}, nil)
295 defer d.Close()
296
297 // Let's lookup for safesearch domain
298 safeDomain, ok := d.SafeSearchDomain(domain)
299 if !ok {
300 t.Fatalf("Failed to get safesearch domain for %s", domain)
301 }
302
303 ips, err := net.LookupIP(safeDomain)
304 if err != nil {
305 t.Fatalf("Failed to lookup for %s", safeDomain)
306 }
307
308 ip := ips[0]
309 for _, i := range ips {
310 if i.To4() != nil {
311 ip = i
312 break
313 }
314 }
315
316 result, err = d.CheckHost(domain, dns.TypeA, &setts)
317 if err != nil {
318 t.Fatalf("CheckHost for safesearh domain %s failed cause %s", domain, err)
319 }
320
321 if result.IP.String() != ip.String() {
322 t.Fatalf("Wrong IP for %s safesearch: %s. Should be: %s",
323 domain, result.IP.String(), ip)
324 }
325
326 // Check cache
327 cachedValue, isFound := getCachedResult(gctx.safeSearchCache, domain)
328
329 if !isFound {
330 t.Fatalf("Safesearch cache doesn't work for %s!", domain)
331 }
332
333 if cachedValue.IP.String() != ip.String() {
334 t.Fatalf("Wrong IP in cache for %s safesearch: %s", domain, cachedValue.IP.String())
335 }
336 }
337
338 // PARENTAL
339
340 func TestParentalControl(t *testing.T) {
341 logOutput := &bytes.Buffer{}
342 testutil.ReplaceLogWriter(t, logOutput)
343 testutil.ReplaceLogLevel(t, log.DEBUG)
344
345 d := NewForTest(&Config{ParentalEnabled: true}, nil)
346 defer d.Close()
347 d.checkMatch(t, "pornhub.com")
348 assert.True(t, strings.Contains(logOutput.String(), "Parental lookup for pornhub.com"))
349 d.checkMatch(t, "www.pornhub.com")
350 d.checkMatchEmpty(t, "www.yandex.ru")
351 d.checkMatchEmpty(t, "yandex.ru")
352 d.checkMatchEmpty(t, "api.jquery.com")
353
354 // test cached result
355 d.parentalServer = "127.0.0.1"
356 d.checkMatch(t, "pornhub.com")
357 d.checkMatchEmpty(t, "yandex.ru")
358 d.parentalServer = defaultParentalServer
359 }
360
361 // FILTERING
362
363 const nl = "\n"
364
365 const (
366 blockingRules = `||example.org^` + nl
367 whitelistRules = `||example.org^` + nl + `@@||test.example.org` + nl
368 importantRules = `@@||example.org^` + nl + `||test.example.org^$important` + nl
369 regexRules = `/example\.org/` + nl + `@@||test.example.org^` + nl
370 maskRules = `test*.example.org^` + nl + `exam*.com` + nl
371 )
372
373 var tests = []struct {
374 testname string
375 rules string
376 hostname string
377 isFiltered bool
378 reason Reason
379 }{
380 {"sanity", "||doubleclick.net^", "www.doubleclick.net", true, FilteredBlackList},
381 {"sanity", "||doubleclick.net^", "nodoubleclick.net", false, NotFilteredNotFound},
382 {"sanity", "||doubleclick.net^", "doubleclick.net.ru", false, NotFilteredNotFound},
383 {"sanity", "||doubleclick.net^", "wmconvirus.narod.ru", false, NotFilteredNotFound},
384
385 {"blocking", blockingRules, "example.org", true, FilteredBlackList},
386 {"blocking", blockingRules, "test.example.org", true, FilteredBlackList},
387 {"blocking", blockingRules, "test.test.example.org", true, FilteredBlackList},
388 {"blocking", blockingRules, "testexample.org", false, NotFilteredNotFound},
389 {"blocking", blockingRules, "onemoreexample.org", false, NotFilteredNotFound},
390
391 {"whitelist", whitelistRules, "example.org", true, FilteredBlackList},
392 {"whitelist", whitelistRules, "test.example.org", false, NotFilteredWhiteList},
393 {"whitelist", whitelistRules, "test.test.example.org", false, NotFilteredWhiteList},
394 {"whitelist", whitelistRules, "testexample.org", false, NotFilteredNotFound},
395 {"whitelist", whitelistRules, "onemoreexample.org", false, NotFilteredNotFound},
396
397 {"important", importantRules, "example.org", false, NotFilteredWhiteList},
398 {"important", importantRules, "test.example.org", true, FilteredBlackList},
399 {"important", importantRules, "test.test.example.org", true, FilteredBlackList},
400 {"important", importantRules, "testexample.org", false, NotFilteredNotFound},
401 {"important", importantRules, "onemoreexample.org", false, NotFilteredNotFound},
402
403 {"regex", regexRules, "example.org", true, FilteredBlackList},
404 {"regex", regexRules, "test.example.org", false, NotFilteredWhiteList},
405 {"regex", regexRules, "test.test.example.org", false, NotFilteredWhiteList},
406 {"regex", regexRules, "testexample.org", true, FilteredBlackList},
407 {"regex", regexRules, "onemoreexample.org", true, FilteredBlackList},
408
409 {"mask", maskRules, "test.example.org", true, FilteredBlackList},
410 {"mask", maskRules, "test2.example.org", true, FilteredBlackList},
411 {"mask", maskRules, "example.com", true, FilteredBlackList},
412 {"mask", maskRules, "exampleeee.com", true, FilteredBlackList},
413 {"mask", maskRules, "onemoreexamsite.com", true, FilteredBlackList},
414 {"mask", maskRules, "example.org", false, NotFilteredNotFound},
415 {"mask", maskRules, "testexample.org", false, NotFilteredNotFound},
416 {"mask", maskRules, "example.co.uk", false, NotFilteredNotFound},
417 }
418
419 func TestMatching(t *testing.T) {
420 for _, test := range tests {
421 t.Run(fmt.Sprintf("%s-%s", test.testname, test.hostname), func(t *testing.T) {
422 filters := []Filter{{
423 ID: 0, Data: []byte(test.rules),
424 }}
425 d := NewForTest(nil, filters)
426 defer d.Close()
427
428 ret, err := d.CheckHost(test.hostname, dns.TypeA, &setts)
429 if err != nil {
430 t.Errorf("Error while matching host %s: %s", test.hostname, err)
431 }
432 if ret.IsFiltered != test.isFiltered {
433 t.Errorf("Hostname %s has wrong result (%v must be %v)", test.hostname, ret.IsFiltered, test.isFiltered)
434 }
435 if ret.Reason != test.reason {
436 t.Errorf("Hostname %s has wrong reason (%v must be %v)", test.hostname, ret.Reason.String(), test.reason.String())
437 }
438 })
439 }
440 }
441
442 func TestWhitelist(t *testing.T) {
443 rules := `||host1^
444 ||host2^
445 `
446 filters := []Filter{{
447 ID: 0, Data: []byte(rules),
448 }}
449
450 whiteRules := `||host1^
451 ||host3^
452 `
453 whiteFilters := []Filter{{
454 ID: 0, Data: []byte(whiteRules),
455 }}
456 d := NewForTest(nil, filters)
457 d.SetFilters(filters, whiteFilters, false)
458 defer d.Close()
459
460 // matched by white filter
461 ret, err := d.CheckHost("host1", dns.TypeA, &setts)
462 assert.True(t, err == nil)
463 assert.True(t, !ret.IsFiltered && ret.Reason == NotFilteredWhiteList)
464 assert.True(t, ret.Rule == "||host1^")
465
466 // not matched by white filter, but matched by block filter
467 ret, err = d.CheckHost("host2", dns.TypeA, &setts)
468 assert.True(t, err == nil)
469 assert.True(t, ret.IsFiltered && ret.Reason == FilteredBlackList)
470 assert.True(t, ret.Rule == "||host2^")
471 }
472
473 // CLIENT SETTINGS
474
475 func applyClientSettings(setts *RequestFilteringSettings) {
476 setts.FilteringEnabled = false
477 setts.ParentalEnabled = false
478 setts.SafeBrowsingEnabled = true
479
480 rule, _ := rules.NewNetworkRule("||facebook.com^", 0)
481 s := ServiceEntry{}
482 s.Name = "facebook"
483 s.Rules = []*rules.NetworkRule{rule}
484 setts.ServicesRules = append(setts.ServicesRules, s)
485 }
486
487 // Check behaviour without any per-client settings,
488 // then apply per-client settings and check behaviour once again
489 func TestClientSettings(t *testing.T) {
490 var r Result
491 filters := []Filter{{
492 ID: 0, Data: []byte("||example.org^\n"),
493 }}
494 d := NewForTest(&Config{ParentalEnabled: true, SafeBrowsingEnabled: false}, filters)
495 defer d.Close()
496
497 // no client settings:
498
499 // blocked by filters
500 r, _ = d.CheckHost("example.org", dns.TypeA, &setts)
501 if !r.IsFiltered || r.Reason != FilteredBlackList {
502 t.Fatalf("CheckHost FilteredBlackList")
503 }
504
505 // blocked by parental
506 r, _ = d.CheckHost("pornhub.com", dns.TypeA, &setts)
507 if !r.IsFiltered || r.Reason != FilteredParental {
508 t.Fatalf("CheckHost FilteredParental")
509 }
510
511 // safesearch is disabled
512 r, _ = d.CheckHost("wmconvirus.narod.ru", dns.TypeA, &setts)
513 if r.IsFiltered {
514 t.Fatalf("CheckHost safesearch")
515 }
516
517 // not blocked
518 r, _ = d.CheckHost("facebook.com", dns.TypeA, &setts)
519 assert.True(t, !r.IsFiltered)
520
521 // override client settings:
522 applyClientSettings(&setts)
523
524 // override filtering settings
525 r, _ = d.CheckHost("example.org", dns.TypeA, &setts)
526 if r.IsFiltered {
527 t.Fatalf("CheckHost")
528 }
529
530 // override parental settings (force disable parental)
531 r, _ = d.CheckHost("pornhub.com", dns.TypeA, &setts)
532 if r.IsFiltered {
533 t.Fatalf("CheckHost")
534 }
535
536 // override safesearch settings (force enable safesearch)
537 r, _ = d.CheckHost("wmconvirus.narod.ru", dns.TypeA, &setts)
538 if !r.IsFiltered || r.Reason != FilteredSafeBrowsing {
539 t.Fatalf("CheckHost FilteredSafeBrowsing")
540 }
541
542 // blocked by additional rules
543 r, _ = d.CheckHost("facebook.com", dns.TypeA, &setts)
544 assert.True(t, r.IsFiltered && r.Reason == FilteredBlockedService)
545 }
546
547 // BENCHMARKS
548
549 func BenchmarkSafeBrowsing(b *testing.B) {
550 d := NewForTest(&Config{SafeBrowsingEnabled: true}, nil)
551 defer d.Close()
552 for n := 0; n < b.N; n++ {
553 hostname := "wmconvirus.narod.ru"
554 ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
555 if err != nil {
556 b.Errorf("Error while matching host %s: %s", hostname, err)
557 }
558 if !ret.IsFiltered {
559 b.Errorf("Expected hostname %s to match", hostname)
560 }
561 }
562 }
563
564 func BenchmarkSafeBrowsingParallel(b *testing.B) {
565 d := NewForTest(&Config{SafeBrowsingEnabled: true}, nil)
566 defer d.Close()
567 b.RunParallel(func(pb *testing.PB) {
568 for pb.Next() {
569 hostname := "wmconvirus.narod.ru"
570 ret, err := d.CheckHost(hostname, dns.TypeA, &setts)
571 if err != nil {
572 b.Errorf("Error while matching host %s: %s", hostname, err)
573 }
574 if !ret.IsFiltered {
575 b.Errorf("Expected hostname %s to match", hostname)
576 }
577 }
578 })
579 }
580
581 func BenchmarkSafeSearch(b *testing.B) {
582 d := NewForTest(&Config{SafeSearchEnabled: true}, nil)
583 defer d.Close()
584 for n := 0; n < b.N; n++ {
585 val, ok := d.SafeSearchDomain("www.google.com")
586 if !ok {
587 b.Errorf("Expected safesearch to find result for www.google.com")
588 }
589 if val != "forcesafesearch.google.com" {
590 b.Errorf("Expected safesearch for google.com to be forcesafesearch.google.com")
591 }
592 }
593 }
594
595 func BenchmarkSafeSearchParallel(b *testing.B) {
596 d := NewForTest(&Config{SafeSearchEnabled: true}, nil)
597 defer d.Close()
598 b.RunParallel(func(pb *testing.PB) {
599 for pb.Next() {
600 val, ok := d.SafeSearchDomain("www.google.com")
601 if !ok {
602 b.Errorf("Expected safesearch to find result for www.google.com")
603 }
604 if val != "forcesafesearch.google.com" {
605 b.Errorf("Expected safesearch for google.com to be forcesafesearch.google.com")
606 }
607 }
608 })
609 }