Skip to content

Commit 0461d9a

Browse files
committed
resolve conflicts
2 parents 55fa8c2 + d1c9454 commit 0461d9a

18 files changed

+646
-344
lines changed

checks/block_lists.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package checks
2+
3+
import (
4+
"context"
5+
"net"
6+
"slices"
7+
"sort"
8+
"sync"
9+
"time"
10+
11+
"github.com/xray-web/web-check-api/checks/clients/ip"
12+
)
13+
14+
type dnsServer struct {
15+
Name string
16+
IP string
17+
}
18+
19+
var DNS_SERVERS = []dnsServer{
20+
{Name: "AdGuard", IP: "176.103.130.130"},
21+
{Name: "AdGuard Family", IP: "176.103.130.132"},
22+
{Name: "CleanBrowsing Adult", IP: "185.228.168.10"},
23+
{Name: "CleanBrowsing Family", IP: "185.228.168.168"},
24+
{Name: "CleanBrowsing Security", IP: "185.228.168.9"},
25+
{Name: "CloudFlare", IP: "1.1.1.1"},
26+
{Name: "CloudFlare Family", IP: "1.1.1.3"},
27+
{Name: "Comodo Secure", IP: "8.26.56.26"},
28+
{Name: "Google DNS", IP: "8.8.8.8"},
29+
{Name: "Neustar Family", IP: "156.154.70.3"},
30+
{Name: "Neustar Protection", IP: "156.154.70.2"},
31+
{Name: "Norton Family", IP: "199.85.126.20"},
32+
{Name: "OpenDNS", IP: "208.67.222.222"},
33+
{Name: "OpenDNS Family", IP: "208.67.222.123"},
34+
{Name: "Quad9", IP: "9.9.9.9"},
35+
{Name: "Yandex Family", IP: "77.88.8.7"},
36+
{Name: "Yandex Safe", IP: "77.88.8.88"},
37+
}
38+
39+
var knownBlockIPs = []string{
40+
"146.112.61.106",
41+
"185.228.168.10",
42+
"8.26.56.26",
43+
"9.9.9.9",
44+
"208.69.38.170",
45+
"208.69.39.170",
46+
"208.67.222.222",
47+
"208.67.222.123",
48+
"199.85.126.10",
49+
"199.85.126.20",
50+
"156.154.70.22",
51+
"77.88.8.7",
52+
"77.88.8.8",
53+
"::1",
54+
"2a02:6b8::feed:0ff",
55+
"2a02:6b8::feed:bad",
56+
"2a02:6b8::feed:a11",
57+
"2620:119:35::35",
58+
"2620:119:53::53",
59+
"2606:4700:4700::1111",
60+
"2606:4700:4700::1001",
61+
"2001:4860:4860::8888",
62+
"2a0d:2a00:1::",
63+
"2a0d:2a00:2::",
64+
}
65+
66+
type Blocklist struct {
67+
Server string `json:"server"`
68+
ServerIP string `json:"serverIp"`
69+
IsBlocked bool `json:"isBlocked"`
70+
}
71+
72+
type BlockList struct {
73+
lookup ip.DNSLookup
74+
}
75+
76+
func NewBlockList(lookup ip.DNSLookup) *BlockList {
77+
return &BlockList{lookup: lookup}
78+
}
79+
80+
func (b *BlockList) domainBlocked(ctx context.Context, domain, serverIP string) bool {
81+
ips, err := b.lookup.DNSLookupIP(ctx, "ip4", domain, serverIP)
82+
if err != nil {
83+
// if there's an error, consider it not blocked
84+
// TODO: return more detailed errors for each server
85+
return false
86+
}
87+
88+
return slices.ContainsFunc(ips, func(ip net.IP) bool {
89+
return slices.Contains(knownBlockIPs, ip.String())
90+
})
91+
}
92+
93+
func (b *BlockList) BlockedServers(ctx context.Context, domain string) []Blocklist {
94+
var lock sync.Mutex
95+
var wg sync.WaitGroup
96+
limit := make(chan struct{}, 5)
97+
98+
var results []Blocklist
99+
100+
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
101+
defer cancel()
102+
103+
for _, server := range DNS_SERVERS {
104+
wg.Add(1)
105+
go func(server dnsServer) {
106+
limit <- struct{}{}
107+
defer func() {
108+
<-limit
109+
wg.Done()
110+
}()
111+
112+
isBlocked := b.domainBlocked(ctx, domain, server.IP)
113+
lock.Lock()
114+
defer lock.Unlock()
115+
results = append(results, Blocklist{
116+
Server: server.Name,
117+
ServerIP: server.IP,
118+
IsBlocked: isBlocked,
119+
})
120+
}(server)
121+
}
122+
wg.Wait()
123+
124+
sort.Slice(results, func(i, j int) bool {
125+
return results[i].Server < results[j].Server
126+
})
127+
return results
128+
}

checks/block_lists_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package checks
2+
3+
import (
4+
"context"
5+
"net"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/xray-web/web-check-api/checks/clients/ip"
10+
)
11+
12+
func TestBlockList(t *testing.T) {
13+
t.Parallel()
14+
15+
t.Run("blocked IP", func(t *testing.T) {
16+
t.Parallel()
17+
18+
dnsLookup := ip.DNSLookupFunc(func(ctx context.Context, network, host, dns string) ([]net.IP, error) {
19+
return []net.IP{net.ParseIP("146.112.61.106")}, nil
20+
})
21+
list := NewBlockList(dnsLookup).BlockedServers(context.Background(), "example.com")
22+
assert.Contains(t, list, Blocklist{Server: "AdGuard", ServerIP: "176.103.130.130", IsBlocked: true})
23+
})
24+
}

checks/checks.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,37 @@ import (
44
"net/http"
55
"time"
66

7+
"github.com/xray-web/web-check-api/checks/clients/ip"
78
"github.com/xray-web/web-check-api/checks/store/legacyrank"
89
)
910

1011
type Checks struct {
11-
Carbon *Carbon
12-
IpAddress *Ip
13-
LegacyRank *LegacyRank
14-
Rank *Rank
15-
SocialTags *SocialTags
16-
Tls *Tls
17-
Hsts *Hsts
12+
BlockList *BlockList
13+
Carbon *Carbon
14+
Headers *Headers
15+
Hsts *Hsts
16+
IpAddress *Ip
17+
LegacyRank *LegacyRank
18+
LinkedPages *LinkedPages
19+
Rank *Rank
20+
SocialTags *SocialTags
21+
Tls *Tls
1822
}
1923

2024
func NewChecks() *Checks {
2125
client := &http.Client{
2226
Timeout: 5 * time.Second,
2327
}
2428
return &Checks{
25-
Carbon: NewCarbon(client),
26-
IpAddress: NewIp(NewNetIp()),
27-
LegacyRank: NewLegacyRank(legacyrank.NewInMemoryStore()),
28-
Rank: NewRank(client),
29-
SocialTags: NewSocialTags(client),
30-
Tls: NewTls(client),
31-
Hsts: NewHsts(client),
29+
BlockList: NewBlockList(&ip.NetDNSLookup{}),
30+
Carbon: NewCarbon(client),
31+
Headers: NewHeaders(client),
32+
Hsts: NewHsts(client),
33+
IpAddress: NewIp(NewNetIp()),
34+
LegacyRank: NewLegacyRank(legacyrank.NewInMemoryStore()),
35+
LinkedPages: NewLinkedPages(client),
36+
Rank: NewRank(client),
37+
SocialTags: NewSocialTags(client),
38+
Tls: NewTls(client),
3239
}
3340
}

checks/clients/ip/ip.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package ip
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net"
7+
"time"
8+
)
9+
10+
type Lookup interface {
11+
LookupIP(ctx context.Context, network string, host string) ([]net.IP, error)
12+
}
13+
14+
type LookupFunc func(ctx context.Context, network string, host string) ([]net.IP, error)
15+
16+
func (fn LookupFunc) LookupIP(ctx context.Context, network string, host string) ([]net.IP, error) {
17+
return fn(ctx, network, host)
18+
}
19+
20+
// NetLookup is a client for looking up IP addresses using a net.Resolver.
21+
type NetLookup struct{}
22+
23+
func (l *NetLookup) LookupIP(ctx context.Context, network string, host string) ([]net.IP, error) {
24+
netResolver := &net.Resolver{
25+
PreferGo: true,
26+
}
27+
return netResolver.LookupIP(ctx, network, host)
28+
}
29+
30+
type DNSLookup interface {
31+
DNSLookupIP(ctx context.Context, network, host, dns string) ([]net.IP, error)
32+
}
33+
34+
type DNSLookupFunc func(ctx context.Context, network, host, dns string) ([]net.IP, error)
35+
36+
func (fn DNSLookupFunc) DNSLookupIP(ctx context.Context, network, host, dns string) ([]net.IP, error) {
37+
return fn(ctx, network, host, dns)
38+
}
39+
40+
// DNSLookup is a client for looking up IP addresses with a custom DNS server.
41+
type NetDNSLookup struct{}
42+
43+
func (l *NetDNSLookup) DNSLookupIP(ctx context.Context, network, host, dns string) ([]net.IP, error) {
44+
netResolver := &net.Resolver{
45+
PreferGo: true,
46+
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
47+
d := net.Dialer{
48+
Timeout: 3 * time.Second,
49+
}
50+
return d.DialContext(ctx, network, fmt.Sprintf("%s:%d", dns, 53))
51+
},
52+
}
53+
return netResolver.LookupIP(ctx, network, host)
54+
}

checks/headers.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package checks
2+
3+
import (
4+
"context"
5+
"net/http"
6+
)
7+
8+
type Headers struct {
9+
client *http.Client
10+
}
11+
12+
func NewHeaders(client *http.Client) *Headers {
13+
return &Headers{client: client}
14+
}
15+
16+
func (h *Headers) List(ctx context.Context, url string) (http.Header, error) {
17+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
18+
if err != nil {
19+
return nil, err
20+
}
21+
22+
resp, err := h.client.Do(req)
23+
if err != nil {
24+
return nil, err
25+
}
26+
defer resp.Body.Close()
27+
28+
return resp.Header, nil
29+
}

checks/headers_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package checks
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/xray-web/web-check-api/testutils"
10+
)
11+
12+
func TestList(t *testing.T) {
13+
t.Parallel()
14+
15+
c := testutils.MockClient(&http.Response{
16+
Header: http.Header{
17+
"Cache-Control": {"private", "max-age=0"},
18+
"X-Xss-Protection": {"0"},
19+
},
20+
})
21+
h := NewHeaders(c)
22+
23+
actual, err := h.List(context.Background(), "example.com")
24+
assert.NoError(t, err)
25+
26+
assert.Equal(t, []string{"private", "max-age=0"}, actual["Cache-Control"])
27+
assert.Equal(t, []string{"0"}, actual["X-Xss-Protection"])
28+
}

0 commit comments

Comments
 (0)