2018-08-24 17:20:03 +03:00
package ip
import (
2019-08-26 13:20:06 +03:00
"net"
2018-08-24 17:20:03 +03:00
"net/http"
2024-09-24 19:04:05 +03:00
"net/netip"
2018-08-24 17:20:03 +03:00
"strings"
)
const (
xForwardedFor = "X-Forwarded-For"
)
2020-05-11 13:06:07 +03:00
// Strategy a strategy for IP selection.
2018-08-24 17:20:03 +03:00
type Strategy interface {
GetIP ( req * http . Request ) string
}
2020-05-11 13:06:07 +03:00
// RemoteAddrStrategy a strategy that always return the remote address.
2024-09-24 19:04:05 +03:00
type RemoteAddrStrategy struct {
// IPv6Subnet instructs the strategy to return the first IP of the subnet where IP belongs.
IPv6Subnet * int
}
2018-08-24 17:20:03 +03:00
2020-05-11 13:06:07 +03:00
// GetIP returns the selected IP.
2018-08-24 17:20:03 +03:00
func ( s * RemoteAddrStrategy ) GetIP ( req * http . Request ) string {
2019-08-26 13:20:06 +03:00
ip , _ , err := net . SplitHostPort ( req . RemoteAddr )
if err != nil {
return req . RemoteAddr
}
2024-09-24 19:04:05 +03:00
if s . IPv6Subnet != nil {
return getIPv6SubnetIP ( ip , * s . IPv6Subnet )
}
2019-08-26 13:20:06 +03:00
return ip
2018-08-24 17:20:03 +03:00
}
2020-05-11 13:06:07 +03:00
// DepthStrategy a strategy based on the depth inside the X-Forwarded-For from right to left.
2018-08-24 17:20:03 +03:00
type DepthStrategy struct {
Depth int
2024-09-24 19:04:05 +03:00
// IPv6Subnet instructs the strategy to return the first IP of the subnet where IP belongs.
IPv6Subnet * int
2018-08-24 17:20:03 +03:00
}
2024-09-24 19:04:05 +03:00
// GetIP returns the selected IP.
2018-08-24 17:20:03 +03:00
func ( s * DepthStrategy ) GetIP ( req * http . Request ) string {
xff := req . Header . Get ( xForwardedFor )
xffs := strings . Split ( xff , "," )
if len ( xffs ) < s . Depth {
return ""
}
2024-09-24 19:04:05 +03:00
ip := strings . TrimSpace ( xffs [ len ( xffs ) - s . Depth ] )
if s . IPv6Subnet != nil {
return getIPv6SubnetIP ( ip , * s . IPv6Subnet )
}
return ip
2018-08-24 17:20:03 +03:00
}
2021-06-07 18:46:14 +03:00
// PoolStrategy is a strategy based on an IP Checker.
// It allows to check whether addresses are in a given pool of IPs.
type PoolStrategy struct {
2018-08-24 17:20:03 +03:00
Checker * Checker
}
2021-06-07 18:46:14 +03:00
// GetIP checks the list of Forwarded IPs (most recent first) against the
// Checker pool of IPs. It returns the first IP that is not in the pool, or the
// empty string otherwise.
func ( s * PoolStrategy ) GetIP ( req * http . Request ) string {
2018-08-24 17:20:03 +03:00
if s . Checker == nil {
return ""
}
xff := req . Header . Get ( xForwardedFor )
xffs := strings . Split ( xff , "," )
for i := len ( xffs ) - 1 ; i >= 0 ; i -- {
2018-10-05 13:43:17 +03:00
xffTrimmed := strings . TrimSpace ( xffs [ i ] )
2021-06-07 18:46:14 +03:00
if len ( xffTrimmed ) == 0 {
continue
}
2018-10-05 13:43:17 +03:00
if contain , _ := s . Checker . Contains ( xffTrimmed ) ; ! contain {
return xffTrimmed
2018-08-24 17:20:03 +03:00
}
}
2021-06-07 18:46:14 +03:00
2018-08-24 17:20:03 +03:00
return ""
}
2024-09-24 19:04:05 +03:00
// getIPv6SubnetIP returns the IPv6 subnet IP.
// It returns the original IP when it is not an IPv6, or if parsing the IP has failed with an error.
func getIPv6SubnetIP ( ip string , ipv6Subnet int ) string {
addr , err := netip . ParseAddr ( ip )
if err != nil {
return ip
}
if ! addr . Is6 ( ) {
return ip
}
prefix , err := addr . Prefix ( ipv6Subnet )
if err != nil {
return ip
}
return prefix . Addr ( ) . String ( )
}