mirror of
https://github.com/gin-gonic/gin.git
synced 2026-04-29 23:23:18 +08:00
Merge 2dc3c023731c6f8e21f658a02e4a99eb22b889de into d3ffc9985281dcf4d3bef604cce4e662b1a327a6
This commit is contained in:
commit
49145d46f2
33
gin.go
33
gin.go
@ -485,8 +485,8 @@ func (engine *Engine) validateHeader(header string) (clientIP string, valid bool
|
||||
}
|
||||
items := strings.Split(header, ",")
|
||||
for i := len(items) - 1; i >= 0; i-- {
|
||||
ipStr := strings.TrimSpace(items[i])
|
||||
ip := net.ParseIP(ipStr)
|
||||
item := strings.TrimSpace(items[i])
|
||||
ipStr, ip := parseForwardedForItem(item)
|
||||
if ip == nil {
|
||||
break
|
||||
}
|
||||
@ -500,6 +500,35 @@ func (engine *Engine) validateHeader(header string) (clientIP string, valid bool
|
||||
return "", false
|
||||
}
|
||||
|
||||
// parseForwardedForItem normalizes a single X-Forwarded-For entry and parses it.
|
||||
// It accepts the four common forms emitted by reverse proxies:
|
||||
//
|
||||
// - "1.2.3.4"
|
||||
// - "2001:db8::1"
|
||||
// - "[2001:db8::1]" (IIS/ARR style)
|
||||
// - "1.2.3.4:12345" (with port, some LBs)
|
||||
// - "[2001:db8::1]:12345" (IIS/ARR + port)
|
||||
//
|
||||
// The returned string is the IP without brackets or port, so callers see a
|
||||
// consistent form regardless of which proxy produced the header.
|
||||
func parseForwardedForItem(item string) (string, net.IP) {
|
||||
// Try host:port form first (handles "ip:port" and "[ipv6]:port").
|
||||
if host, _, err := net.SplitHostPort(item); err == nil {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return host, ip
|
||||
}
|
||||
}
|
||||
// Strip optional surrounding brackets for bare "[ipv6]" with no port.
|
||||
unbracketed := item
|
||||
if strings.HasPrefix(unbracketed, "[") && strings.HasSuffix(unbracketed, "]") {
|
||||
unbracketed = unbracketed[1 : len(unbracketed)-1]
|
||||
}
|
||||
if ip := net.ParseIP(unbracketed); ip != nil {
|
||||
return unbracketed, ip
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// updateRouteTree do update to the route tree recursively
|
||||
func updateRouteTree(n *node) {
|
||||
n.path = strings.ReplaceAll(n.path, escapedColon, colon)
|
||||
|
||||
32
gin_test.go
32
gin_test.go
@ -1156,3 +1156,35 @@ func TestUpdateRouteTreesCalledOnce(t *testing.T) {
|
||||
assert.Equal(t, "ok", w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateHeaderForwardedForForms(t *testing.T) {
|
||||
engine := New()
|
||||
// Disable trusted proxies so the rightmost parseable entry is returned.
|
||||
require.NoError(t, engine.SetTrustedProxies(nil))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
header string
|
||||
wantIP string
|
||||
wantOK bool
|
||||
}{
|
||||
{"plain IPv4", "192.168.8.39", "192.168.8.39", true},
|
||||
{"plain IPv6", "240e:318:2f4a:de56::240", "240e:318:2f4a:de56::240", true},
|
||||
{"bracketed IPv6 (IIS/ARR)", "[240e:318:2f4a:de56::240]", "240e:318:2f4a:de56::240", true},
|
||||
{"IPv4 with port", "192.168.8.39:38792", "192.168.8.39", true},
|
||||
{"bracketed IPv6 with port", "[240e:318:2f4a:de56::240]:38792", "240e:318:2f4a:de56::240", true},
|
||||
{"IPv6 loopback bracketed", "[::1]", "::1", true},
|
||||
{"chain with port on last entry", "1.2.3.4, 5.6.7.8:9000", "5.6.7.8", true},
|
||||
{"empty", "", "", false},
|
||||
{"garbage", "not-an-ip", "", false},
|
||||
{"bracketed garbage", "[not-an-ip]", "", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotIP, gotOK := engine.validateHeader(tt.header)
|
||||
assert.Equal(t, tt.wantOK, gotOK)
|
||||
assert.Equal(t, tt.wantIP, gotIP)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user