Merge d27dab6e8f87ddccb4c49d2d111b1f1a46d1fc4f into d3ffc9985281dcf4d3bef604cce4e662b1a327a6

This commit is contained in:
Shirshendu Bhowmick 2026-03-27 19:38:22 +00:00 committed by GitHub
commit ee02ec7f0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 37 additions and 62 deletions

View File

@ -15,6 +15,7 @@ import (
"mime/multipart"
"net"
"net/http"
"net/netip"
"net/url"
"os"
"path/filepath"
@ -991,7 +992,7 @@ func (c *Context) ClientIP() string {
var (
trusted bool
remoteIP net.IP
remoteIP netip.Addr
)
// If gin is listening a unix socket, always trust it.
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.
// 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()
remoteIP = net.ParseIP(c.RemoteIP())
if remoteIP == nil {
var err error
remoteIP, err = netip.ParseAddr(c.RemoteIP())
if err != nil {
return ""
}
trusted = c.engine.isTrustedProxy(remoteIP)

View File

@ -16,6 +16,7 @@ import (
"net"
"net/http"
"net/http/httptest"
"net/netip"
"net/url"
"os"
"path/filepath"
@ -3128,9 +3129,9 @@ func TestRemoteIPFail(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest(http.MethodPost, "/", nil)
c.Request.RemoteAddr = "[:::]:80"
ip := net.ParseIP(c.RemoteIP())
ip, err := netip.ParseAddr(c.RemoteIP())
trust := c.engine.isTrustedProxy(ip)
assert.Nil(t, ip)
require.Error(t, err)
assert.False(t, trust)
}

61
gin.go
View File

@ -9,6 +9,7 @@ import (
"html/template"
"net"
"net/http"
"net/netip"
"os"
"path"
"strings"
@ -36,15 +37,9 @@ var (
var defaultPlatform string
var defaultTrustedCIDRs = []*net.IPNet{
{ // 0.0.0.0/0 (IPv4)
IP: net.IP{0x0, 0x0, 0x0, 0x0},
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},
},
var defaultTrustedCIDRs = []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"), // IPv4
netip.MustParsePrefix("::/0"), // IPv6
}
// HandlerFunc defines the handler used by gin middleware as return value.
@ -185,7 +180,7 @@ type Engine struct {
maxParams uint16
maxSections uint16
trustedProxies []string
trustedCIDRs []*net.IPNet
trustedCIDRs []netip.Prefix
}
var _ IRouter = (*Engine)(nil)
@ -411,33 +406,31 @@ func iterate(path, method string, routes RoutesInfo, root *node) RoutesInfo {
return routes
}
func (engine *Engine) prepareTrustedCIDRs() ([]*net.IPNet, error) {
func (engine *Engine) prepareTrustedCIDRs() ([]netip.Prefix, error) {
if engine.trustedProxies == 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 {
if !strings.Contains(trustedProxy, "/") {
ip := parseIP(trustedProxy)
if ip == nil {
return cidr, &net.ParseError{Type: "IP address", Text: trustedProxy}
addr, err := netip.ParseAddr(trustedProxy)
if err != nil {
return cidrs, &net.ParseError{Type: "IP address", Text: trustedProxy}
}
switch len(ip) {
case net.IPv4len:
if addr.Is4() {
trustedProxy += "/32"
case net.IPv6len:
} else {
trustedProxy += "/128"
}
}
_, cidrNet, err := net.ParseCIDR(trustedProxy)
prefix, err := netip.ParsePrefix(trustedProxy)
if err != nil {
return cidr, err
return cidrs, err
}
cidr = append(cidr, cidrNet)
cidrs = append(cidrs, prefix.Masked())
}
return cidr, nil
return cidrs, nil
}
// SetTrustedProxies set a list of network origins (IPv4 addresses,
@ -455,7 +448,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)
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
@ -466,7 +459,7 @@ func (engine *Engine) parseTrustedProxies() error {
}
// 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 {
return false
}
@ -486,8 +479,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)
if ip == nil {
ip, err := netip.ParseAddr(ipStr)
if err != nil {
break
}
@ -520,20 +513,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.
// It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.

View File

@ -12,6 +12,7 @@ import (
"net"
"net/http"
"net/http/httptest"
"net/netip"
"reflect"
"strconv"
"strings"
@ -869,7 +870,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
// 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"})
require.NoError(t, err)
@ -885,7 +886,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
// 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"})
@ -902,7 +903,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
// 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"})
require.NoError(t, err)
@ -918,7 +919,7 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
// valid ipv6 cidr
{
expectedTrustedCIDRs := []*net.IPNet{parseCIDR("::/0")}
expectedTrustedCIDRs := []netip.Prefix{netip.MustParsePrefix("::/0")}
err := r.SetTrustedProxies([]string{"::/0"})
require.NoError(t, err)
@ -934,10 +935,10 @@ func TestPrepareTrustedCIRDsWith(t *testing.T) {
// valid combination
{
expectedTrustedCIDRs := []*net.IPNet{
parseCIDR("::/0"),
parseCIDR("192.168.0.0/16"),
parseCIDR("172.16.0.1/32"),
expectedTrustedCIDRs := []netip.Prefix{
netip.MustParsePrefix("::/0"),
netip.MustParsePrefix("192.168.0.0/16"),
netip.MustParsePrefix("172.16.0.1/32"),
}
err := r.SetTrustedProxies([]string{
"::/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) {
for _, gotRoute := range gotRoutes {
if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method {