Go · 1519 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package ratelimit
4
5 import (
6 "net"
7 "net/http"
8 "net/netip"
9 "strings"
10 )
11
12 // IPKey is the canonical anonymous-request keyer: extracts the
13 // client IP from r.RemoteAddr or the X-Forwarded-For header. The
14 // trust-XFF flag should be set ONLY when the deployment runs
15 // behind a CDN/proxy we control; otherwise an attacker can spoof
16 // the header and dodge IP-keyed limits.
17 func IPKey(trustForwarded bool) KeyFunc {
18 return func(r *http.Request) string {
19 if trustForwarded {
20 if v := r.Header.Get("X-Forwarded-For"); v != "" {
21 // First entry is the client; downstream proxies append.
22 if comma := strings.IndexByte(v, ','); comma > 0 {
23 v = v[:comma]
24 }
25 return "ip:" + strings.TrimSpace(v)
26 }
27 }
28 host, _, err := net.SplitHostPort(r.RemoteAddr)
29 if err != nil {
30 host = r.RemoteAddr
31 }
32 return "ip:" + host
33 }
34 }
35
36 // ClientIP returns the parsed client IP using the same rules as
37 // IPKey. Used by signup-throttle's CIDR keying.
38 func ClientIP(r *http.Request, trustForwarded bool) (netip.Addr, bool) {
39 raw := ""
40 if trustForwarded {
41 if v := r.Header.Get("X-Forwarded-For"); v != "" {
42 if comma := strings.IndexByte(v, ','); comma > 0 {
43 v = v[:comma]
44 }
45 raw = strings.TrimSpace(v)
46 }
47 }
48 if raw == "" {
49 host, _, err := net.SplitHostPort(r.RemoteAddr)
50 if err == nil {
51 raw = host
52 } else {
53 raw = r.RemoteAddr
54 }
55 }
56 addr, err := netip.ParseAddr(raw)
57 if err != nil {
58 return netip.Addr{}, false
59 }
60 return addr, true
61 }
62