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, ",")
|
items := strings.Split(header, ",")
|
||||||
for i := len(items) - 1; i >= 0; i-- {
|
for i := len(items) - 1; i >= 0; i-- {
|
||||||
ipStr := strings.TrimSpace(items[i])
|
item := strings.TrimSpace(items[i])
|
||||||
ip := net.ParseIP(ipStr)
|
ipStr, ip := parseForwardedForItem(item)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -500,6 +500,35 @@ func (engine *Engine) validateHeader(header string) (clientIP string, valid bool
|
|||||||
return "", false
|
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
|
// updateRouteTree do update to the route tree recursively
|
||||||
func updateRouteTree(n *node) {
|
func updateRouteTree(n *node) {
|
||||||
n.path = strings.ReplaceAll(n.path, escapedColon, colon)
|
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())
|
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