package acl import ( "bufio" lru "github.com/hashicorp/golang-lru" "net" "os" "strings" ) const entryCacheSize = 1024 type Engine struct { DefaultAction Action Entries []Entry Cache *lru.ARCCache } type cacheEntry struct { Action Action Arg string } func LoadFromFile(filename string) (*Engine, error) { f, err := os.Open(filename) if err != nil { return nil, err } defer f.Close() scanner := bufio.NewScanner(f) entries := make([]Entry, 0, 1024) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if len(line) == 0 || strings.HasPrefix(line, "#") { // Ignore empty lines & comments continue } entry, err := ParseEntry(line) if err != nil { return nil, err } entries = append(entries, entry) } cache, err := lru.NewARC(entryCacheSize) if err != nil { return nil, err } return &Engine{ DefaultAction: ActionProxy, Entries: entries, Cache: cache, }, nil } func (e *Engine) Lookup(domain string, ip net.IP) (Action, string) { if len(domain) > 0 { // Domain if v, ok := e.Cache.Get(domain); ok { // Cache hit ce := v.(cacheEntry) return ce.Action, ce.Arg } ips, _ := net.LookupIP(domain) for _, entry := range e.Entries { if entry.MatchDomain(domain) || (len(ips) > 0 && entry.MatchIPs(ips)) { e.Cache.Add(domain, cacheEntry{entry.Action, entry.ActionArg}) return entry.Action, entry.ActionArg } } e.Cache.Add(domain, cacheEntry{e.DefaultAction, ""}) return e.DefaultAction, "" } else if ip != nil { // IP if v, ok := e.Cache.Get(ip.String()); ok { // Cache hit ce := v.(cacheEntry) return ce.Action, ce.Arg } for _, entry := range e.Entries { if entry.MatchIP(ip) { e.Cache.Add(ip.String(), cacheEntry{entry.Action, entry.ActionArg}) return entry.Action, entry.ActionArg } } e.Cache.Add(ip.String(), cacheEntry{e.DefaultAction, ""}) return e.DefaultAction, "" } else { return e.DefaultAction, "" } }