Files
elmprodvpn/selective-vpn-api/app/transport_policy_validate_normalize.go

75 lines
2.1 KiB
Go

package app
import (
"fmt"
"net/netip"
"strings"
)
func normalizeTransportIntent(in TransportPolicyIntent) (TransportPolicyIntent, string, netip.Prefix, error) {
out := TransportPolicyIntent{
SelectorType: strings.ToLower(strings.TrimSpace(in.SelectorType)),
SelectorValue: strings.TrimSpace(in.SelectorValue),
ClientID: sanitizeID(in.ClientID),
Priority: in.Priority,
Mode: strings.ToLower(strings.TrimSpace(in.Mode)),
}
if out.ClientID == "" {
return out, "", netip.Prefix{}, fmt.Errorf("missing client_id")
}
if out.Priority <= 0 {
out.Priority = 100
}
if out.Mode == "" {
out.Mode = "strict"
}
if out.Mode != "strict" && out.Mode != "fallback" {
return out, "", netip.Prefix{}, fmt.Errorf("mode must be strict|fallback")
}
switch out.SelectorType {
case "domain":
out.SelectorValue = strings.ToLower(out.SelectorValue)
case "cidr":
pfx, err := parseIntentCIDR(out.SelectorValue)
if err != nil {
return out, "", netip.Prefix{}, err
}
out.SelectorValue = pfx.String()
return out, out.SelectorType + ":" + out.SelectorValue, pfx, nil
case "app_key", "cgroup", "uid":
// keep as-is
default:
return out, "", netip.Prefix{}, fmt.Errorf("selector_type must be domain|cidr|app_key|cgroup|uid")
}
if out.SelectorValue == "" {
return out, "", netip.Prefix{}, fmt.Errorf("selector_value is empty")
}
return out, out.SelectorType + ":" + out.SelectorValue, netip.Prefix{}, nil
}
func parseIntentCIDR(raw string) (netip.Prefix, error) {
v := strings.TrimSpace(raw)
if v == "" {
return netip.Prefix{}, fmt.Errorf("selector_value is empty")
}
if strings.Contains(v, "/") {
pfx, err := netip.ParsePrefix(v)
if err != nil || !pfx.Addr().Is4() {
return netip.Prefix{}, fmt.Errorf("invalid cidr: %q", raw)
}
return pfx.Masked(), nil
}
ip, err := netip.ParseAddr(v)
if err != nil || !ip.Is4() {
return netip.Prefix{}, fmt.Errorf("invalid cidr: %q", raw)
}
return netip.PrefixFrom(ip, 32), nil
}
func prefixOverlap(a, b netip.Prefix) bool {
if !a.IsValid() || !b.IsValid() {
return false
}
return a.Contains(b.Addr()) || b.Contains(a.Addr())
}