diff --git a/dnscrypt-proxy/config.go b/dnscrypt-proxy/config.go index 1ad84b25..807d971e 100644 --- a/dnscrypt-proxy/config.go +++ b/dnscrypt-proxy/config.go @@ -17,6 +17,7 @@ type Config struct { ForceTCP bool `toml:"force_tcp"` Timeout int `toml:"timeout_ms"` CertRefreshDelay int `toml:"cert_refresh_delay"` + BlockIPv6 bool `toml:"block_ipv6"` ServersConfig map[string]ServerConfig `toml:"servers"` } @@ -56,6 +57,7 @@ func ConfigLoad(proxy *Proxy, config_file string) error { } proxy.listenAddresses = config.ListenAddresses proxy.daemonize = config.Daemonize + proxy.pluginBlockIPv6 = config.BlockIPv6 if len(config.ServerNames) == 0 { for serverName := range config.ServersConfig { config.ServerNames = append(config.ServerNames, serverName) diff --git a/dnscrypt-proxy/dnscrypt-proxy.toml b/dnscrypt-proxy/dnscrypt-proxy.toml index 68395326..bb785f1b 100644 --- a/dnscrypt-proxy/dnscrypt-proxy.toml +++ b/dnscrypt-proxy/dnscrypt-proxy.toml @@ -40,6 +40,13 @@ timeout = 2500 cert_refresh_delay = 30 +############## Filters ############## + +## Immediately respond to IPv6-related queries with an empty response +## This makes things faster when there is no IPv6 connectivity + +block_ipv6 = false + ############## Servers ############## diff --git a/dnscrypt-proxy/dnsutils.go b/dnscrypt-proxy/dnsutils.go index d1f5ae90..893d14d1 100644 --- a/dnscrypt-proxy/dnsutils.go +++ b/dnscrypt-proxy/dnsutils.go @@ -18,6 +18,15 @@ func TruncatedResponse(packet []byte) ([]byte, error) { return dstMsg.Pack() } +func EmptyResponseFromMessage(srcMsg *dns.Msg) (*dns.Msg, error) { + dstMsg := srcMsg + dstMsg.Response = true + dstMsg.Answer = make([]dns.RR, 0) + dstMsg.Ns = make([]dns.RR, 0) + dstMsg.Extra = make([]dns.RR, 0) + return dstMsg, nil +} + func HasTCFlag(packet []byte) bool { return packet[2]&2 == 2 } diff --git a/dnscrypt-proxy/main.go b/dnscrypt-proxy/main.go index 4c8482c9..c41737d0 100644 --- a/dnscrypt-proxy/main.go +++ b/dnscrypt-proxy/main.go @@ -23,6 +23,7 @@ type Proxy struct { listenAddresses []string daemonize bool registeredServers []RegisteredServer + pluginBlockIPv6 bool } func main() { @@ -163,22 +164,33 @@ func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, serverProto str if clientAddr == nil { clientProto = "tcp" } - pluginsState := NewPluginsState(clientProto) + pluginsState := NewPluginsState(proxy, clientProto) query, _ = pluginsState.ApplyQueryPlugins(query) - encryptedQuery, clientNonce, err := proxy.Encrypt(serverInfo, query, serverProto) - if err != nil { - return - } - serverInfo.noticeBegin(proxy) var response []byte - if serverProto == "udp" { - response, err = proxy.exchangeWithUDPServer(serverInfo, encryptedQuery, clientNonce) - } else { - response, err = proxy.exchangeWithTCPServer(serverInfo, encryptedQuery, clientNonce) + var err error + if pluginsState.action != PluginsActionForward { + if pluginsState.synthResponse != nil { + response, err = pluginsState.synthResponse.PackBuffer(response) + if err != nil { + return + } + } } - if err != nil { - serverInfo.noticeFailure(proxy) - return + if len(response) == 0 { + encryptedQuery, clientNonce, err := proxy.Encrypt(serverInfo, query, serverProto) + if err != nil { + return + } + serverInfo.noticeBegin(proxy) + if serverProto == "udp" { + response, err = proxy.exchangeWithUDPServer(serverInfo, encryptedQuery, clientNonce) + } else { + response, err = proxy.exchangeWithTCPServer(serverInfo, encryptedQuery, clientNonce) + } + if err != nil { + serverInfo.noticeFailure(proxy) + return + } } if clientAddr != nil { if len(response) > MaxDNSUDPPacketSize { diff --git a/dnscrypt-proxy/plugins.go b/dnscrypt-proxy/plugins.go index 1f29fbfe..f6af0e14 100644 --- a/dnscrypt-proxy/plugins.go +++ b/dnscrypt-proxy/plugins.go @@ -11,6 +11,7 @@ const ( PluginsActionForward = 1 PluginsActionDrop = 2 PluginsActionReject = 3 + PluginsActionSynth = 4 ) type PluginsState struct { @@ -21,6 +22,7 @@ type PluginsState struct { proto string queryPlugins *[]Plugin responsePlugins *[]Plugin + synthResponse *dns.Msg } type Plugin interface { @@ -29,9 +31,15 @@ type Plugin interface { Eval(pluginsState *PluginsState, msg *dns.Msg) error } -func NewPluginsState(proto string) PluginsState { - queryPlugins := &[]Plugin{Plugin(new(PluginGetSetPayloadSize))} +func NewPluginsState(proxy *Proxy, proto string) PluginsState { + queryPlugins := &[]Plugin{} + if proxy.pluginBlockIPv6 { + *queryPlugins = append(*queryPlugins, Plugin(new(PluginBlockIPv6))) + } + *queryPlugins = append(*queryPlugins, Plugin(new(PluginGetSetPayloadSize))) + responsePlugins := &[]Plugin{} + return PluginsState{action: PluginsActionForward, maxPayloadSize: MaxDNSUDPPacketSize - ResponseOverhead, queryPlugins: queryPlugins, responsePlugins: responsePlugins, proto: proto} } @@ -47,6 +55,9 @@ func (pluginsState *PluginsState) ApplyQueryPlugins(packet []byte) ([]byte, erro pluginsState.action = PluginsActionDrop return packet, ret } + if pluginsState.action != PluginsActionForward { + break + } } packet2, err := msg.PackBuffer(packet) if err != nil { @@ -55,6 +66,8 @@ func (pluginsState *PluginsState) ApplyQueryPlugins(packet []byte) ([]byte, erro return packet2, nil } +// -------- get_set_payload_size plugin -------- + type PluginGetSetPayloadSize struct{} func (plugin *PluginGetSetPayloadSize) Name() string { @@ -86,3 +99,33 @@ func (plugin *PluginGetSetPayloadSize) Eval(pluginsState *PluginsState, msg *dns } return nil } + +// -------- block_ipv6 plugin -------- + +type PluginBlockIPv6 struct{} + +func (plugin *PluginBlockIPv6) Name() string { + return "block_ipv6" +} + +func (plugin *PluginBlockIPv6) Description() string { + return "Immediately return a synthetic response to AAAA queries" +} + +func (plugin *PluginBlockIPv6) Eval(pluginsState *PluginsState, msg *dns.Msg) error { + questions := msg.Question + if len(questions) != 1 { + return nil + } + question := questions[0] + if question.Qclass != dns.ClassINET || question.Qtype != dns.TypeAAAA { + return nil + } + synth, err := EmptyResponseFromMessage(msg) + if err != nil { + return err + } + pluginsState.synthResponse = synth + pluginsState.action = PluginsActionSynth + return nil +}