filter.go 3.31 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
package multiaddr

import (
	"net"
	"sync"
)

// Action is an enum modelling all possible filter actions.
type Action int32

const (
	ActionNone Action = iota // zero value.
	ActionAccept
	ActionDeny
)

type filterEntry struct {
	f      net.IPNet
	action Action
}

// Filters is a structure representing a collection of accept/deny
// net.IPNet filters, together with the DefaultAction flag, which
// represents the default filter policy.
//
// Note that the last policy added to the Filters is authoritative.
type Filters struct {
	DefaultAction Action

	mu      sync.RWMutex
	filters []*filterEntry
}

// NewFilters constructs and returns a new set of net.IPNet filters.
// By default, the new filter accepts all addresses.
func NewFilters() *Filters {
	return &Filters{
		DefaultAction: ActionAccept,
		filters:       make([]*filterEntry, 0),
	}
}

func (fs *Filters) find(ipnet net.IPNet) (int, *filterEntry) {
	s := ipnet.String()
	for idx, ft := range fs.filters {
		if ft.f.String() == s {
			return idx, ft
		}
	}
	return -1, nil
}

// AddFilter adds a rule to the Filters set, enforcing the desired action for
// the provided IPNet mask.
func (fs *Filters) AddFilter(ipnet net.IPNet, action Action) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	if _, f := fs.find(ipnet); f != nil {
		f.action = action
	} else {
		fs.filters = append(fs.filters, &filterEntry{ipnet, action})
	}
}

// RemoveLiteral removes the first filter associated with the supplied IPNet,
// returning whether something was removed or not. It makes no distinction
// between whether the rule is an accept or a deny.
func (fs *Filters) RemoveLiteral(ipnet net.IPNet) (removed bool) {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	if idx, _ := fs.find(ipnet); idx != -1 {
		fs.filters = append(fs.filters[:idx], fs.filters[idx+1:]...)
		return true
	}
	return false
}

// AddrBlocked parses a ma.Multiaddr and, if a valid netip is found, it applies the
// Filter set rules, returning true if the given address should be denied, and false if
// the given address is accepted.
//
// If a parsing error occurs, or no filter matches, the Filters'
// default is returned.
//
// TODO: currently, the last filter to match wins always, but it shouldn't be that way.
//  Instead, the highest-specific last filter should win; that way more specific filters
//  override more general ones.
func (fs *Filters) AddrBlocked(a Multiaddr) (deny bool) {
	var (
		netip net.IP
		found bool
	)

	ForEach(a, func(c Component) bool {
		switch c.Protocol().Code {
		case P_IP6ZONE:
			return true
		case P_IP6, P_IP4:
			found = true
			netip = net.IP(c.RawValue())
			return false
		default:
			return false
		}
	})

	if !found {
		return fs.DefaultAction == ActionDeny
	}

	fs.mu.RLock()
	defer fs.mu.RUnlock()

	action := fs.DefaultAction
	for _, ft := range fs.filters {
		if ft.f.Contains(netip) {
			action = ft.action
		}
	}

	return action == ActionDeny
}

func (fs *Filters) ActionForFilter(ipnet net.IPNet) (action Action, ok bool) {
	if _, f := fs.find(ipnet); f != nil {
		return f.action, true
	}
	return ActionNone, false
}

// FiltersForAction returns the filters associated with the indicated action.
func (fs *Filters) FiltersForAction(action Action) (result []net.IPNet) {
	fs.mu.RLock()
	defer fs.mu.RUnlock()

	for _, ff := range fs.filters {
		if ff.action == action {
			result = append(result, ff.f)
		}
	}
	return result
}