github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/utils/domain/whitelist.go (about)

     1  /*
     2   * Copyright (C) 2021 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package domain
    19  
    20  import (
    21  	"strings"
    22  )
    23  
    24  // NormalizeHostname casts FQDN to canonical representation:
    25  // no whitespace, no trailing dot, lower case.
    26  func NormalizeHostname(hostname string) string {
    27  	return strings.ToLower(
    28  		strings.TrimRight(
    29  			strings.TrimSpace(hostname),
    30  			".",
    31  		),
    32  	)
    33  }
    34  
    35  // Whitelist is a set of domain names and suffixes for fast matching
    36  type Whitelist struct {
    37  	exactList  map[string]struct{}
    38  	suffixList map[string]struct{}
    39  }
    40  
    41  // NewWhitelist creates Whitelist from list of domains and suffixes
    42  func NewWhitelist(domainList []string) *Whitelist {
    43  	exactList := make(map[string]struct{})
    44  	suffixList := make(map[string]struct{})
    45  	for _, domain := range domainList {
    46  		domain = strings.TrimSpace(domain)
    47  		normalized := NormalizeHostname(domain)
    48  		if strings.Index(domain, ".") == 0 {
    49  			// suffix pattern
    50  			suffixList[strings.TrimLeft(normalized, ".")] = struct{}{}
    51  		} else {
    52  			// exact domain name
    53  			exactList[normalized] = struct{}{}
    54  		}
    55  	}
    56  	return &Whitelist{
    57  		exactList:  exactList,
    58  		suffixList: suffixList,
    59  	}
    60  }
    61  
    62  // Match returns whether hostname is present in whitelist
    63  func (l *Whitelist) Match(hostname string) bool {
    64  	hostname = NormalizeHostname(hostname)
    65  
    66  	// check for exact match
    67  	if _, found := l.exactList[hostname]; found {
    68  		return true
    69  	}
    70  
    71  	// handle special case of root suffix (".") added to whitelist
    72  	if _, found := l.suffixList[""]; found && hostname != "" {
    73  		return true
    74  	}
    75  
    76  	// check for suffix match
    77  	for needle := strings.Split(hostname, ".")[1:]; len(needle) > 0; needle = needle[1:] {
    78  		if _, found := l.suffixList[strings.Join(needle, ".")]; found {
    79  			return true
    80  		}
    81  	}
    82  
    83  	return false
    84  }