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 (
2019-12-17 19:19:36 +01:00
PluginsActionNone = 0
PluginsActionContinue = 1
PluginsActionDrop = 2
PluginsActionReject = 3
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
2019-11-17 22:20:47 +01:00
PluginsReturnCodeServFail
2019-11-17 19:48:15 +01:00
PluginsReturnCodeNetworkError
2019-03-02 18:01:21 +01:00
PluginsReturnCodeCloak
2019-10-19 10:15:41 +02:00
PluginsReturnCodeServerTimeout
2021-01-03 18:09:03 +01:00
PluginsReturnCodeNotReady
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" ,
2019-11-17 22:20:47 +01:00
PluginsReturnCodeServFail : "SERVFAIL" ,
2019-11-17 19:48:15 +01:00
PluginsReturnCodeNetworkError : "NETWORK_ERROR" ,
2019-03-02 18:01:21 +01:00
PluginsReturnCodeCloak : "CLOAK" ,
2019-10-19 10:15:41 +02:00
PluginsReturnCodeServerTimeout : "SERVER_TIMEOUT" ,
2021-01-03 18:09:03 +01:00
PluginsReturnCodeNotReady : "NOT_READY" ,
2018-06-04 23:18:28 +02:00
}
2018-01-10 09:04:03 +01:00
type PluginsState struct {
2020-11-14 14:46:59 +01:00
requestStart time . Time
requestEnd time . Time
2019-10-12 21:15:39 +02:00
clientProto string
2020-11-14 14:46:59 +01:00
serverName string
serverProto string
qName string
2019-10-12 21:15:39 +02:00
clientAddr * net . Addr
synthResponse * dns . Msg
2020-11-14 14:46:59 +01:00
questionMsg * dns . Msg
sessionData map [ string ] interface { }
action PluginsAction
timeout time . Duration
returnCode PluginsReturnCode
maxPayloadSize int
2019-10-12 21:15:39 +02:00
cacheSize int
2020-11-14 14:46:59 +01:00
originalMaxPayloadSize int
maxUnencryptedUDPSafePayloadSize int
rejectTTL uint32
cacheMaxTTL uint32
2019-10-12 21:15:39 +02:00
cacheNegMaxTTL uint32
2020-11-14 14:46:59 +01:00
cacheNegMinTTL uint32
2019-10-12 21:15:39 +02:00
cacheMinTTL uint32
cacheHit bool
2020-11-14 14:46:59 +01:00
dnssec bool
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
2021-01-01 21:39:17 +01:00
if proxy . captivePortalMap != nil {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginCaptivePortal ) ) )
}
2019-09-07 16:19:47 +02:00
if len ( proxy . queryMeta ) != 0 {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginQueryMeta ) ) )
}
2021-03-30 11:02:47 +02:00
if len ( proxy . allowNameFile ) != 0 {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginAllowName ) ) )
2018-04-07 23:02:40 +02:00
}
2019-09-07 11:00:18 +02:00
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginFirefox ) ) )
2020-09-18 00:11:26 +02:00
if len ( proxy . ednsClientSubnets ) != 0 {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginECS ) ) )
}
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 ) ) )
}
2019-12-09 20:25:38 +01:00
if proxy . pluginBlockUnqualified {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginBlockUnqualified ) ) )
}
2019-12-16 16:13:18 +01:00
if proxy . pluginBlockUndelegated {
* queryPlugins = append ( * queryPlugins , Plugin ( new ( PluginBlockUndelegated ) ) )
}
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 ) ) )
}
2020-11-15 20:59:58 +01:00
if len ( proxy . allowedIPFile ) != 0 {
* responsePlugins = append ( * responsePlugins , Plugin ( new ( PluginAllowedIP ) ) )
}
2019-11-24 10:04:16 +01:00
if len ( proxy . blockNameFile ) != 0 {
* responsePlugins = append ( * responsePlugins , Plugin ( new ( PluginBlockNameResponse ) ) )
}
2018-01-21 16:07:44 +01:00
if len ( proxy . blockIPFile ) != 0 {
* responsePlugins = append ( * responsePlugins , Plugin ( new ( PluginBlockIP ) ) )
}
2020-06-08 18:23:03 +02:00
if len ( proxy . dns64Resolvers ) != 0 || len ( proxy . dns64Prefixes ) != 0 {
2020-06-19 11:37:53 +02:00
* responsePlugins = append ( * responsePlugins , Plugin ( new ( PluginDNS64 ) ) )
2020-06-08 18:23:03 +02:00
}
2018-01-21 16:07:44 +01:00
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 {
2022-03-23 17:48:48 +01:00
dlog . Notice (
"Error parsing IPv6 response given in blocked_query_response option, defaulting to IPv4" ,
)
2019-07-14 19:46:11 +02:00
}
} 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
}
2022-03-23 17:48:48 +01:00
func NewPluginsState (
proxy * Proxy ,
clientProto string ,
clientAddr * net . Addr ,
serverProto string ,
start time . Time ,
) PluginsState {
2018-01-10 18:32:05 +01:00
return PluginsState {
2019-12-17 19:19:36 +01:00
action : PluginsActionContinue ,
2019-12-17 18:54:49 +01:00
returnCode : PluginsReturnCodePass ,
2019-10-12 21:15:39 +02:00
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 ,
2019-12-17 09:38:53 +01:00
qName : "" ,
2020-03-13 17:50:58 +01:00
serverName : "-" ,
2020-04-05 20:49:30 +02:00
serverProto : serverProto ,
2020-04-17 20:57:23 +02:00
timeout : proxy . timeout ,
2019-10-12 21:15:39 +02:00
requestStart : start ,
maxUnencryptedUDPSafePayloadSize : MaxDNSUDPSafePacketSize ,
2020-01-30 13:15:29 +01:00
sessionData : make ( map [ string ] interface { } ) ,
2018-01-10 18:32:05 +01:00
}
2018-01-10 09:04:03 +01:00
}
2022-03-23 17:48:48 +01:00
func ( pluginsState * PluginsState ) ApplyQueryPlugins (
pluginsGlobals * PluginsGlobals ,
packet [ ] byte ,
needsEDNS0Padding bool ,
) ( [ ] byte , error ) {
2018-01-10 09:04:03 +01:00
msg := dns . Msg { }
if err := msg . Unpack ( packet ) ; err != nil {
return packet , err
}
2019-12-17 09:38:53 +01:00
if len ( msg . Question ) != 1 {
2018-01-17 02:40:47 +01:00
return packet , errors . New ( "Unexpected number of questions" )
}
2019-12-17 09:38:53 +01:00
qName , err := NormalizeQName ( msg . Question [ 0 ] . Name )
if err != nil {
return packet , err
}
2020-08-09 13:09:37 +02:00
dlog . Debugf ( "Handling query for [%v]" , qName )
2019-12-17 09:38:53 +01:00
pluginsState . qName = qName
2018-06-04 20:52:16 +02:00
pluginsState . questionMsg = & msg
2019-12-17 09:38:53 +01:00
if len ( * pluginsGlobals . queryPlugins ) == 0 && len ( * pluginsGlobals . loggingPlugins ) == 0 {
return packet , nil
}
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 {
2022-03-23 17:48:48 +01:00
synth := RefusedResponseFromMessage (
& msg ,
pluginsGlobals . refusedCodeInResponses ,
pluginsGlobals . respondWithIPv4 ,
pluginsGlobals . respondWithIPv6 ,
pluginsState . rejectTTL ,
)
2018-01-17 02:40:47 +01:00
pluginsState . synthResponse = synth
}
2019-12-17 19:19:36 +01:00
if pluginsState . action != PluginsActionContinue {
2018-01-10 17:23:20 +01:00
break
}
2018-01-10 09:04:03 +01:00
}
2020-09-18 00:11:26 +02:00
2018-01-10 09:04:03 +01:00
packet2 , err := msg . PackBuffer ( packet )
if err != nil {
return packet , err
}
2019-12-23 11:37:45 +01:00
if needsEDNS0Padding && pluginsState . action == PluginsActionContinue {
2019-12-23 23:17:46 +01:00
padLen := 63 - ( ( len ( packet2 ) + 63 ) & 63 )
2019-12-23 11:37:45 +01:00
if paddedPacket2 , _ := addEDNS0PaddingIfNoneFound ( & msg , packet2 , padLen ) ; paddedPacket2 != nil {
return paddedPacket2 , nil
}
}
2018-01-10 09:04:03 +01:00
return packet2 , nil
}
2022-03-23 17:48:48 +01:00
func ( pluginsState * PluginsState ) ApplyResponsePlugins (
pluginsGlobals * PluginsGlobals ,
packet [ ] byte ,
ttl * uint32 ,
) ( [ ] byte , error ) {
2019-11-18 01:13:18 +01:00
msg := dns . Msg { Compress : true }
2018-01-10 18:32:05 +01:00
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 :
2019-11-17 22:20:47 +01:00
pluginsState . returnCode = PluginsReturnCodeServFail
2018-06-04 23:18:28 +02:00
default :
pluginsState . returnCode = PluginsReturnCodeResponseError
}
2019-12-22 18:02:33 +01:00
removeEDNS0Options ( & 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 . 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 {
2022-03-23 17:48:48 +01:00
synth := RefusedResponseFromMessage (
& msg ,
pluginsGlobals . refusedCodeInResponses ,
pluginsGlobals . respondWithIPv4 ,
pluginsGlobals . respondWithIPv6 ,
pluginsState . rejectTTL ,
)
2018-01-17 02:40:47 +01:00
pluginsState . synthResponse = synth
}
2019-12-17 19:19:36 +01:00
if pluginsState . action != PluginsActionContinue {
2018-01-10 18:32:05 +01:00
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
2019-12-17 09:38:53 +01:00
if questionMsg == nil {
return errors . New ( "Question not found" )
2018-06-04 20:52:16 +02:00
}
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
}