2018-01-10 09:04:03 +01:00
package main
import (
2018-01-17 02:40:47 +01:00
"errors"
2018-01-14 23:47:49 +01:00
"net"
2019-07-14 19:46:11 +02:00
"strings"
2018-01-10 18:32:05 +01:00
"sync"
2019-05-26 21:16:47 +02:00
"time"
2018-01-10 18:32:05 +01:00
2018-01-17 02:40:47 +01:00
"github.com/jedisct1/dlog"
2018-01-10 09:04:03 +01:00
"github.com/miekg/dns"
)
type PluginsAction int
const (
PluginsActionNone = 0
PluginsActionForward = 1
2018-01-10 10:11:59 +01:00
PluginsActionDrop = 2
PluginsActionReject = 3
2018-01-10 17:23:20 +01:00
PluginsActionSynth = 4
2018-01-10 09:04:03 +01:00
)
2018-01-15 23:07:41 +01:00
type PluginsGlobals struct {
sync . RWMutex
2019-02-23 00:58:25 +01:00
queryPlugins * [ ] Plugin
responsePlugins * [ ] Plugin
loggingPlugins * [ ] Plugin
refusedCodeInResponses bool
2019-07-14 19:46:11 +02:00
respondWithIPv4 net . IP
respondWithIPv6 net . IP
2018-01-15 23:07:41 +01:00
}
2018-06-04 23:18:28 +02:00
type PluginsReturnCode int
const (
PluginsReturnCodePass = iota
PluginsReturnCodeForward
PluginsReturnCodeDrop
PluginsReturnCodeReject
PluginsReturnCodeSynth
PluginsReturnCodeParseError
PluginsReturnCodeNXDomain
PluginsReturnCodeResponseError
PluginsReturnCodeServerError
2019-03-02 18:01:21 +01:00
PluginsReturnCodeCloak
2019-10-19 10:15:41 +02:00
PluginsReturnCodeServerTimeout
2018-06-04 23:18:28 +02:00
)
var PluginsReturnCodeToString = map [ PluginsReturnCode ] string {
PluginsReturnCodePass : "PASS" ,
PluginsReturnCodeForward : "FORWARD" ,
PluginsReturnCodeDrop : "DROP" ,
PluginsReturnCodeReject : "REJECT" ,
PluginsReturnCodeSynth : "SYNTH" ,
PluginsReturnCodeParseError : "PARSE_ERROR" ,
PluginsReturnCodeNXDomain : "NXDOMAIN" ,
PluginsReturnCodeResponseError : "RESPONSE_ERROR" ,
PluginsReturnCodeServerError : "SERVER_ERROR" ,
2019-03-02 18:01:21 +01:00
PluginsReturnCodeCloak : "CLOAK" ,
2019-10-19 10:15:41 +02:00
PluginsReturnCodeServerTimeout : "SERVER_TIMEOUT" ,
2018-06-04 23:18:28 +02:00
}
2018-01-10 09:04:03 +01:00
type PluginsState struct {
2019-10-12 21:15:39 +02:00
sessionData map [ string ] interface { }
action PluginsAction
maxUnencryptedUDPSafePayloadSize int
originalMaxPayloadSize int
maxPayloadSize int
clientProto string
clientAddr * net . Addr
synthResponse * dns . Msg
dnssec bool
cacheSize int
cacheNegMinTTL uint32
cacheNegMaxTTL uint32
cacheMinTTL uint32
cacheMaxTTL uint32
2019-10-21 18:26:49 +02:00
rejectTTL uint32
2019-10-12 21:15:39 +02:00
questionMsg * dns . Msg
requestStart time . Time
requestEnd time . Time
cacheHit bool
returnCode PluginsReturnCode
serverName string
2018-01-10 10:11:59 +01:00
}
2019-10-09 17:59:46 +02:00
func ( proxy * Proxy ) InitPluginsGlobals ( ) error {
2018-01-10 17:23:20 +01:00
queryPlugins := & [ ] Plugin { }
2019-09-07 16:19:47 +02:00
if len ( proxy . queryMeta ) != 0 {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginQueryMeta ) ) )
}
2018-04-07 23:02:40 +02:00
if len ( proxy . whitelistNameFile ) != 0 {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginWhitelistName ) ) )
}
2019-09-07 11:00:18 +02:00
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginFirefox ) ) )
2018-01-17 02:40:47 +01:00
if len ( proxy . blockNameFile ) != 0 {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginBlockName ) ) )
}
2018-01-10 17:23:20 +01:00
if proxy . pluginBlockIPv6 {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginBlockIPv6 ) ) )
}
2018-02-04 01:43:37 +01:00
if len ( proxy . cloakFile ) != 0 {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginCloak ) ) )
}
2018-01-10 17:23:20 +01:00
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginGetSetPayloadSize ) ) )
2018-01-10 18:53:09 +01:00
if proxy . cache {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginCache ) ) )
}
2018-01-17 09:44:03 +01:00
if len ( proxy . forwardFile ) != 0 {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginForward ) ) )
}
2018-01-20 16:59:40 +01:00
2018-01-10 10:11:59 +01:00
responsePlugins := & [ ] Plugin { }
2018-01-20 16:59:40 +01:00
if len ( proxy . nxLogFile ) != 0 {
* responsePlugins = append ( * responsePlugins , Plugin ( new ( PluginNxLog ) ) )
}
2018-01-21 16:07:44 +01:00
if len ( proxy . blockIPFile ) != 0 {
* responsePlugins = append ( * responsePlugins , Plugin ( new ( PluginBlockIP ) ) )
}
if proxy . cache {
* responsePlugins = append ( * responsePlugins , Plugin ( new ( PluginCacheResponse ) ) )
}
2018-06-04 20:52:16 +02:00
loggingPlugins := & [ ] Plugin { }
if len ( proxy . queryLogFile ) != 0 {
* loggingPlugins = append ( * loggingPlugins , Plugin ( new ( PluginQueryLog ) ) )
}
2018-01-15 23:07:41 +01:00
for _ , plugin := range * queryPlugins {
if err := plugin . Init ( proxy ) ; err != nil {
return err
}
}
for _ , plugin := range * responsePlugins {
if err := plugin . Init ( proxy ) ; err != nil {
return err
}
}
2018-06-04 20:52:16 +02:00
for _ , plugin := range * loggingPlugins {
if err := plugin . Init ( proxy ) ; err != nil {
return err
}
}
2018-01-15 23:07:41 +01:00
2019-10-09 17:59:46 +02:00
proxy . pluginsGlobals . queryPlugins = queryPlugins
proxy . pluginsGlobals . responsePlugins = responsePlugins
proxy . pluginsGlobals . loggingPlugins = loggingPlugins
2019-06-19 18:11:06 +02:00
2019-10-09 17:59:46 +02:00
parseBlockedQueryResponse ( proxy . blockedQueryResponse , & proxy . pluginsGlobals )
2019-07-14 19:46:11 +02:00
return nil
}
// blockedQueryResponse can be 'refused', 'hinfo' or IP responses 'a:IPv4,aaaa:IPv6
2019-10-12 22:18:10 +02:00
func parseBlockedQueryResponse ( blockedResponse string , pluginsGlobals * PluginsGlobals ) {
2019-10-12 22:22:28 +02:00
blockedResponse = StringStripSpaces ( strings . ToLower ( blockedResponse ) )
2019-07-14 19:46:11 +02:00
2019-10-12 22:18:10 +02:00
if strings . HasPrefix ( blockedResponse , "a:" ) {
blockedIPStrings := strings . Split ( blockedResponse , "," )
2019-07-14 19:46:11 +02:00
( * pluginsGlobals ) . respondWithIPv4 = net . ParseIP ( strings . TrimPrefix ( blockedIPStrings [ 0 ] , "a:" ) )
if ( * pluginsGlobals ) . respondWithIPv4 == nil {
dlog . Notice ( "Error parsing IPv4 response given in blocked_query_response option, defaulting to `hinfo`" )
( * pluginsGlobals ) . refusedCodeInResponses = false
return
}
if len ( blockedIPStrings ) > 1 {
if strings . HasPrefix ( blockedIPStrings [ 1 ] , "aaaa:" ) {
ipv6Response := strings . TrimPrefix ( blockedIPStrings [ 1 ] , "aaaa:" )
if strings . HasPrefix ( ipv6Response , "[" ) {
ipv6Response = strings . Trim ( ipv6Response , "[]" )
}
( * pluginsGlobals ) . respondWithIPv6 = net . ParseIP ( ipv6Response )
if ( * pluginsGlobals ) . respondWithIPv6 == nil {
dlog . Notice ( "Error parsing IPv6 response given in blocked_query_response option, defaulting to IPv4" )
}
} else {
dlog . Noticef ( "Invalid IPv6 response given in blocked_query_response option [%s], the option should take the form 'a:<IPv4>,aaaa:<IPv6>'" , blockedIPStrings [ 1 ] )
}
}
if ( * pluginsGlobals ) . respondWithIPv6 == nil {
( * pluginsGlobals ) . respondWithIPv6 = ( * pluginsGlobals ) . respondWithIPv4
}
} else {
2019-10-12 22:18:10 +02:00
switch blockedResponse {
2019-06-19 18:11:06 +02:00
case "refused" :
( * pluginsGlobals ) . refusedCodeInResponses = true
case "hinfo" :
( * pluginsGlobals ) . refusedCodeInResponses = false
default :
2019-10-12 22:18:10 +02:00
dlog . Noticef ( "Invalid blocked_query_response option [%s], defaulting to `hinfo`" , blockedResponse )
2019-06-19 18:11:06 +02:00
( * pluginsGlobals ) . refusedCodeInResponses = false
}
}
2018-01-15 23:07:41 +01:00
}
type Plugin interface {
Name ( ) string
Description ( ) string
Init ( proxy * Proxy ) error
Drop ( ) error
Reload ( ) error
Eval ( pluginsState * PluginsState , msg * dns . Msg ) error
}
2019-05-26 21:16:47 +02:00
func NewPluginsState ( proxy * Proxy , clientProto string , clientAddr * net . Addr , start time . Time ) PluginsState {
2018-01-10 18:32:05 +01:00
return PluginsState {
2019-10-12 21:15:39 +02:00
action : PluginsActionForward ,
maxPayloadSize : MaxDNSUDPPacketSize - ResponseOverhead ,
clientProto : clientProto ,
clientAddr : clientAddr ,
cacheSize : proxy . cacheSize ,
cacheNegMinTTL : proxy . cacheNegMinTTL ,
cacheNegMaxTTL : proxy . cacheNegMaxTTL ,
cacheMinTTL : proxy . cacheMinTTL ,
cacheMaxTTL : proxy . cacheMaxTTL ,
2019-10-21 18:26:49 +02:00
rejectTTL : proxy . rejectTTL ,
2019-10-12 21:15:39 +02:00
questionMsg : nil ,
requestStart : start ,
maxUnencryptedUDPSafePayloadSize : MaxDNSUDPSafePacketSize ,
2018-01-10 18:32:05 +01:00
}
2018-01-10 09:04:03 +01:00
}
2019-06-03 18:51:21 +02:00
func ( pluginsState * PluginsState ) ApplyQueryPlugins ( pluginsGlobals * PluginsGlobals , packet [ ] byte , serverName string ) ( [ ] byte , error ) {
2018-06-04 21:35:07 +02:00
if len ( * pluginsGlobals . queryPlugins ) == 0 && len ( * pluginsGlobals . loggingPlugins ) == 0 {
2018-01-10 18:32:05 +01:00
return packet , nil
}
2019-06-03 18:51:21 +02:00
pluginsState . serverName = serverName
2018-01-10 10:11:59 +01:00
pluginsState . action = PluginsActionForward
2018-01-10 09:04:03 +01:00
msg := dns . Msg { }
if err := msg . Unpack ( packet ) ; err != nil {
return packet , err
}
2018-01-17 02:40:47 +01:00
if len ( msg . Question ) > 1 {
return packet , errors . New ( "Unexpected number of questions" )
}
2018-06-04 20:52:16 +02:00
pluginsState . questionMsg = & msg
2018-01-15 23:07:41 +01:00
pluginsGlobals . RLock ( )
2019-10-09 18:32:14 +02:00
defer pluginsGlobals . RUnlock ( )
2018-01-15 23:07:41 +01:00
for _ , plugin := range * pluginsGlobals . queryPlugins {
2019-10-15 16:06:46 +02:00
if err := plugin . Eval ( pluginsState , & msg ) ; err != nil {
2018-01-10 10:11:59 +01:00
pluginsState . action = PluginsActionDrop
2019-10-15 16:06:46 +02:00
return packet , err
2018-01-10 10:11:59 +01:00
}
2018-01-17 02:40:47 +01:00
if pluginsState . action == PluginsActionReject {
2019-11-01 16:37:33 +01:00
synth := RefusedResponseFromMessage ( & msg , pluginsGlobals . refusedCodeInResponses , pluginsGlobals . respondWithIPv4 , pluginsGlobals . respondWithIPv6 , pluginsState . rejectTTL )
2018-01-17 02:40:47 +01:00
pluginsState . synthResponse = synth
}
2018-01-10 17:23:20 +01:00
if pluginsState . action != PluginsActionForward {
break
}
2018-01-10 09:04:03 +01:00
}
packet2 , err := msg . PackBuffer ( packet )
if err != nil {
return packet , err
}
return packet2 , nil
}
2018-02-04 13:35:40 +01:00
func ( pluginsState * PluginsState ) ApplyResponsePlugins ( pluginsGlobals * PluginsGlobals , packet [ ] byte , ttl * uint32 ) ( [ ] byte , error ) {
2018-06-04 21:35:07 +02:00
if len ( * pluginsGlobals . responsePlugins ) == 0 && len ( * pluginsGlobals . loggingPlugins ) == 0 {
2018-01-10 18:32:05 +01:00
return packet , nil
}
pluginsState . action = PluginsActionForward
msg := dns . Msg { }
if err := msg . Unpack ( packet ) ; err != nil {
2018-02-23 17:05:58 +01:00
if len ( packet ) >= MinDNSPacketSize && HasTCFlag ( packet ) {
err = nil
}
2018-01-10 18:32:05 +01:00
return packet , err
}
2018-06-04 23:18:28 +02:00
switch Rcode ( packet ) {
case dns . RcodeSuccess :
pluginsState . returnCode = PluginsReturnCodePass
case dns . RcodeNameError :
pluginsState . returnCode = PluginsReturnCodeNXDomain
case dns . RcodeServerFailure :
pluginsState . returnCode = PluginsReturnCodeServerError
default :
pluginsState . returnCode = PluginsReturnCodeResponseError
}
2018-01-15 23:07:41 +01:00
pluginsGlobals . RLock ( )
2019-10-09 18:32:14 +02:00
defer pluginsGlobals . RUnlock ( )
2018-01-15 23:07:41 +01:00
for _ , plugin := range * pluginsGlobals . responsePlugins {
2019-10-15 16:06:46 +02:00
if err := plugin . Eval ( pluginsState , & msg ) ; err != nil {
2018-01-10 18:32:05 +01:00
pluginsState . action = PluginsActionDrop
2019-10-15 16:06:46 +02:00
return packet , err
2018-01-10 18:32:05 +01:00
}
2018-01-17 02:40:47 +01:00
if pluginsState . action == PluginsActionReject {
2019-11-01 16:37:33 +01:00
synth := RefusedResponseFromMessage ( & msg , pluginsGlobals . refusedCodeInResponses , pluginsGlobals . respondWithIPv4 , pluginsGlobals . respondWithIPv6 , pluginsState . rejectTTL )
2018-01-17 02:40:47 +01:00
dlog . Infof ( "Blocking [%s]" , synth . Question [ 0 ] . Name )
pluginsState . synthResponse = synth
}
2018-01-10 18:32:05 +01:00
if pluginsState . action != PluginsActionForward {
break
}
}
2018-02-04 13:35:40 +01:00
if ttl != nil {
setMaxTTL ( & msg , * ttl )
}
2018-01-10 18:32:05 +01:00
packet2 , err := msg . PackBuffer ( packet )
if err != nil {
return packet , err
}
return packet2 , nil
}
2018-06-04 20:52:16 +02:00
func ( pluginsState * PluginsState ) ApplyLoggingPlugins ( pluginsGlobals * PluginsGlobals ) error {
if len ( * pluginsGlobals . loggingPlugins ) == 0 {
return nil
}
2019-05-26 21:16:47 +02:00
pluginsState . requestEnd = time . Now ( )
2018-06-04 20:52:16 +02:00
questionMsg := pluginsState . questionMsg
if questionMsg == nil || len ( questionMsg . Question ) > 1 {
return errors . New ( "Unexpected number of questions" )
}
pluginsGlobals . RLock ( )
2019-10-09 18:32:14 +02:00
defer pluginsGlobals . RUnlock ( )
2018-06-04 20:52:16 +02:00
for _ , plugin := range * pluginsGlobals . loggingPlugins {
2019-10-15 16:06:46 +02:00
if err := plugin . Eval ( pluginsState , questionMsg ) ; err != nil {
return err
2018-06-04 20:52:16 +02:00
}
}
return nil
}