dnscrypt-proxy/dnscrypt-proxy/plugins.go

268 lines
7.3 KiB
Go
Raw Normal View History

package main
import (
"errors"
"net"
2018-01-10 18:32:05 +01:00
"sync"
"github.com/jedisct1/dlog"
"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
)
type PluginsGlobals struct {
sync.RWMutex
queryPlugins *[]Plugin
responsePlugins *[]Plugin
loggingPlugins *[]Plugin
refusedCodeInResponses bool
}
2018-06-04 23:18:28 +02:00
type PluginsReturnCode int
const (
PluginsReturnCodePass = iota
PluginsReturnCodeForward
PluginsReturnCodeDrop
PluginsReturnCodeReject
PluginsReturnCodeSynth
PluginsReturnCodeParseError
PluginsReturnCodeNXDomain
PluginsReturnCodeResponseError
PluginsReturnCodeServerError
)
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",
}
type PluginsState struct {
sessionData map[string]interface{}
action PluginsAction
originalMaxPayloadSize int
maxPayloadSize int
clientProto string
clientAddr *net.Addr
2018-01-10 17:23:20 +01:00
synthResponse *dns.Msg
2018-01-10 18:32:05 +01:00
dnssec bool
cacheSize int
cacheNegMinTTL uint32
cacheNegMaxTTL uint32
cacheMinTTL uint32
cacheMaxTTL uint32
questionMsg *dns.Msg
2018-06-04 23:18:28 +02:00
returnCode PluginsReturnCode
2018-01-10 10:11:59 +01:00
}
func InitPluginsGlobals(pluginsGlobals *PluginsGlobals, proxy *Proxy) error {
2018-01-10 17:23:20 +01:00
queryPlugins := &[]Plugin{}
2018-04-07 23:02:40 +02:00
if len(proxy.whitelistNameFile) != 0 {
*queryPlugins = append(*queryPlugins, Plugin(new(PluginWhitelistName)))
}
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)))
}
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-10 10:11:59 +01:00
responsePlugins := &[]Plugin{}
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)))
}
loggingPlugins := &[]Plugin{}
if len(proxy.queryLogFile) != 0 {
*loggingPlugins = append(*loggingPlugins, Plugin(new(PluginQueryLog)))
}
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
}
}
for _, plugin := range *loggingPlugins {
if err := plugin.Init(proxy); err != nil {
return err
}
}
(*pluginsGlobals).queryPlugins = queryPlugins
(*pluginsGlobals).responsePlugins = responsePlugins
(*pluginsGlobals).loggingPlugins = loggingPlugins
(*pluginsGlobals).refusedCodeInResponses = proxy.refusedCodeInResponses
return nil
}
type Plugin interface {
Name() string
Description() string
Init(proxy *Proxy) error
Drop() error
Reload() error
Eval(pluginsState *PluginsState, msg *dns.Msg) error
}
func NewPluginsState(proxy *Proxy, clientProto string, clientAddr *net.Addr) PluginsState {
2018-01-10 18:32:05 +01:00
return PluginsState{
action: PluginsActionForward,
maxPayloadSize: MaxDNSUDPPacketSize - ResponseOverhead,
clientProto: clientProto,
clientAddr: clientAddr,
cacheSize: proxy.cacheSize,
cacheNegMinTTL: proxy.cacheNegMinTTL,
cacheNegMaxTTL: proxy.cacheNegMaxTTL,
cacheMinTTL: proxy.cacheMinTTL,
cacheMaxTTL: proxy.cacheMaxTTL,
questionMsg: nil,
2018-01-10 18:32:05 +01:00
}
}
func (pluginsState *PluginsState) ApplyQueryPlugins(pluginsGlobals *PluginsGlobals, packet []byte) ([]byte, error) {
if len(*pluginsGlobals.queryPlugins) == 0 && len(*pluginsGlobals.loggingPlugins) == 0 {
2018-01-10 18:32:05 +01:00
return packet, nil
}
2018-01-10 10:11:59 +01:00
pluginsState.action = PluginsActionForward
msg := dns.Msg{}
if err := msg.Unpack(packet); err != nil {
return packet, err
}
if len(msg.Question) > 1 {
return packet, errors.New("Unexpected number of questions")
}
pluginsState.questionMsg = &msg
pluginsGlobals.RLock()
for _, plugin := range *pluginsGlobals.queryPlugins {
2018-01-10 10:11:59 +01:00
if ret := plugin.Eval(pluginsState, &msg); ret != nil {
pluginsGlobals.RUnlock()
2018-01-10 10:11:59 +01:00
pluginsState.action = PluginsActionDrop
return packet, ret
}
if pluginsState.action == PluginsActionReject {
synth, err := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses)
if err != nil {
return nil, err
}
pluginsState.synthResponse = synth
}
2018-01-10 17:23:20 +01:00
if pluginsState.action != PluginsActionForward {
break
}
}
pluginsGlobals.RUnlock()
packet2, err := msg.PackBuffer(packet)
if err != nil {
return packet, err
}
return packet2, nil
}
func (pluginsState *PluginsState) ApplyResponsePlugins(pluginsGlobals *PluginsGlobals, packet []byte, ttl *uint32) ([]byte, error) {
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 {
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
}
pluginsGlobals.RLock()
for _, plugin := range *pluginsGlobals.responsePlugins {
2018-01-10 18:32:05 +01:00
if ret := plugin.Eval(pluginsState, &msg); ret != nil {
pluginsGlobals.RUnlock()
2018-01-10 18:32:05 +01:00
pluginsState.action = PluginsActionDrop
return packet, ret
}
if pluginsState.action == PluginsActionReject {
synth, err := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses)
if err != nil {
return nil, err
}
dlog.Infof("Blocking [%s]", synth.Question[0].Name)
pluginsState.synthResponse = synth
}
2018-01-10 18:32:05 +01:00
if pluginsState.action != PluginsActionForward {
break
}
}
pluginsGlobals.RUnlock()
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
}
func (pluginsState *PluginsState) ApplyLoggingPlugins(pluginsGlobals *PluginsGlobals) error {
if len(*pluginsGlobals.loggingPlugins) == 0 {
return nil
}
questionMsg := pluginsState.questionMsg
if questionMsg == nil || len(questionMsg.Question) > 1 {
return errors.New("Unexpected number of questions")
}
pluginsGlobals.RLock()
for _, plugin := range *pluginsGlobals.loggingPlugins {
if ret := plugin.Eval(pluginsState, questionMsg); ret != nil {
pluginsGlobals.RUnlock()
return ret
}
}
pluginsGlobals.RUnlock()
return nil
}