Skip to content

Commit dab9bc0

Browse files
committed
Add more ranges to google's allowed bots
1 parent 2490bde commit dab9bc0

3 files changed

Lines changed: 131 additions & 2 deletions

File tree

internal/helper/google.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,19 @@ import (
77
"log/slog"
88
"net"
99
"net/http"
10+
"net/netip"
11+
"slices"
1012
"sync"
1113
"time"
1214
)
1315

16+
var GoogleCrawlerIPRangeURLs = []string{
17+
"https://developers.google.com/static/search/apis/ipranges/googlebot.json",
18+
"https://developers.google.com/static/crawling/ipranges/common-crawlers.json",
19+
"https://developers.google.com/static/crawling/ipranges/special-crawlers.json",
20+
"https://developers.google.com/static/crawling/ipranges/user-triggered-fetchers-google.json",
21+
}
22+
1423
// GooglebotIPs holds the list of Googlebot IP ranges, providing a thread-safe way to check if an IP is a Googlebot.
1524
type GooglebotIPs struct {
1625
cidrs []*net.IPNet
@@ -108,3 +117,76 @@ func FetchGooglebotIPs(log *slog.Logger, httpClient *http.Client, url string) ([
108117

109118
return cidrs, nil
110119
}
120+
121+
// FetchGoogleCrawlerIPs fetches crawler IP ranges from multiple Google-managed endpoints,
122+
// then returns a canonical, unique list where broader prefixes replace narrower prefixes.
123+
func FetchGoogleCrawlerIPs(log *slog.Logger, httpClient *http.Client, urls []string) ([]string, error) {
124+
if len(urls) == 0 {
125+
return nil, nil
126+
}
127+
128+
allCIDRs := make([]string, 0)
129+
for _, url := range urls {
130+
cidrs, err := FetchGooglebotIPs(log, httpClient, url)
131+
if err != nil {
132+
return nil, err
133+
}
134+
allCIDRs = append(allCIDRs, cidrs...)
135+
}
136+
137+
return ReduceCIDRs(allCIDRs, log), nil
138+
}
139+
140+
// ReduceCIDRs canonicalizes CIDRs, removes exact duplicates, and removes narrower
141+
// ranges when they are fully covered by broader ranges.
142+
func ReduceCIDRs(cidrs []string, log *slog.Logger) []string {
143+
prefixes := make([]netip.Prefix, 0, len(cidrs))
144+
for _, cidr := range cidrs {
145+
prefix, err := netip.ParsePrefix(cidr)
146+
if err != nil {
147+
if log != nil {
148+
log.Error("error parsing CIDR", "cidr", cidr, "err", err)
149+
}
150+
continue
151+
}
152+
prefixes = append(prefixes, prefix.Masked())
153+
}
154+
155+
slices.SortFunc(prefixes, func(a, b netip.Prefix) int {
156+
aIs4 := a.Addr().Is4()
157+
bIs4 := b.Addr().Is4()
158+
if aIs4 && !bIs4 {
159+
return -1
160+
}
161+
if !aIs4 && bIs4 {
162+
return 1
163+
}
164+
165+
if a.Bits() != b.Bits() {
166+
return a.Bits() - b.Bits()
167+
}
168+
169+
return a.Addr().Compare(b.Addr())
170+
})
171+
172+
reduced := make([]netip.Prefix, 0, len(prefixes))
173+
for _, candidate := range prefixes {
174+
covered := false
175+
for _, existing := range reduced {
176+
if existing.Bits() <= candidate.Bits() && existing.Contains(candidate.Addr()) {
177+
covered = true
178+
break
179+
}
180+
}
181+
if !covered {
182+
reduced = append(reduced, candidate)
183+
}
184+
}
185+
186+
result := make([]string, 0, len(reduced))
187+
for _, prefix := range reduced {
188+
result = append(result, prefix.String())
189+
}
190+
191+
return result
192+
}

internal/helper/google_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77
"net/http/httptest"
88
"os"
9+
"reflect"
910
"testing"
1011
)
1112

@@ -77,3 +78,49 @@ func TestFetchGooglebotIPs(t *testing.T) {
7778
}
7879
}
7980
}
81+
82+
func TestReduceCIDRs(t *testing.T) {
83+
log := slog.New(slog.NewTextHandler(os.Stdout, nil))
84+
85+
input := []string{
86+
"8.8.8.0/24",
87+
"8.8.8.0/25",
88+
"8.8.8.128/25",
89+
"8.8.8.0/24", // duplicate
90+
"2001:4860::/32",
91+
"2001:4860:1234::/48",
92+
}
93+
94+
got := ReduceCIDRs(input, log)
95+
want := []string{"8.8.8.0/24", "2001:4860::/32"}
96+
97+
if !reflect.DeepEqual(got, want) {
98+
t.Fatalf("unexpected reduced CIDRs: got %v want %v", got, want)
99+
}
100+
}
101+
102+
func TestFetchGoogleCrawlerIPs(t *testing.T) {
103+
log := slog.New(slog.NewTextHandler(os.Stdout, nil))
104+
105+
serverA := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
106+
w.Header().Set("Content-Type", "application/json")
107+
_, _ = w.Write([]byte(`{"prefixes":[{"ipv4Prefix":"8.8.8.0/24"}]}`))
108+
}))
109+
defer serverA.Close()
110+
111+
serverB := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
112+
w.Header().Set("Content-Type", "application/json")
113+
_, _ = w.Write([]byte(`{"prefixes":[{"ipv4Prefix":"8.8.8.0/25"},{"ipv6Prefix":"2001:4860::/32"}]}`))
114+
}))
115+
defer serverB.Close()
116+
117+
got, err := FetchGoogleCrawlerIPs(log, serverA.Client(), []string{serverA.URL, serverB.URL})
118+
if err != nil {
119+
t.Fatalf("FetchGoogleCrawlerIPs failed: %v", err)
120+
}
121+
122+
want := []string{"8.8.8.0/24", "2001:4860::/32"}
123+
if !reflect.DeepEqual(got, want) {
124+
t.Fatalf("unexpected CIDRs: got %v want %v", got, want)
125+
}
126+
}

main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ func (bc *CaptchaProtect) googlebotIPCheckLoop(ctx context.Context) {
351351
defer ticker.Stop()
352352

353353
// Initial fetch
354-
cidrs, err := helper.FetchGooglebotIPs(bc.log, bc.httpClient, "https://developers.google.com/static/search/apis/ipranges/googlebot.json")
354+
cidrs, err := helper.FetchGoogleCrawlerIPs(bc.log, bc.httpClient, helper.GoogleCrawlerIPRangeURLs)
355355
if err != nil {
356356
bc.log.Error("failed to fetch googlebot ips", "err", err)
357357
} else {
@@ -362,7 +362,7 @@ func (bc *CaptchaProtect) googlebotIPCheckLoop(ctx context.Context) {
362362
for {
363363
select {
364364
case <-ticker.C:
365-
cidrs, err := helper.FetchGooglebotIPs(bc.log, bc.httpClient, "https://developers.google.com/static/search/apis/ipranges/googlebot.json")
365+
cidrs, err := helper.FetchGoogleCrawlerIPs(bc.log, bc.httpClient, helper.GoogleCrawlerIPRangeURLs)
366366
if err != nil {
367367
bc.log.Error("failed to fetch googlebot ips", "err", err)
368368
continue

0 commit comments

Comments
 (0)