Files
reproxy/vendor/github.com/didip/tollbooth/v7/libstring/libstring.go
2024-05-09 13:16:40 -05:00

102 lines
2.7 KiB
Go

// Package libstring provides various string related functions.
package libstring
import (
"net"
"net/http"
"strings"
)
// StringInSlice finds needle in a slice of strings.
func StringInSlice(sliceString []string, needle string) bool {
for _, b := range sliceString {
if b == needle {
return true
}
}
return false
}
// RemoteIP finds IP Address given http.Request struct.
func RemoteIP(ipLookups []string, forwardedForIndexFromBehind int, r *http.Request) string {
realIP := r.Header.Get("X-Real-IP")
forwardedFor := r.Header.Get("X-Forwarded-For")
for _, lookup := range ipLookups {
if lookup == "RemoteAddr" {
// 1. Cover the basic use cases for both ipv4 and ipv6
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
// 2. Upon error, just return the remote addr.
return r.RemoteAddr
}
return ip
}
if lookup == "X-Forwarded-For" && forwardedFor != "" {
// X-Forwarded-For is potentially a list of addresses separated with ","
parts := strings.Split(forwardedFor, ",")
for i, p := range parts {
parts[i] = strings.TrimSpace(p)
}
partIndex := len(parts) - 1 - forwardedForIndexFromBehind
if partIndex < 0 {
partIndex = 0
}
return parts[partIndex]
}
if lookup == "X-Real-IP" && realIP != "" {
return realIP
}
}
return ""
}
// CanonicalizeIP returns a form of ip suitable for comparison to other IPs.
// For IPv4 addresses, this is simply the whole string.
// For IPv6 addresses, this is the /64 prefix.
func CanonicalizeIP(ip string) string {
isIPv6 := false
// This is how net.ParseIP decides if an address is IPv6
// https://cs.opensource.google/go/go/+/refs/tags/go1.17.7:src/net/ip.go;l=704
for i := 0; !isIPv6 && i < len(ip); i++ {
switch ip[i] {
case '.':
// IPv4
return ip
case ':':
// IPv6
isIPv6 = true
}
}
if !isIPv6 {
// Not an IP address at all
return ip
}
// By default, the string representation of a net.IPNet (masked IP address) is just
// "full_address/mask_bits". But using that will result in different addresses with
// the same /64 prefix comparing differently. So we need to zero out the last 64 bits
// so that all IPs in the same prefix will be the same.
//
// Note: When 1.18 is the minimum Go version, this can be written more cleanly like:
// netip.PrefixFrom(netip.MustParseAddr(ipv6), 64).Masked().Addr().String()
// (With appropriate error checking.)
ipv6 := net.ParseIP(ip)
if ipv6 == nil {
return ip
}
const bytesToZero = (128 - 64) / 8
for i := len(ipv6) - bytesToZero; i < len(ipv6); i++ {
ipv6[i] = 0
}
// Note that this doesn't have the "/64" suffix customary with a CIDR representation,
// but those three bytes add nothing for us.
return ipv6.String()
}