2018-08-24 16:20:03 +02:00
package ip
import (
"errors"
"fmt"
"net"
2024-07-01 16:26:04 +02:00
"net/netip"
2018-08-24 16:20:03 +02:00
"strings"
)
2020-05-11 12:06:07 +02:00
// Checker allows to check that addresses are in a trusted IPs.
2018-08-24 16:20:03 +02:00
type Checker struct {
authorizedIPs [ ] * net . IP
authorizedIPsNet [ ] * net . IPNet
}
2020-05-11 12:06:07 +02:00
// NewChecker builds a new Checker given a list of CIDR-Strings to trusted IPs.
2018-08-24 16:20:03 +02:00
func NewChecker ( trustedIPs [ ] string ) ( * Checker , error ) {
if len ( trustedIPs ) == 0 {
return nil , errors . New ( "no trusted IPs provided" )
}
checker := & Checker { }
for _ , ipMask := range trustedIPs {
if ipAddr := net . ParseIP ( ipMask ) ; ipAddr != nil {
checker . authorizedIPs = append ( checker . authorizedIPs , & ipAddr )
2021-06-07 18:14:09 +02:00
continue
2018-08-24 16:20:03 +02:00
}
2021-06-07 18:14:09 +02:00
_ , ipAddr , err := net . ParseCIDR ( ipMask )
if err != nil {
return nil , fmt . Errorf ( "parsing CIDR trusted IPs %s: %w" , ipAddr , err )
}
checker . authorizedIPsNet = append ( checker . authorizedIPsNet , ipAddr )
2018-08-24 16:20:03 +02:00
}
return checker , nil
}
2020-05-11 12:06:07 +02:00
// IsAuthorized checks if provided request is authorized by the trusted IPs.
2018-08-24 16:20:03 +02:00
func ( ip * Checker ) IsAuthorized ( addr string ) error {
var invalidMatches [ ] string
host , _ , err := net . SplitHostPort ( addr )
if err != nil {
host = addr
}
ok , err := ip . Contains ( host )
if err != nil {
return err
}
if ! ok {
invalidMatches = append ( invalidMatches , addr )
return fmt . Errorf ( "%q matched none of the trusted IPs" , strings . Join ( invalidMatches , ", " ) )
}
return nil
}
2020-05-11 12:06:07 +02:00
// Contains checks if provided address is in the trusted IPs.
2018-08-24 16:20:03 +02:00
func ( ip * Checker ) Contains ( addr string ) ( bool , error ) {
2018-11-27 17:42:04 +01:00
if len ( addr ) == 0 {
2018-08-24 16:20:03 +02:00
return false , errors . New ( "empty IP address" )
}
ipAddr , err := parseIP ( addr )
if err != nil {
2020-05-11 12:06:07 +02:00
return false , fmt . Errorf ( "unable to parse address: %s: %w" , addr , err )
2018-08-24 16:20:03 +02:00
}
return ip . ContainsIP ( ipAddr ) , nil
}
2020-05-11 12:06:07 +02:00
// ContainsIP checks if provided address is in the trusted IPs.
2018-08-24 16:20:03 +02:00
func ( ip * Checker ) ContainsIP ( addr net . IP ) bool {
for _ , authorizedIP := range ip . authorizedIPs {
if authorizedIP . Equal ( addr ) {
return true
}
}
for _ , authorizedNet := range ip . authorizedIPsNet {
if authorizedNet . Contains ( addr ) {
return true
}
}
return false
}
func parseIP ( addr string ) ( net . IP , error ) {
2024-07-01 16:26:04 +02:00
parsedAddr , err := netip . ParseAddr ( addr )
if err != nil {
2018-08-24 16:20:03 +02:00
return nil , fmt . Errorf ( "can't parse IP from address %s" , addr )
}
2024-07-01 16:26:04 +02:00
ip := parsedAddr . As16 ( )
return ip [ : ] , nil
2018-08-24 16:20:03 +02:00
}