mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-27 20:56:44 +00:00
dynamic regex
This commit is contained in:
@@ -4,11 +4,45 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const maxDomains = 32
|
||||
|
||||
var domainRegex = regexp.MustCompile(`^(?:\*\.)?(?:(?:xn--)?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)(?:\.(?:xn--)?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$`)
|
||||
var regexCache = map[string]*regexp.Regexp{}
|
||||
var regexCacheMu sync.Mutex
|
||||
|
||||
func buildDomainRegex(allowWildcard, allowSingleToplevel bool) *regexp.Regexp {
|
||||
key := fmt.Sprintf("%t:%t", allowWildcard, allowSingleToplevel)
|
||||
|
||||
regexCacheMu.Lock()
|
||||
defer regexCacheMu.Unlock()
|
||||
|
||||
if re, ok := regexCache[key]; ok {
|
||||
return re
|
||||
}
|
||||
|
||||
var pattern strings.Builder
|
||||
pattern.WriteString("^")
|
||||
|
||||
if allowWildcard {
|
||||
pattern.WriteString(`(?:\*\.)?`)
|
||||
}
|
||||
|
||||
label := `(?:xn--)?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?`
|
||||
|
||||
if allowSingleToplevel {
|
||||
pattern.WriteString(label + `(?:\.` + label + `)*`)
|
||||
} else {
|
||||
pattern.WriteString(label + `(?:\.` + label + `)+`)
|
||||
}
|
||||
|
||||
pattern.WriteString("$")
|
||||
|
||||
re := regexp.MustCompile(pattern.String())
|
||||
regexCache[key] = re
|
||||
return re
|
||||
}
|
||||
|
||||
// ValidateDomains checks if each domain in the list is valid and returns a punycode-encoded DomainList.
|
||||
func ValidateDomains(domains []string) (List, error) {
|
||||
@@ -22,7 +56,7 @@ func ValidateDomains(domains []string) (List, error) {
|
||||
var domainList List
|
||||
|
||||
for _, d := range domains {
|
||||
validDomain, err := ToValidDomain(d)
|
||||
validDomain, err := ToValidDomain(d, false, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid domain %s: %w", d, err)
|
||||
}
|
||||
@@ -40,6 +74,8 @@ func ValidateDomainsList(domains []string) error {
|
||||
return fmt.Errorf("domains list exceeds maximum allowed domains: %d", maxDomains)
|
||||
}
|
||||
|
||||
domainRegex := buildDomainRegex(false, true)
|
||||
|
||||
for _, d := range domains {
|
||||
d := strings.ToLower(d)
|
||||
if !domainRegex.MatchString(d) {
|
||||
@@ -50,24 +86,26 @@ func ValidateDomainsList(domains []string) error {
|
||||
}
|
||||
|
||||
// IsValidDomain checks if the given domain is valid.
|
||||
func IsValidDomain(domain string) bool {
|
||||
func IsValidDomain(domain string, allowWildcard, allowSingleToplevel bool) bool {
|
||||
// handles length and idna conversion
|
||||
punycode, err := FromString(domain)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
domainRegex := buildDomainRegex(allowWildcard, allowSingleToplevel)
|
||||
return domainRegex.MatchString(string(punycode))
|
||||
}
|
||||
|
||||
// ToValidDomain converts a domain to a valid domain format.
|
||||
func ToValidDomain(domain string) (Domain, error) {
|
||||
func ToValidDomain(domain string, allowWildcard, allowSingleToplevel bool) (Domain, error) {
|
||||
// handles length and idna conversion
|
||||
punycode, err := FromString(domain)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("convert domain to punycode: %s: %w", domain, err)
|
||||
}
|
||||
|
||||
domainRegex := buildDomainRegex(allowWildcard, allowSingleToplevel)
|
||||
if !domainRegex.MatchString(string(punycode)) {
|
||||
return "", fmt.Errorf("invalid domain format: %s", domain)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user