aboutsummaryrefslogtreecommitdiff
path: root/pkg/cloudflare
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/cloudflare')
-rw-r--r--pkg/cloudflare/iplist.go88
-rw-r--r--pkg/cloudflare/iplist_generate.go52
-rw-r--r--pkg/cloudflare/iplist_generated.go34
3 files changed, 174 insertions, 0 deletions
diff --git a/pkg/cloudflare/iplist.go b/pkg/cloudflare/iplist.go
new file mode 100644
index 0000000..d19367e
--- /dev/null
+++ b/pkg/cloudflare/iplist.go
@@ -0,0 +1,88 @@
+// Package cloudflare contains Cloudflare-related stuff.
+package cloudflare
+
+import (
+ "bufio"
+ "context"
+ "fmt"
+ "net/http"
+ "net/netip"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+var iplistMu sync.Mutex
+var iplist []netip.Prefix
+
+//go:generate go run iplist_generate.go
+
+// HasIP checks if ip is in a Cloudflare prefix.
+func HasIP(ip netip.Addr) bool {
+ for _, p := range iplist {
+ if p.Contains(ip) {
+ return true
+ }
+ }
+ return false
+}
+
+// UpdateIPs updates the Cloudflare IP list.
+func UpdateIPs(ctx context.Context) error {
+ var ps []netip.Prefix
+ for _, v := range []int{4, 6} {
+ if v, err := fetchIPs(ctx, "https://www.cloudflare.com/ips-v"+strconv.Itoa(v)); err == nil {
+ ps = append(ps, v...)
+ } else {
+ fmt.Fprintf(os.Stderr, "error: fetch ipv%d list: %v\n", v, err)
+ os.Exit(1)
+ }
+ }
+ iplistMu.Lock()
+ iplist = ps
+ iplistMu.Unlock()
+ return nil
+}
+
+func fetchIPs(ctx context.Context, u string) ([]netip.Prefix, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("response status %d (%s)", resp.StatusCode, resp.Status)
+ }
+
+ var r []netip.Prefix
+ s := bufio.NewScanner(resp.Body)
+ for s.Scan() {
+ if t := strings.TrimSpace(s.Text()); t != "" {
+ if strings.ContainsRune(t, '/') {
+ if x, err := netip.ParsePrefix(t); err == nil {
+ r = append(r, x)
+ } else {
+ return nil, fmt.Errorf("invalid prefix %q: %w", t, err)
+ }
+ } else {
+ if x, err := netip.ParseAddr(t); err == nil {
+ if p, err := x.Prefix(x.BitLen()); err == nil {
+ r = append(r, p)
+ } else {
+ panic(err)
+ }
+ } else {
+ return nil, fmt.Errorf("invalid ip %q: %w", t, err)
+ }
+ }
+ }
+ }
+ return r, s.Err()
+}
diff --git a/pkg/cloudflare/iplist_generate.go b/pkg/cloudflare/iplist_generate.go
new file mode 100644
index 0000000..7e169c4
--- /dev/null
+++ b/pkg/cloudflare/iplist_generate.go
@@ -0,0 +1,52 @@
+//go:build generate
+// +build generate
+
+package main
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "go/format"
+ "net/netip"
+ "os"
+ "time"
+ _ "unsafe"
+
+ "github.com/pg9182/atlas/pkg/cloudflare"
+)
+
+//go:linkname iplist github.com/pg9182/atlas/pkg/cloudflare.iplist
+var iplist []netip.Prefix
+
+func main() {
+ if err := cloudflare.UpdateIPs(context.Background()); err != nil {
+ fmt.Fprintf(os.Stderr, "error: fetch ip list: %v\n", err)
+ os.Exit(1)
+ }
+
+ var b bytes.Buffer
+ fmt.Fprintf(&b, "// Code generated by iplist_generate on %s; DO NOT EDIT.\n", time.Now().UTC())
+ fmt.Fprintf(&b, "\n\n")
+ fmt.Fprintf(&b, "package cloudflare\n")
+ fmt.Fprintf(&b, "\n\n")
+ fmt.Fprintf(&b, "import %#v\n", "net/netip")
+ fmt.Fprintf(&b, "\n\n")
+ fmt.Fprintf(&b, "//go:generate go run iplist_generate.go\n")
+ fmt.Fprintf(&b, "\n\n")
+ fmt.Fprintf(&b, "func init() {\n")
+ fmt.Fprintf(&b, "iplist = append(iplist,\n")
+ for _, p := range iplist {
+ fmt.Fprintf(&b, "netip.MustParsePrefix(%#v),\n", p.String())
+ }
+ fmt.Fprintf(&b, ")\n")
+ fmt.Fprintf(&b, "}\n")
+
+ if src, err := format.Source(b.Bytes()); err != nil {
+ fmt.Fprintf(os.Stderr, "error: format generated code: %v\n", err)
+ os.Exit(1)
+ } else if err = os.WriteFile("iplist_generated.go", src, 0666); err != nil {
+ fmt.Fprintf(os.Stderr, "error: write generated code: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/pkg/cloudflare/iplist_generated.go b/pkg/cloudflare/iplist_generated.go
new file mode 100644
index 0000000..5bf8ed6
--- /dev/null
+++ b/pkg/cloudflare/iplist_generated.go
@@ -0,0 +1,34 @@
+// Code generated by iplist_generate on 2022-10-21 06:19:36.019416787 +0000 UTC; DO NOT EDIT.
+
+package cloudflare
+
+import "net/netip"
+
+//go:generate go run iplist_generate.go
+
+func init() {
+ iplist = append(iplist,
+ netip.MustParsePrefix("173.245.48.0/20"),
+ netip.MustParsePrefix("103.21.244.0/22"),
+ netip.MustParsePrefix("103.22.200.0/22"),
+ netip.MustParsePrefix("103.31.4.0/22"),
+ netip.MustParsePrefix("141.101.64.0/18"),
+ netip.MustParsePrefix("108.162.192.0/18"),
+ netip.MustParsePrefix("190.93.240.0/20"),
+ netip.MustParsePrefix("188.114.96.0/20"),
+ netip.MustParsePrefix("197.234.240.0/22"),
+ netip.MustParsePrefix("198.41.128.0/17"),
+ netip.MustParsePrefix("162.158.0.0/15"),
+ netip.MustParsePrefix("104.16.0.0/13"),
+ netip.MustParsePrefix("104.24.0.0/14"),
+ netip.MustParsePrefix("172.64.0.0/13"),
+ netip.MustParsePrefix("131.0.72.0/22"),
+ netip.MustParsePrefix("2400:cb00::/32"),
+ netip.MustParsePrefix("2606:4700::/32"),
+ netip.MustParsePrefix("2803:f800::/32"),
+ netip.MustParsePrefix("2405:b500::/32"),
+ netip.MustParsePrefix("2405:8100::/32"),
+ netip.MustParsePrefix("2a06:98c0::/29"),
+ netip.MustParsePrefix("2c0f:f248::/32"),
+ )
+}