Skip to content

Commit 90d4f9c

Browse files
authored
Merge pull request #17 from projectdiscovery/dev
0.0.3
2 parents 8df620a + 6b4cdc0 commit 90d4f9c

9 files changed

+320
-54
lines changed

README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
# cdncheck
2-
Helper library that checks if a given IP belongs to known CDN ranges (akamai, cloudflare, incapsula and sucuri).
2+
Helper library that checks if a given IP belongs to known CDN ranges (akamai, cloudflare, incapsula, sucuri and leaseweb).
33
The library can be used by importing `github.com/projectdiscovery/cdncheck`. here follows a basic example:
44

5-
65
```go
76
package main
87

cdn-server/main.go

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"flag"
8+
"log"
9+
"net/http"
10+
"sync"
11+
"time"
12+
13+
"github.com/projectdiscovery/cdncheck"
14+
)
15+
16+
var (
17+
cdncheckData []byte
18+
cdncehckDataMutex = &sync.RWMutex{}
19+
20+
addr = flag.String("addr", "127.0.0.1:80", "Address to listen cdncheck server on")
21+
)
22+
23+
func main() {
24+
flag.Parse()
25+
26+
var cancel context.CancelFunc
27+
go func() {
28+
cancel = cdncheckWorker()
29+
}()
30+
cdncheckRefreshDataFunc()
31+
32+
http.HandleFunc("/", cdncheckHandler)
33+
if err := http.ListenAndServe(*addr, http.DefaultServeMux); err != nil {
34+
cancel()
35+
panic(err)
36+
}
37+
}
38+
39+
func cdncheckWorker() context.CancelFunc {
40+
ctx, cancel := context.WithCancel(context.Background())
41+
42+
ticker := time.NewTicker(24 * time.Hour)
43+
go func() {
44+
for {
45+
select {
46+
case <-ticker.C:
47+
cdncheckRefreshDataFunc()
48+
case <-ctx.Done():
49+
ticker.Stop()
50+
return
51+
default:
52+
continue
53+
}
54+
}
55+
}()
56+
return cancel
57+
}
58+
59+
func cdncheckRefreshDataFunc() {
60+
log.Printf("[%s] Refreshing cdncheck data from providers\n", time.Now().String())
61+
62+
client, err := cdncheck.New()
63+
if err != nil {
64+
log.Printf("[err] could not create cdncheck client: %s\n", err)
65+
return
66+
}
67+
data := client.Ranges()
68+
69+
var buf bytes.Buffer
70+
if err := json.NewEncoder(&buf).Encode(data); err != nil {
71+
log.Printf("[err] could not json encode cdn data: %s\n", err)
72+
return
73+
}
74+
75+
cdncehckDataMutex.Lock()
76+
cdncheckData = buf.Bytes()
77+
cdncehckDataMutex.Unlock()
78+
}
79+
80+
func cdncheckHandler(w http.ResponseWriter, r *http.Request) {
81+
cdncehckDataMutex.RLock()
82+
data := cdncheckData
83+
cdncehckDataMutex.RUnlock()
84+
85+
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
86+
_, _ = w.Write(data)
87+
}

cdncheck.go

+92-28
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,41 @@ import (
1212
// Client checks for CDN based IPs which should be excluded
1313
// during scans since they belong to third party firewalls.
1414
type Client struct {
15-
Data map[string]struct{}
16-
ranger cidranger.Ranger
15+
Options *Options
16+
ranges map[string][]string
17+
rangers map[string]cidranger.Ranger
1718
}
1819

1920
var defaultScrapers = map[string]scraperFunc{
20-
"akamai": scrapeAkamai,
21+
"azure": scrapeAzure,
2122
"cloudflare": scrapeCloudflare,
23+
"cloudfront": scrapeCloudFront,
24+
"fastly": scrapeFastly,
2225
"incapsula": scrapeIncapsula,
23-
"sucuri": scrapeSucuri,
2426
}
2527

26-
var cachedScrapers = map[string]scraperFunc{
27-
"projectdiscovery": scrapeProjectDiscovery,
28+
var defaultScrapersWithOptions = map[string]scraperWithOptionsFunc{
29+
"akamai": scrapeAkamai,
30+
"sucuri": scrapeSucuri,
31+
"leaseweb": scrapeLeaseweb,
2832
}
2933

3034
// New creates a new firewall IP checking client.
3135
func New() (*Client, error) {
32-
return new(false)
36+
return new(&Options{})
3337
}
3438

3539
// NewWithCache creates a new firewall IP with cached data from project discovery (faster)
3640
func NewWithCache() (*Client, error) {
37-
return new(true)
41+
return new(&Options{Cache: true})
3842
}
3943

40-
func new(cache bool) (*Client, error) {
44+
// NewWithOptions creates a new instance with options
45+
func NewWithOptions(Options *Options) (*Client, error) {
46+
return new(Options)
47+
}
48+
49+
func new(options *Options) (*Client, error) {
4150
httpClient := &http.Client{
4251
Transport: &http.Transport{
4352
MaxIdleConns: 100,
@@ -49,40 +58,95 @@ func new(cache bool) (*Client, error) {
4958
},
5059
Timeout: time.Duration(30) * time.Second,
5160
}
52-
client := &Client{}
61+
client := &Client{Options: options}
5362

54-
var scrapers map[string]scraperFunc
55-
if cache {
56-
scrapers = cachedScrapers
63+
var err error
64+
if options.Cache {
65+
err = client.getCDNDataFromCache(httpClient)
5766
} else {
58-
scrapers = defaultScrapers
67+
err = client.getCDNData(httpClient)
68+
}
69+
if err != nil {
70+
return nil, err
5971
}
72+
return client, nil
73+
}
6074

61-
client.Data = make(map[string]struct{})
62-
for _, scraper := range scrapers {
75+
func (c *Client) getCDNData(httpClient *http.Client) error {
76+
c.ranges = make(map[string][]string)
77+
c.rangers = make(map[string]cidranger.Ranger)
78+
79+
for provider, scraper := range defaultScrapers {
6380
cidrs, err := scraper(httpClient)
6481
if err != nil {
65-
return nil, err
82+
return err
6683
}
84+
85+
c.ranges[provider] = cidrs
86+
ranger := cidranger.NewPCTrieRanger()
6787
for _, cidr := range cidrs {
68-
client.Data[cidr] = struct{}{}
88+
_, network, err := net.ParseCIDR(cidr)
89+
if err != nil {
90+
continue
91+
}
92+
_ = ranger.Insert(cidranger.NewBasicRangerEntry(*network))
6993
}
7094
}
95+
if c.Options.HasAuthInfo() {
96+
for provider, scraper := range defaultScrapersWithOptions {
97+
cidrs, err := scraper(httpClient, c.Options)
98+
if err != nil {
99+
return err
100+
}
71101

72-
ranger := cidranger.NewPCTrieRanger()
73-
for cidr := range client.Data {
74-
_, network, err := net.ParseCIDR(cidr)
75-
if err != nil {
76-
continue
102+
c.ranges[provider] = cidrs
103+
ranger := cidranger.NewPCTrieRanger()
104+
for _, cidr := range cidrs {
105+
_, network, err := net.ParseCIDR(cidr)
106+
if err != nil {
107+
continue
108+
}
109+
_ = ranger.Insert(cidranger.NewBasicRangerEntry(*network))
110+
}
77111
}
78-
ranger.Insert(cidranger.NewBasicRangerEntry(*network))
79112
}
80-
client.ranger = ranger
113+
return nil
114+
}
81115

82-
return client, nil
116+
func (c *Client) getCDNDataFromCache(httpClient *http.Client) error {
117+
var err error
118+
c.ranges, err = scrapeProjectDiscovery(httpClient)
119+
if err != nil {
120+
return err
121+
}
122+
123+
c.rangers = make(map[string]cidranger.Ranger)
124+
for provider, ranges := range c.ranges {
125+
ranger := cidranger.NewPCTrieRanger()
126+
127+
for _, cidr := range ranges {
128+
_, network, err := net.ParseCIDR(cidr)
129+
if err != nil {
130+
continue
131+
}
132+
_ = ranger.Insert(cidranger.NewBasicRangerEntry(*network))
133+
}
134+
c.rangers[provider] = ranger
135+
}
136+
return nil
83137
}
84138

85139
// Check checks if an IP is contained in the blacklist
86-
func (c *Client) Check(ip net.IP) (bool, error) {
87-
return c.ranger.Contains(ip)
140+
func (c *Client) Check(ip net.IP) (bool, string, error) {
141+
for provider, ranger := range c.rangers {
142+
if contains, err := ranger.Contains(ip); contains {
143+
return true, provider, err
144+
}
145+
}
146+
return false, "", nil
147+
}
148+
149+
// Ranges returns the providers and ranges for the cdn client
150+
func (c *Client) Ranges() map[string][]string {
151+
return c.ranges
88152
}

cdncheck_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ func TestCDNCheck(t *testing.T) {
1111
client, err := New()
1212
require.Nil(t, err, "Could not create cdncheck client")
1313

14-
found, err := client.Check(net.ParseIP("173.245.48.12"))
14+
found, provider, err := client.Check(net.ParseIP("173.245.48.12"))
15+
require.Equal(t, "cloudflare", provider, "could not get correct provider")
1516
require.Nil(t, err, "Could not check ip in ranger")
1617
require.True(t, found, "Could not check cloudlfare ip blacklist")
1718

18-
found, err = client.Check(net.ParseIP("127.0.0.1"))
19+
found, _, err = client.Check(net.ParseIP("127.0.0.1"))
1920
require.Nil(t, err, "Could not check ip in ranger")
2021
require.False(t, found, "Localhost IP found in blacklist")
2122
}

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/projectdiscovery/cdncheck
33
go 1.14
44

55
require (
6-
github.com/davecgh/go-spew v1.1.1 // indirect
6+
github.com/json-iterator/go v1.1.12
77
github.com/kr/text v0.2.0 // indirect
88
github.com/stretchr/testify v1.7.0
99
github.com/yl2chen/cidranger v1.0.2

go.sum

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
11
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
2-
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
32
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
43
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
54
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
6+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
7+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
68
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
79
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
810
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
9-
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
1011
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
1112
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
1213
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
14+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
15+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
16+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
17+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
1318
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1419
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
15-
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
1620
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
21+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
1722
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
1823
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
1924
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
2025
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
2126
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
22-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2327
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2428
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
2529
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
2630
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
27-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
2831
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2932
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
3033
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

options.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package cdncheck
2+
3+
type Options struct {
4+
Cache bool
5+
IPInfoToken string
6+
}
7+
8+
func (options *Options) HasAuthInfo() bool {
9+
return options.IPInfoToken != ""
10+
}

0 commit comments

Comments
 (0)