mirror of
https://github.com/gin-gonic/gin.git
synced 2026-06-07 04:38:19 +08:00
Merge 16857146c8961f809f1a6735e84556d5835eaec9 into 5f4f9643258dc2a65e684b63f12c8d543c936c67
This commit is contained in:
commit
55382278e7
11
context.go
11
context.go
@ -15,6 +15,7 @@ import (
|
|||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -991,7 +992,7 @@ func (c *Context) ClientIP() string {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
trusted bool
|
trusted bool
|
||||||
remoteIP net.IP
|
remoteIP netip.Addr
|
||||||
)
|
)
|
||||||
// If gin is listening a unix socket, always trust it.
|
// If gin is listening a unix socket, always trust it.
|
||||||
localAddr, ok := c.Request.Context().Value(http.LocalAddrContextKey).(net.Addr)
|
localAddr, ok := c.Request.Context().Value(http.LocalAddrContextKey).(net.Addr)
|
||||||
@ -1004,8 +1005,9 @@ func (c *Context) ClientIP() string {
|
|||||||
// It also checks if the remoteIP is a trusted proxy or not.
|
// It also checks if the remoteIP is a trusted proxy or not.
|
||||||
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
|
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
|
||||||
// defined by Engine.SetTrustedProxies()
|
// defined by Engine.SetTrustedProxies()
|
||||||
remoteIP = net.ParseIP(c.RemoteIP())
|
var err error
|
||||||
if remoteIP == nil {
|
remoteIP, err = netip.ParseAddr(c.RemoteIP())
|
||||||
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
trusted = c.engine.isTrustedProxy(remoteIP)
|
trusted = c.engine.isTrustedProxy(remoteIP)
|
||||||
@ -1020,6 +1022,9 @@ func (c *Context) ClientIP() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !remoteIP.IsValid() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return remoteIP.String()
|
return remoteIP.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -1983,6 +1984,12 @@ func TestContextClientIP(t *testing.T) {
|
|||||||
c.Request.RemoteAddr = addr.String()
|
c.Request.RemoteAddr = addr.String()
|
||||||
assert.Equal(t, "20.20.20.20", c.ClientIP())
|
assert.Equal(t, "20.20.20.20", c.ClientIP())
|
||||||
|
|
||||||
|
// unix address with no valid forwarded header: remoteIP stays zero, must return ""
|
||||||
|
c.Request.Header.Del("X-Forwarded-For")
|
||||||
|
c.Request.Header.Del("X-Real-IP")
|
||||||
|
assert.Empty(t, c.ClientIP())
|
||||||
|
resetContextForClientIPTests(c)
|
||||||
|
|
||||||
// reset
|
// reset
|
||||||
c.Request = c.Request.WithContext(context.Background())
|
c.Request = c.Request.WithContext(context.Background())
|
||||||
resetContextForClientIPTests(c)
|
resetContextForClientIPTests(c)
|
||||||
@ -3128,9 +3135,9 @@ func TestRemoteIPFail(t *testing.T) {
|
|||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
|
c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
|
||||||
c.Request.RemoteAddr = "[:::]:80"
|
c.Request.RemoteAddr = "[:::]:80"
|
||||||
ip := net.ParseIP(c.RemoteIP())
|
ip, err := netip.ParseAddr(c.RemoteIP())
|
||||||
trust := c.engine.isTrustedProxy(ip)
|
trust := c.engine.isTrustedProxy(ip)
|
||||||
assert.Nil(t, ip)
|
require.Error(t, err)
|
||||||
assert.False(t, trust)
|
assert.False(t, trust)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
67
gin.go
67
gin.go
@ -9,6 +9,7 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
@ -36,15 +37,9 @@ var (
|
|||||||
|
|
||||||
var defaultPlatform string
|
var defaultPlatform string
|
||||||
|
|
||||||
var defaultTrustedCIDRs = []*net.IPNet{
|
var defaultTrustedCIDRs = []netip.Prefix{
|
||||||
{ // 0.0.0.0/0 (IPv4)
|
netip.MustParsePrefix("0.0.0.0/0"), // IPv4
|
||||||
IP: net.IP{0x0, 0x0, 0x0, 0x0},
|
netip.MustParsePrefix("::/0"), // IPv6
|
||||||
Mask: net.IPMask{0x0, 0x0, 0x0, 0x0},
|
|
||||||
},
|
|
||||||
{ // ::/0 (IPv6)
|
|
||||||
IP: net.IP{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
|
||||||
Mask: net.IPMask{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandlerFunc defines the handler used by gin middleware as return value.
|
// HandlerFunc defines the handler used by gin middleware as return value.
|
||||||
@ -185,7 +180,7 @@ type Engine struct {
|
|||||||
maxParams uint16
|
maxParams uint16
|
||||||
maxSections uint16
|
maxSections uint16
|
||||||
trustedProxies []string
|
trustedProxies []string
|
||||||
trustedCIDRs []*net.IPNet
|
trustedCIDRs []netip.Prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ IRouter = (*Engine)(nil)
|
var _ IRouter = (*Engine)(nil)
|
||||||
@ -411,33 +406,33 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
|
|||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
|
func (engine *Engine) prepareTrustedCIDRs() ([]netip.Prefix, error) {
|
||||||
if engine.trustedProxies == nil {
|
if engine.trustedProxies == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cidr := make([]*net.IPNet, 0, len(engine.trustedProxies))
|
cidrs := make([]netip.Prefix, 0, len(engine.trustedProxies))
|
||||||
for _, trustedProxy := range engine.trustedProxies {
|
for _, trustedProxy := range engine.trustedProxies {
|
||||||
if !strings.Contains(trustedProxy, "/") {
|
if !strings.Contains(trustedProxy, "/") {
|
||||||
ip := parseIP(trustedProxy)
|
addr, err := netip.ParseAddr(trustedProxy)
|
||||||
if ip == nil {
|
if err != nil {
|
||||||
return cidr, &net.ParseError{Type: "IP address", Text: trustedProxy}
|
return cidrs, &net.ParseError{Type: "IP address", Text: trustedProxy}
|
||||||
}
|
}
|
||||||
|
addr = addr.Unmap()
|
||||||
switch len(ip) {
|
bits := 128
|
||||||
case net.IPv4len:
|
if addr.Is4() {
|
||||||
trustedProxy += "/32"
|
bits = 32
|
||||||
case net.IPv6len:
|
|
||||||
trustedProxy += "/128"
|
|
||||||
}
|
}
|
||||||
|
cidrs = append(cidrs, netip.PrefixFrom(addr, bits))
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
_, cidrNet, err := net.ParseCIDR(trustedProxy)
|
prefix, err := netip.ParsePrefix(trustedProxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cidr, err
|
return cidrs, &net.ParseError{Type: "CIDR address", Text: trustedProxy}
|
||||||
}
|
}
|
||||||
cidr = append(cidr, cidrNet)
|
cidrs = append(cidrs, prefix.Masked())
|
||||||
}
|
}
|
||||||
return cidr, nil
|
return cidrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTrustedProxies set a list of network origins (IPv4 addresses,
|
// SetTrustedProxies set a list of network origins (IPv4 addresses,
|
||||||
@ -455,7 +450,7 @@ func (engine *Engine) SetTrustedProxies(trustedProxies []string) error {
|
|||||||
|
|
||||||
// isUnsafeTrustedProxies checks if Engine.trustedCIDRs contains all IPs, it's not safe if it has (returns true)
|
// isUnsafeTrustedProxies checks if Engine.trustedCIDRs contains all IPs, it's not safe if it has (returns true)
|
||||||
func (engine *Engine) isUnsafeTrustedProxies() bool {
|
func (engine *Engine) isUnsafeTrustedProxies() bool {
|
||||||
return engine.isTrustedProxy(net.ParseIP("0.0.0.0")) || engine.isTrustedProxy(net.ParseIP("::"))
|
return engine.isTrustedProxy(netip.MustParseAddr("0.0.0.0")) || engine.isTrustedProxy(netip.MustParseAddr("::"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseTrustedProxies parse Engine.trustedProxies to Engine.trustedCIDRs
|
// parseTrustedProxies parse Engine.trustedProxies to Engine.trustedCIDRs
|
||||||
@ -466,7 +461,7 @@ func (engine *Engine) parseTrustedProxies() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isTrustedProxy will check whether the IP address is included in the trusted list according to Engine.trustedCIDRs
|
// isTrustedProxy will check whether the IP address is included in the trusted list according to Engine.trustedCIDRs
|
||||||
func (engine *Engine) isTrustedProxy(ip net.IP) bool {
|
func (engine *Engine) isTrustedProxy(ip netip.Addr) bool {
|
||||||
if engine.trustedCIDRs == nil {
|
if engine.trustedCIDRs == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -486,8 +481,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])
|
ipStr := strings.TrimSpace(items[i])
|
||||||
ip := net.ParseIP(ipStr)
|
ip, err := netip.ParseAddr(ipStr)
|
||||||
if ip == nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,20 +515,6 @@ func (engine *Engine) updateRouteTrees() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseIP parse a string representation of an IP and returns a net.IP with the
|
|
||||||
// minimum byte representation or nil if input is invalid.
|
|
||||||
func parseIP(ip string) net.IP {
|
|
||||||
parsedIP := net.ParseIP(ip)
|
|
||||||
|
|
||||||
if ipv4 := parsedIP.To4(); ipv4 != nil {
|
|
||||||
// return ip in a 4-byte representation
|
|
||||||
return ipv4
|
|
||||||
}
|
|
||||||
|
|
||||||
// return ip in a 16-byte representation or nil
|
|
||||||
return parsedIP
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
|
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
|
||||||
// It is a shortcut for http.ListenAndServe(addr, router)
|
// It is a shortcut for http.ListenAndServe(addr, router)
|
||||||
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
// Note: this method will block the calling goroutine indefinitely unless an error happens.
|
||||||
|
|||||||
25
gin_test.go
25
gin_test.go
@ -12,6 +12,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/netip"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -869,7 +870,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
|
|
||||||
// valid ipv4 cidr
|
// valid ipv4 cidr
|
||||||
{
|
{
|
||||||
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("0.0.0.0/0")}
|
expectedTrustedCIDRs := []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")}
|
||||||
err := r.SetTrustedProxies([]string{"0.0.0.0/0"})
|
err := r.SetTrustedProxies([]string{"0.0.0.0/0"})
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -885,7 +886,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
|
|
||||||
// valid ipv4 address
|
// valid ipv4 address
|
||||||
{
|
{
|
||||||
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("192.168.1.33/32")}
|
expectedTrustedCIDRs := []netip.Prefix{netip.MustParsePrefix("192.168.1.33/32")}
|
||||||
|
|
||||||
err := r.SetTrustedProxies([]string{"192.168.1.33"})
|
err := r.SetTrustedProxies([]string{"192.168.1.33"})
|
||||||
|
|
||||||
@ -902,7 +903,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
|
|
||||||
// valid ipv6 address
|
// valid ipv6 address
|
||||||
{
|
{
|
||||||
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("2002:0000:0000:1234:abcd:ffff:c0a8:0101/128")}
|
expectedTrustedCIDRs := []netip.Prefix{netip.MustParsePrefix("2002:0000:0000:1234:abcd:ffff:c0a8:0101/128")}
|
||||||
err := r.SetTrustedProxies([]string{"2002:0000:0000:1234:abcd:ffff:c0a8:0101"})
|
err := r.SetTrustedProxies([]string{"2002:0000:0000:1234:abcd:ffff:c0a8:0101"})
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -918,7 +919,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
|
|
||||||
// valid ipv6 cidr
|
// valid ipv6 cidr
|
||||||
{
|
{
|
||||||
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("::/0")}
|
expectedTrustedCIDRs := []netip.Prefix{netip.MustParsePrefix("::/0")}
|
||||||
err := r.SetTrustedProxies([]string{"::/0"})
|
err := r.SetTrustedProxies([]string{"::/0"})
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -934,10 +935,10 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
|
|
||||||
// valid combination
|
// valid combination
|
||||||
{
|
{
|
||||||
expectedTrustedCIDRs := []*net.IPNet{
|
expectedTrustedCIDRs := []netip.Prefix{
|
||||||
parseCIDR("::/0"),
|
netip.MustParsePrefix("::/0"),
|
||||||
parseCIDR("192.168.0.0/16"),
|
netip.MustParsePrefix("192.168.0.0/16"),
|
||||||
parseCIDR("172.16.0.1/32"),
|
netip.MustParsePrefix("172.16.0.1/32"),
|
||||||
}
|
}
|
||||||
err := r.SetTrustedProxies([]string{
|
err := r.SetTrustedProxies([]string{
|
||||||
"::/0",
|
"::/0",
|
||||||
@ -969,14 +970,6 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCIDR(cidr string) *net.IPNet {
|
|
||||||
_, parsedCIDR, err := net.ParseCIDR(cidr)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
return parsedCIDR
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) {
|
func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) {
|
||||||
for _, gotRoute := range gotRoutes {
|
for _, gotRoute := range gotRoutes {
|
||||||
if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method {
|
if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user