package querylog import ( "strings" "github.com/AdguardTeam/AdGuardHome/internal/dnsfilter" ) type criteriaType int const ( // ctDomainOrClient is for searching by the domain name, the client's IP // address, or the clinet's ID. ctDomainOrClient criteriaType = iota // ctFilteringStatus is for searching by the filtering status. // // See (*searchCriteria).ctFilteringStatusCase for details. ctFilteringStatus ) const ( filteringStatusAll = "all" filteringStatusFiltered = "filtered" // all kinds of filtering filteringStatusBlocked = "blocked" // blocked or blocked services filteringStatusBlockedService = "blocked_services" // blocked filteringStatusBlockedSafebrowsing = "blocked_safebrowsing" // blocked by safebrowsing filteringStatusBlockedParental = "blocked_parental" // blocked by parental control filteringStatusWhitelisted = "whitelisted" // whitelisted filteringStatusRewritten = "rewritten" // all kinds of rewrites filteringStatusSafeSearch = "safe_search" // enforced safe search filteringStatusProcessed = "processed" // not blocked, not white-listed entries ) // filteringStatusValues -- array with all possible filteringStatus values var filteringStatusValues = []string{ filteringStatusAll, filteringStatusFiltered, filteringStatusBlocked, filteringStatusBlockedService, filteringStatusBlockedSafebrowsing, filteringStatusBlockedParental, filteringStatusWhitelisted, filteringStatusRewritten, filteringStatusSafeSearch, filteringStatusProcessed, } // searchCriteria - every search request may contain a list of different search criteria // we use each of them to match the query type searchCriteria struct { value string // search criteria value criteriaType criteriaType // type of the criteria strict bool // should we strictly match (equality) or not (indexOf) } // quickMatch - quickly checks if the log entry matches this search criteria // the reason is to do it as quickly as possible without de-serializing the entry func (c *searchCriteria) quickMatch(line string) bool { // note that we do this only for a limited set of criteria switch c.criteriaType { case ctDomainOrClient: return c.quickMatchJSONValue(line, "QH") || c.quickMatchJSONValue(line, "IP") || c.quickMatchJSONValue(line, "ID") default: return true } } // quickMatchJSONValue - helper used by quickMatch func (c *searchCriteria) quickMatchJSONValue(line, propertyName string) bool { val := readJSONValue(line, propertyName) if len(val) == 0 { return false } val = strings.ToLower(val) searchVal := strings.ToLower(c.value) if c.strict && searchVal == val { return true } if !c.strict && strings.Contains(val, searchVal) { return true } return false } // match - checks if the log entry matches this search criteria func (c *searchCriteria) match(entry *logEntry) bool { switch c.criteriaType { case ctDomainOrClient: return c.ctDomainOrClientCase(entry) case ctFilteringStatus: return c.ctFilteringStatusCase(entry.Result) } return false } func (c *searchCriteria) ctDomainOrClientCase(entry *logEntry) bool { clientID := strings.ToLower(entry.ClientID) qhost := strings.ToLower(entry.QHost) searchVal := strings.ToLower(c.value) if c.strict && (qhost == searchVal || clientID == searchVal) { return true } if !c.strict && (strings.Contains(qhost, searchVal) || strings.Contains(clientID, searchVal)) { return true } ipStr := entry.IP.String() if c.strict && ipStr == c.value { return true } if !c.strict && strings.Contains(ipStr, c.value) { return true } return false } func (c *searchCriteria) ctFilteringStatusCase(res dnsfilter.Result) bool { switch c.value { case filteringStatusAll: return true case filteringStatusFiltered: return res.IsFiltered || res.Reason.In( dnsfilter.NotFilteredAllowList, dnsfilter.Rewritten, dnsfilter.RewrittenAutoHosts, dnsfilter.RewrittenRule, ) case filteringStatusBlocked: return res.IsFiltered && res.Reason.In(dnsfilter.FilteredBlockList, dnsfilter.FilteredBlockedService) case filteringStatusBlockedService: return res.IsFiltered && res.Reason == dnsfilter.FilteredBlockedService case filteringStatusBlockedParental: return res.IsFiltered && res.Reason == dnsfilter.FilteredParental case filteringStatusBlockedSafebrowsing: return res.IsFiltered && res.Reason == dnsfilter.FilteredSafeBrowsing case filteringStatusWhitelisted: return res.Reason == dnsfilter.NotFilteredAllowList case filteringStatusRewritten: return res.Reason.In( dnsfilter.Rewritten, dnsfilter.RewrittenAutoHosts, dnsfilter.RewrittenRule, ) case filteringStatusSafeSearch: return res.IsFiltered && res.Reason == dnsfilter.FilteredSafeSearch case filteringStatusProcessed: return !res.Reason.In( dnsfilter.FilteredBlockList, dnsfilter.FilteredBlockedService, dnsfilter.NotFilteredAllowList, ) default: return false } }