gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/renter/hostdb/hosttree/addressfilter.go (about)

     1  package hosttree
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  
     7  	"gitlab.com/SiaPrime/SiaPrime/modules"
     8  )
     9  
    10  const (
    11  	// IPv4FilterRange is the number of bits within an IP address (starting
    12  	// from the left) which have to be unique for the host not to be filtered.
    13  	IPv4FilterRange = 24
    14  	// IPv6FilterRange is the number of bits within an IP address (starting
    15  	// from the left) which have to be unique for the host not to be filtered.
    16  	IPv6FilterRange = 54
    17  )
    18  
    19  // Filter filters host addresses which belong to the same subnet to
    20  // avoid selecting hosts from the same region.
    21  type Filter struct {
    22  	filter   map[string]struct{}
    23  	resolver modules.Resolver
    24  }
    25  
    26  // NewFilter creates a new addressFilter object.
    27  func NewFilter(resolver modules.Resolver) *Filter {
    28  	return &Filter{
    29  		filter:   make(map[string]struct{}),
    30  		resolver: resolver,
    31  	}
    32  }
    33  
    34  // Add adds a host to the filter. This will resolve the hostname into one
    35  // or more IP addresses, extract the subnets used by those addresses and
    36  // add the subnets to the filter. Add doesn't return an error, but if the
    37  // addresses of a host can't be resolved it will be handled as if the host
    38  // had no addresses associated with it.
    39  func (af *Filter) Add(host modules.NetAddress) {
    40  	// Translate the hostname to one or multiple IPs. If the argument is an IP
    41  	// address LookupIP will just return that IP.
    42  	addresses, err := af.resolver.LookupIP(host.Host())
    43  	if err != nil {
    44  		return
    45  	}
    46  	// If any of the addresses is blocked we ignore the host.
    47  	for _, ip := range addresses {
    48  		// Set the filterRange according to the type of IP address.
    49  		var filterRange int
    50  		if ip.To4() != nil {
    51  			filterRange = IPv4FilterRange
    52  		} else {
    53  			filterRange = IPv6FilterRange
    54  		}
    55  		// Get the subnet.
    56  		_, ipnet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", ip.String(), filterRange))
    57  		if err != nil {
    58  			continue
    59  		}
    60  		// Add the subnet to the map.
    61  		af.filter[ipnet.String()] = struct{}{}
    62  	}
    63  }
    64  
    65  // Filtered checks if a host uses a subnet that is already in use by a host
    66  // that was previously added to the filter. If it is in use, or if the host is
    67  // associated with 2 addresses of the same type (e.g. IPv4 and IPv4) or if it
    68  // is associated with more than 2 addresses, Filtered will return 'true'.
    69  func (af *Filter) Filtered(host modules.NetAddress) bool {
    70  	// Translate the hostname to one or multiple IPs. If the argument is an IP
    71  	// address LookupIP will just return that IP.
    72  	addresses, err := af.resolver.LookupIP(host.Host())
    73  	if err != nil {
    74  		return true
    75  	}
    76  	// If the hostname is associated with more than 2 addresses we filter it
    77  	if len(addresses) > 2 {
    78  		return true
    79  	}
    80  	// If the hostname is associated with 2 addresses of the same type, we
    81  	// filter it.
    82  	if (len(addresses) == 2) && (len(addresses[0]) == len(addresses[1])) {
    83  		return true
    84  	}
    85  	// If any of the addresses is blocked we ignore the host.
    86  	for _, ip := range addresses {
    87  		// Set the filterRange according to the type of IP address.
    88  		var filterRange int
    89  		if ip.To4() != nil {
    90  			filterRange = IPv4FilterRange
    91  		} else {
    92  			filterRange = IPv6FilterRange
    93  		}
    94  
    95  		// Get the subnet.
    96  		_, ipnet, err := net.ParseCIDR(fmt.Sprintf("%s/%d", ip.String(), filterRange))
    97  		if err != nil {
    98  			continue
    99  		}
   100  		// Check if the subnet is in the map. If it is, we filter the host.
   101  		if _, exists := af.filter[ipnet.String()]; exists {
   102  			return true
   103  		}
   104  	}
   105  	return false
   106  }
   107  
   108  // Reset clears the filter's contents.
   109  func (af *Filter) Reset() {
   110  	af.filter = make(map[string]struct{})
   111  }