2018-08-30 14:25:33 +00:00
package dnsfilter
import (
"bufio"
"errors"
"fmt"
"log"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
2018-10-15 13:02:19 +00:00
"github.com/AdguardTeam/AdGuardHome/dnsfilter"
2018-08-30 14:25:33 +00:00
"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/metrics"
"github.com/coredns/coredns/plugin/pkg/dnstest"
"github.com/coredns/coredns/plugin/pkg/upstream"
"github.com/coredns/coredns/request"
"github.com/mholt/caddy"
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/net/context"
)
var defaultSOA = & dns . SOA {
// values copied from verisign's nonexistent .com domain
// their exact values are not important in our use case because they are used for domain transfers between primary/secondary DNS servers
Refresh : 1800 ,
Retry : 900 ,
Expire : 604800 ,
Minttl : 86400 ,
}
func init ( ) {
caddy . RegisterPlugin ( "dnsfilter" , caddy . Plugin {
ServerType : "dns" ,
Action : setup ,
} )
}
2018-10-29 23:17:24 +00:00
type plugFilter struct {
2018-10-30 14:16:20 +00:00
ID int64
2018-10-29 23:17:24 +00:00
Path string
}
2018-10-07 18:24:22 +00:00
type plugSettings struct {
SafeBrowsingBlockHost string
ParentalBlockHost string
QueryLogEnabled bool
BlockedTTL uint32 // in seconds, default 3600
2018-10-29 23:17:24 +00:00
Filters [ ] plugFilter
2018-10-07 18:24:22 +00:00
}
2018-09-14 13:50:56 +00:00
type plug struct {
2018-08-30 14:25:33 +00:00
d * dnsfilter . Dnsfilter
Next plugin . Handler
upstream upstream . Upstream
2018-10-07 18:24:22 +00:00
settings plugSettings
2018-10-06 21:58:59 +00:00
sync . RWMutex
2018-08-30 14:25:33 +00:00
}
2018-10-07 18:24:22 +00:00
var defaultPluginSettings = plugSettings {
2018-08-30 14:25:33 +00:00
SafeBrowsingBlockHost : "safebrowsing.block.dns.adguard.com" ,
ParentalBlockHost : "family.block.dns.adguard.com" ,
2018-09-26 15:27:31 +00:00
BlockedTTL : 3600 , // in seconds
2018-10-29 23:17:24 +00:00
Filters : make ( [ ] plugFilter , 0 ) ,
2018-08-30 14:25:33 +00:00
}
//
// coredns handling functions
//
2018-09-14 13:50:56 +00:00
func setupPlugin ( c * caddy . Controller ) ( * plug , error ) {
2018-08-30 14:25:33 +00:00
// create new Plugin and copy default values
2018-10-07 18:24:22 +00:00
p := & plug {
settings : defaultPluginSettings ,
2018-11-30 10:47:26 +00:00
d : dnsfilter . New ( nil ) ,
2018-10-07 18:24:22 +00:00
}
2018-08-30 14:25:33 +00:00
2018-10-29 23:17:24 +00:00
log . Println ( "Initializing the CoreDNS plugin" )
2018-08-30 14:25:33 +00:00
for c . Next ( ) {
for c . NextBlock ( ) {
2018-10-29 23:17:24 +00:00
blockValue := c . Val ( )
switch blockValue {
2018-08-30 14:25:33 +00:00
case "safebrowsing" :
2018-10-30 09:57:16 +00:00
log . Println ( "Browsing security service is enabled" )
2018-11-30 10:32:51 +00:00
p . d . SafeBrowsingEnabled = true
2018-08-30 14:25:33 +00:00
if c . NextArg ( ) {
if len ( c . Val ( ) ) == 0 {
return nil , c . ArgErr ( )
}
2018-09-14 13:50:56 +00:00
p . d . SetSafeBrowsingServer ( c . Val ( ) )
2018-08-30 14:25:33 +00:00
}
case "safesearch" :
2018-10-30 09:57:16 +00:00
log . Println ( "Safe search is enabled" )
2018-11-30 10:32:51 +00:00
p . d . SafeSearchEnabled = true
2018-08-30 14:25:33 +00:00
case "parental" :
if ! c . NextArg ( ) {
return nil , c . ArgErr ( )
}
sensitivity , err := strconv . Atoi ( c . Val ( ) )
if err != nil {
return nil , c . ArgErr ( )
}
2018-10-30 09:57:16 +00:00
log . Println ( "Parental control is enabled" )
2018-11-30 10:32:51 +00:00
if ! dnsfilter . IsParentalSensitivityValid ( sensitivity ) {
return nil , dnsfilter . ErrInvalidParental
2018-08-30 14:25:33 +00:00
}
2018-11-30 10:32:51 +00:00
p . d . ParentalEnabled = true
p . d . ParentalSensitivity = sensitivity
2018-08-30 14:25:33 +00:00
if c . NextArg ( ) {
if len ( c . Val ( ) ) == 0 {
return nil , c . ArgErr ( )
}
2018-10-07 18:24:22 +00:00
p . settings . ParentalBlockHost = c . Val ( )
2018-08-30 14:25:33 +00:00
}
2018-09-26 15:27:31 +00:00
case "blocked_ttl" :
if ! c . NextArg ( ) {
return nil , c . ArgErr ( )
}
2018-10-30 09:57:16 +00:00
blockedTtl , err := strconv . ParseUint ( c . Val ( ) , 10 , 32 )
2018-09-26 15:27:31 +00:00
if err != nil {
return nil , c . ArgErr ( )
}
2018-10-30 09:57:16 +00:00
log . Printf ( "Blocked request TTL is %d" , blockedTtl )
p . settings . BlockedTTL = uint32 ( blockedTtl )
2018-08-30 14:25:33 +00:00
case "querylog" :
2018-10-30 09:57:16 +00:00
log . Println ( "Query log is enabled" )
2018-10-07 18:24:22 +00:00
p . settings . QueryLogEnabled = true
2018-10-29 23:17:24 +00:00
case "filter" :
if ! c . NextArg ( ) {
return nil , c . ArgErr ( )
}
2018-10-30 14:16:20 +00:00
filterId , err := strconv . ParseInt ( c . Val ( ) , 10 , 64 )
2018-10-29 23:17:24 +00:00
if err != nil {
return nil , c . ArgErr ( )
}
if ! c . NextArg ( ) {
return nil , c . ArgErr ( )
}
filterPath := c . Val ( )
// Initialize filter and add it to the list
p . settings . Filters = append ( p . settings . Filters , plugFilter {
2018-10-30 14:16:20 +00:00
ID : filterId ,
2018-10-29 23:17:24 +00:00
Path : filterPath ,
} )
2018-08-30 14:25:33 +00:00
}
}
}
2018-10-29 23:17:24 +00:00
for _ , filter := range p . settings . Filters {
log . Printf ( "Loading rules from %s" , filter . Path )
2018-09-25 16:26:26 +00:00
2018-10-29 23:17:24 +00:00
file , err := os . Open ( filter . Path )
2018-08-30 14:25:33 +00:00
if err != nil {
return nil , err
}
2018-09-25 16:26:26 +00:00
defer file . Close ( )
count := 0
scanner := bufio . NewScanner ( file )
for scanner . Scan ( ) {
text := scanner . Text ( )
2018-10-29 12:46:58 +00:00
2018-10-29 23:17:24 +00:00
err = p . d . AddRule ( text , filter . ID )
2018-10-29 13:17:18 +00:00
if err == dnsfilter . ErrAlreadyExists || err == dnsfilter . ErrInvalidSyntax {
2018-09-25 16:26:26 +00:00
continue
}
if err != nil {
2018-10-29 13:17:18 +00:00
log . Printf ( "Cannot add rule %s: %s" , text , err )
// Just ignore invalid rules
continue
2018-09-25 16:26:26 +00:00
}
count ++
}
2018-10-30 08:01:09 +00:00
log . Printf ( "Added %d rules from filter ID=%d" , count , filter . ID )
2018-08-30 14:25:33 +00:00
2018-09-25 16:26:26 +00:00
if err = scanner . Err ( ) ; err != nil {
return nil , err
}
2018-08-30 14:25:33 +00:00
}
2018-10-09 01:45:05 +00:00
log . Printf ( "Loading stats from querylog" )
err := fillStatsFromQueryLog ( )
2018-10-07 20:24:04 +00:00
if err != nil {
2018-10-09 01:45:05 +00:00
log . Printf ( "Failed to load stats from querylog: %s" , err )
2018-10-07 20:24:04 +00:00
return nil , err
}
if p . settings . QueryLogEnabled {
onceQueryLog . Do ( func ( ) {
2018-10-17 15:55:27 +00:00
go periodicQueryLogRotate ( )
go periodicHourlyTopRotate ( )
go statsRotator ( )
2018-10-07 20:24:04 +00:00
} )
}
2018-10-11 17:52:23 +00:00
onceHook . Do ( func ( ) {
caddy . RegisterEventHook ( "dnsfilter-reload" , hook )
} )
2018-09-14 13:50:56 +00:00
p . upstream , err = upstream . New ( nil )
2018-08-30 14:25:33 +00:00
if err != nil {
return nil , err
}
2018-09-14 13:50:56 +00:00
return p , nil
2018-08-30 14:25:33 +00:00
}
func setup ( c * caddy . Controller ) error {
2018-09-14 13:50:56 +00:00
p , err := setupPlugin ( c )
2018-08-30 14:25:33 +00:00
if err != nil {
return err
}
config := dnsserver . GetConfig ( c )
config . AddPlugin ( func ( next plugin . Handler ) plugin . Handler {
2018-09-14 13:50:56 +00:00
p . Next = next
return p
2018-08-30 14:25:33 +00:00
} )
c . OnStartup ( func ( ) error {
2018-09-05 23:07:57 +00:00
m := dnsserver . GetConfig ( c ) . Handler ( "prometheus" )
if m == nil {
return nil
}
if x , ok := m . ( * metrics . Metrics ) ; ok {
x . MustRegister ( requests )
x . MustRegister ( filtered )
x . MustRegister ( filteredLists )
x . MustRegister ( filteredSafebrowsing )
x . MustRegister ( filteredParental )
x . MustRegister ( whitelisted )
x . MustRegister ( safesearch )
x . MustRegister ( errorsTotal )
2018-10-09 01:45:05 +00:00
x . MustRegister ( elapsedTime )
2018-09-14 13:50:56 +00:00
x . MustRegister ( p )
2018-09-05 23:07:57 +00:00
}
2018-08-30 14:25:33 +00:00
return nil
} )
2018-09-14 13:50:56 +00:00
c . OnShutdown ( p . onShutdown )
2018-10-06 21:51:44 +00:00
c . OnFinalShutdown ( p . onFinalShutdown )
2018-08-30 14:25:33 +00:00
return nil
}
2018-09-14 13:50:56 +00:00
func ( p * plug ) onShutdown ( ) error {
2018-10-06 21:58:59 +00:00
p . Lock ( )
2018-09-14 13:50:56 +00:00
p . d . Destroy ( )
p . d = nil
2018-10-06 21:58:59 +00:00
p . Unlock ( )
2018-08-30 14:25:33 +00:00
return nil
}
2018-10-06 21:51:44 +00:00
func ( p * plug ) onFinalShutdown ( ) error {
2018-10-06 21:58:59 +00:00
logBufferLock . Lock ( )
2018-10-06 21:51:44 +00:00
err := flushToFile ( logBuffer )
if err != nil {
log . Printf ( "failed to flush to file: %s" , err )
return err
}
2018-10-06 21:58:59 +00:00
logBufferLock . Unlock ( )
2018-10-06 21:51:44 +00:00
return nil
}
2018-08-30 14:25:33 +00:00
type statsFunc func ( ch interface { } , name string , text string , value float64 , valueType prometheus . ValueType )
func doDesc ( ch interface { } , name string , text string , value float64 , valueType prometheus . ValueType ) {
realch , ok := ch . ( chan <- * prometheus . Desc )
2018-09-14 13:50:56 +00:00
if ! ok {
2018-08-30 14:25:33 +00:00
log . Printf ( "Couldn't convert ch to chan<- *prometheus.Desc\n" )
return
}
realch <- prometheus . NewDesc ( name , text , nil , nil )
}
func doMetric ( ch interface { } , name string , text string , value float64 , valueType prometheus . ValueType ) {
realch , ok := ch . ( chan <- prometheus . Metric )
2018-09-14 13:50:56 +00:00
if ! ok {
2018-08-30 14:25:33 +00:00
log . Printf ( "Couldn't convert ch to chan<- prometheus.Metric\n" )
return
}
desc := prometheus . NewDesc ( name , text , nil , nil )
realch <- prometheus . MustNewConstMetric ( desc , valueType , value )
}
func gen ( ch interface { } , doFunc statsFunc , name string , text string , value float64 , valueType prometheus . ValueType ) {
doFunc ( ch , name , text , value , valueType )
}
2018-09-07 13:10:11 +00:00
func doStatsLookup ( ch interface { } , doFunc statsFunc , name string , lookupstats * dnsfilter . LookupStats ) {
gen ( ch , doFunc , fmt . Sprintf ( "coredns_dnsfilter_%s_requests" , name ) , fmt . Sprintf ( "Number of %s HTTP requests that were sent" , name ) , float64 ( lookupstats . Requests ) , prometheus . CounterValue )
gen ( ch , doFunc , fmt . Sprintf ( "coredns_dnsfilter_%s_cachehits" , name ) , fmt . Sprintf ( "Number of %s lookups that didn't need HTTP requests" , name ) , float64 ( lookupstats . CacheHits ) , prometheus . CounterValue )
gen ( ch , doFunc , fmt . Sprintf ( "coredns_dnsfilter_%s_pending" , name ) , fmt . Sprintf ( "Number of currently pending %s HTTP requests" , name ) , float64 ( lookupstats . Pending ) , prometheus . GaugeValue )
gen ( ch , doFunc , fmt . Sprintf ( "coredns_dnsfilter_%s_pending_max" , name ) , fmt . Sprintf ( "Maximum number of pending %s HTTP requests" , name ) , float64 ( lookupstats . PendingMax ) , prometheus . GaugeValue )
}
2018-09-14 13:50:56 +00:00
func ( p * plug ) doStats ( ch interface { } , doFunc statsFunc ) {
2018-10-06 21:58:59 +00:00
p . RLock ( )
2018-09-14 13:50:56 +00:00
stats := p . d . GetStats ( )
2018-09-07 13:10:11 +00:00
doStatsLookup ( ch , doFunc , "safebrowsing" , & stats . Safebrowsing )
doStatsLookup ( ch , doFunc , "parental" , & stats . Parental )
2018-10-06 21:58:59 +00:00
p . RUnlock ( )
2018-08-30 14:25:33 +00:00
}
2018-09-14 13:50:56 +00:00
// Describe is called by prometheus handler to know stat types
func ( p * plug ) Describe ( ch chan <- * prometheus . Desc ) {
p . doStats ( ch , doDesc )
2018-08-30 14:25:33 +00:00
}
2018-09-14 13:50:56 +00:00
// Collect is called by prometheus handler to collect stats
func ( p * plug ) Collect ( ch chan <- prometheus . Metric ) {
p . doStats ( ch , doMetric )
2018-08-30 14:25:33 +00:00
}
2018-09-14 13:50:56 +00:00
func ( p * plug ) replaceHostWithValAndReply ( ctx context . Context , w dns . ResponseWriter , r * dns . Msg , host string , val string , question dns . Question ) ( int , error ) {
2018-08-30 14:25:33 +00:00
// check if it's a domain name or IP address
addr := net . ParseIP ( val )
var records [ ] dns . RR
2018-10-11 15:33:07 +00:00
// log.Println("Will give", val, "instead of", host) // debug logging
2018-08-30 14:25:33 +00:00
if addr != nil {
// this is an IP address, return it
2018-10-07 18:24:22 +00:00
result , err := dns . NewRR ( fmt . Sprintf ( "%s %d A %s" , host , p . settings . BlockedTTL , val ) )
2018-08-30 14:25:33 +00:00
if err != nil {
log . Printf ( "Got error %s\n" , err )
return dns . RcodeServerFailure , fmt . Errorf ( "plugin/dnsfilter: %s" , err )
}
records = append ( records , result )
} else {
// this is a domain name, need to look it up
2018-11-02 09:15:30 +00:00
req := new ( dns . Msg )
req . SetQuestion ( dns . Fqdn ( val ) , question . Qtype )
req . RecursionDesired = true
reqstate := request . Request { W : w , Req : req , Context : ctx }
result , err := p . upstream . Lookup ( reqstate , dns . Fqdn ( val ) , reqstate . QType ( ) )
if err != nil {
log . Printf ( "Got error %s\n" , err )
return dns . RcodeServerFailure , fmt . Errorf ( "plugin/dnsfilter: %s" , err )
}
if result != nil {
for _ , answer := range result . Answer {
answer . Header ( ) . Name = question . Name
2018-08-30 14:25:33 +00:00
}
2018-11-02 09:15:30 +00:00
records = result . Answer
2018-08-30 14:25:33 +00:00
}
}
m := new ( dns . Msg )
m . SetReply ( r )
m . Authoritative , m . RecursionAvailable , m . Compress = true , true , true
m . Answer = append ( m . Answer , records ... )
state := request . Request { W : w , Req : r , Context : ctx }
state . SizeAndDo ( m )
err := state . W . WriteMsg ( m )
if err != nil {
log . Printf ( "Got error %s\n" , err )
return dns . RcodeServerFailure , fmt . Errorf ( "plugin/dnsfilter: %s" , err )
}
return dns . RcodeSuccess , nil
}
// generate SOA record that makes DNS clients cache NXdomain results
// the only value that is important is TTL in header, other values like refresh, retry, expire and minttl are irrelevant
2018-09-26 15:27:31 +00:00
func ( p * plug ) genSOA ( r * dns . Msg ) [ ] dns . RR {
2018-08-30 14:25:33 +00:00
zone := r . Question [ 0 ] . Name
2018-10-07 18:24:22 +00:00
header := dns . RR_Header { Name : zone , Rrtype : dns . TypeSOA , Ttl : p . settings . BlockedTTL , Class : dns . ClassINET }
2018-08-30 14:25:33 +00:00
Mbox := "hostmaster."
if zone [ 0 ] != '.' {
Mbox += zone
}
Ns := "fake-for-negative-caching.adguard.com."
2018-10-06 21:58:59 +00:00
soa := * defaultSOA
2018-08-30 14:25:33 +00:00
soa . Hdr = header
soa . Mbox = Mbox
soa . Ns = Ns
2018-10-06 21:58:59 +00:00
soa . Serial = 100500 // faster than uint32(time.Now().Unix())
return [ ] dns . RR { & soa }
2018-08-30 14:25:33 +00:00
}
2018-09-26 15:27:31 +00:00
func ( p * plug ) writeNXdomain ( ctx context . Context , w dns . ResponseWriter , r * dns . Msg ) ( int , error ) {
2018-08-30 14:25:33 +00:00
state := request . Request { W : w , Req : r , Context : ctx }
m := new ( dns . Msg )
m . SetRcode ( state . Req , dns . RcodeNameError )
m . Authoritative , m . RecursionAvailable , m . Compress = true , true , true
2018-09-26 15:27:31 +00:00
m . Ns = p . genSOA ( r )
2018-08-30 14:25:33 +00:00
state . SizeAndDo ( m )
err := state . W . WriteMsg ( m )
if err != nil {
log . Printf ( "Got error %s\n" , err )
return dns . RcodeServerFailure , err
}
return dns . RcodeNameError , nil
}
2018-09-14 13:50:56 +00:00
func ( p * plug ) serveDNSInternal ( ctx context . Context , w dns . ResponseWriter , r * dns . Msg ) ( int , dnsfilter . Result , error ) {
2018-08-30 14:25:33 +00:00
if len ( r . Question ) != 1 {
// google DNS, bind and others do the same
2018-10-29 23:17:24 +00:00
return dns . RcodeFormatError , dnsfilter . Result { } , fmt . Errorf ( "got a DNS request with more than one Question" )
2018-08-30 14:25:33 +00:00
}
for _ , question := range r . Question {
host := strings . ToLower ( strings . TrimSuffix ( question . Name , "." ) )
// is it a safesearch domain?
2018-10-06 21:58:59 +00:00
p . RLock ( )
2018-09-14 13:50:56 +00:00
if val , ok := p . d . SafeSearchDomain ( host ) ; ok {
rcode , err := p . replaceHostWithValAndReply ( ctx , w , r , host , val , question )
2018-08-30 14:25:33 +00:00
if err != nil {
2018-10-06 21:58:59 +00:00
p . RUnlock ( )
2018-09-14 13:50:56 +00:00
return rcode , dnsfilter . Result { } , err
2018-08-30 14:25:33 +00:00
}
2018-10-06 21:58:59 +00:00
p . RUnlock ( )
2018-09-14 13:50:56 +00:00
return rcode , dnsfilter . Result { Reason : dnsfilter . FilteredSafeSearch } , err
2018-08-30 14:25:33 +00:00
}
2018-10-06 21:58:59 +00:00
p . RUnlock ( )
2018-08-30 14:25:33 +00:00
// needs to be filtered instead
2018-10-06 21:58:59 +00:00
p . RLock ( )
2018-09-14 13:50:56 +00:00
result , err := p . d . CheckHost ( host )
2018-08-30 14:25:33 +00:00
if err != nil {
log . Printf ( "plugin/dnsfilter: %s\n" , err )
2018-10-06 21:58:59 +00:00
p . RUnlock ( )
2018-09-14 13:50:56 +00:00
return dns . RcodeServerFailure , dnsfilter . Result { } , fmt . Errorf ( "plugin/dnsfilter: %s" , err )
2018-08-30 14:25:33 +00:00
}
2018-10-06 21:58:59 +00:00
p . RUnlock ( )
2018-08-30 14:25:33 +00:00
2018-09-05 23:09:57 +00:00
if result . IsFiltered {
switch result . Reason {
case dnsfilter . FilteredSafeBrowsing :
// return cname safebrowsing.block.dns.adguard.com
2018-10-07 18:24:22 +00:00
val := p . settings . SafeBrowsingBlockHost
2018-09-14 13:50:56 +00:00
rcode , err := p . replaceHostWithValAndReply ( ctx , w , r , host , val , question )
2018-09-05 23:09:57 +00:00
if err != nil {
2018-09-14 13:50:56 +00:00
return rcode , dnsfilter . Result { } , err
2018-09-05 23:09:57 +00:00
}
2018-09-14 13:50:56 +00:00
return rcode , result , err
2018-09-05 23:09:57 +00:00
case dnsfilter . FilteredParental :
// return cname family.block.dns.adguard.com
2018-10-07 18:24:22 +00:00
val := p . settings . ParentalBlockHost
2018-09-14 13:50:56 +00:00
rcode , err := p . replaceHostWithValAndReply ( ctx , w , r , host , val , question )
2018-09-05 23:09:57 +00:00
if err != nil {
2018-09-14 13:50:56 +00:00
return rcode , dnsfilter . Result { } , err
2018-09-05 23:09:57 +00:00
}
2018-09-14 13:50:56 +00:00
return rcode , result , err
2018-09-05 23:09:57 +00:00
case dnsfilter . FilteredBlackList :
2018-10-29 12:46:58 +00:00
if result . Ip == nil {
// return NXDomain
rcode , err := p . writeNXdomain ( ctx , w , r )
if err != nil {
return rcode , dnsfilter . Result { } , err
}
return rcode , result , err
} else {
// This is a hosts-syntax rule
rcode , err := p . replaceHostWithValAndReply ( ctx , w , r , host , result . Ip . String ( ) , question )
if err != nil {
return rcode , dnsfilter . Result { } , err
}
return rcode , result , err
2018-09-05 23:09:57 +00:00
}
2018-10-05 04:31:56 +00:00
case dnsfilter . FilteredInvalid :
// return NXdomain
rcode , err := p . writeNXdomain ( ctx , w , r )
if err != nil {
return rcode , dnsfilter . Result { } , err
}
return rcode , result , err
2018-09-05 23:09:57 +00:00
default :
2018-10-05 04:31:56 +00:00
log . Printf ( "SHOULD NOT HAPPEN -- got unknown reason for filtering host \"%s\": %v, %+v" , host , result . Reason , result )
2018-08-30 14:25:33 +00:00
}
2018-09-05 23:09:57 +00:00
} else {
switch result . Reason {
case dnsfilter . NotFilteredWhiteList :
2018-09-14 13:50:56 +00:00
rcode , err := plugin . NextOrFailure ( p . Name ( ) , p . Next , ctx , w , r )
return rcode , result , err
2018-09-05 23:09:57 +00:00
case dnsfilter . NotFilteredNotFound :
// do nothing, pass through to lower code
default :
2018-10-05 04:31:56 +00:00
log . Printf ( "SHOULD NOT HAPPEN -- got unknown reason for not filtering host \"%s\": %v, %+v" , host , result . Reason , result )
2018-08-30 14:25:33 +00:00
}
}
}
2018-09-14 13:50:56 +00:00
rcode , err := plugin . NextOrFailure ( p . Name ( ) , p . Next , ctx , w , r )
return rcode , dnsfilter . Result { } , err
2018-08-30 14:25:33 +00:00
}
2018-09-14 13:50:56 +00:00
// ServeDNS handles the DNS request and refuses if it's in filterlists
func ( p * plug ) ServeDNS ( ctx context . Context , w dns . ResponseWriter , r * dns . Msg ) ( int , error ) {
2018-08-30 14:25:33 +00:00
start := time . Now ( )
requests . Inc ( )
2018-08-31 16:59:04 +00:00
state := request . Request { W : w , Req : r }
ip := state . IP ( )
2018-08-30 14:25:33 +00:00
// capture the written answer
rrw := dnstest . NewRecorder ( w )
2018-09-14 13:50:56 +00:00
rcode , result , err := p . serveDNSInternal ( ctx , rrw , r )
2018-08-30 14:25:33 +00:00
if rcode > 0 {
// actually send the answer if we have one
answer := new ( dns . Msg )
answer . SetRcode ( r , rcode )
state . SizeAndDo ( answer )
2018-09-14 13:50:56 +00:00
err = w . WriteMsg ( answer )
if err != nil {
return dns . RcodeServerFailure , err
}
2018-08-30 14:25:33 +00:00
}
// increment counters
switch {
case err != nil :
errorsTotal . Inc ( )
case result . Reason == dnsfilter . FilteredBlackList :
filtered . Inc ( )
filteredLists . Inc ( )
case result . Reason == dnsfilter . FilteredSafeBrowsing :
filtered . Inc ( )
filteredSafebrowsing . Inc ( )
case result . Reason == dnsfilter . FilteredParental :
filtered . Inc ( )
filteredParental . Inc ( )
case result . Reason == dnsfilter . FilteredInvalid :
filtered . Inc ( )
filteredInvalid . Inc ( )
case result . Reason == dnsfilter . FilteredSafeSearch :
// the request was passsed through but not filtered, don't increment filtered
safesearch . Inc ( )
case result . Reason == dnsfilter . NotFilteredWhiteList :
whitelisted . Inc ( )
case result . Reason == dnsfilter . NotFilteredNotFound :
// do nothing
case result . Reason == dnsfilter . NotFilteredError :
text := "SHOULD NOT HAPPEN: got DNSFILTER_NOTFILTERED_ERROR without err != nil!"
log . Println ( text )
err = errors . New ( text )
rcode = dns . RcodeServerFailure
}
// log
2018-10-09 01:45:05 +00:00
elapsed := time . Since ( start )
elapsedTime . Observe ( elapsed . Seconds ( ) )
2018-10-07 18:24:22 +00:00
if p . settings . QueryLogEnabled {
2018-09-05 18:21:46 +00:00
logRequest ( r , rrw . Msg , result , time . Since ( start ) , ip )
2018-08-30 14:25:33 +00:00
}
return rcode , err
}
2018-09-14 13:50:56 +00:00
// Name returns name of the plugin as seen in Corefile and plugin.cfg
func ( p * plug ) Name ( ) string { return "dnsfilter" }
2018-08-30 14:25:33 +00:00
2018-10-11 17:52:23 +00:00
var onceHook sync . Once
2018-09-05 23:07:57 +00:00
var onceQueryLog sync . Once