diff --git a/dnscrypt-proxy/dnsutils.go b/dnscrypt-proxy/dnsutils.go index 22c3421e..61d92903 100644 --- a/dnscrypt-proxy/dnsutils.go +++ b/dnscrypt-proxy/dnsutils.go @@ -32,7 +32,7 @@ func EmptyResponseFromMessage(srcMsg *dns.Msg) (*dns.Msg, error) { return dstMsg, nil } -func RefusedResponseFromMessage(srcMsg *dns.Msg, refusedCode bool, ip net.IP, ttl uint32) (*dns.Msg, error) { +func RefusedResponseFromMessage(srcMsg *dns.Msg, refusedCode bool, ipv4 net.IP, ipv6 net.IP, ttl uint32) (*dns.Msg, error) { dstMsg, err := EmptyResponseFromMessage(srcMsg) if err != nil { return dstMsg, err @@ -46,24 +46,21 @@ func RefusedResponseFromMessage(srcMsg *dns.Msg, refusedCode bool, ip net.IP, tt question := questions[0] sendHInfoResponse := true - if ip != nil { - if question.Qtype == dns.TypeA { - rr := new(dns.A) - rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl} - rr.A = ip.To4() - if rr.A != nil { - dstMsg.Answer = []dns.RR{rr} - sendHInfoResponse = false - } - - } else if question.Qtype == dns.TypeAAAA { - rr := new(dns.AAAA) - rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl} - rr.AAAA = ip.To16() - if rr.AAAA != nil { - dstMsg.Answer = []dns.RR{rr} - sendHInfoResponse = false - } + if ipv4 != nil && question.Qtype == dns.TypeA { + rr := new(dns.A) + rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: ttl} + rr.A = ipv4.To4() + if rr.A != nil { + dstMsg.Answer = []dns.RR{rr} + sendHInfoResponse = false + } + } else if ipv6 != nil && question.Qtype == dns.TypeAAAA { + rr := new(dns.AAAA) + rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl} + rr.AAAA = ipv6.To16() + if rr.AAAA != nil { + dstMsg.Answer = []dns.RR{rr} + sendHInfoResponse = false } } diff --git a/dnscrypt-proxy/example-dnscrypt-proxy.toml b/dnscrypt-proxy/example-dnscrypt-proxy.toml index 4897bba7..3add791a 100644 --- a/dnscrypt-proxy/example-dnscrypt-proxy.toml +++ b/dnscrypt-proxy/example-dnscrypt-proxy.toml @@ -112,9 +112,9 @@ keepalive = 30 ## Response for blocked queries. Options are `refused`, `hinfo` (default) or -## an IP address (e.g. local pixelserv-tls server). Using the `hinfo` option -## means that some responses will be lies. Unfortunately, the `hinfo` option -## appears to be required for Android 8+ +## an IP response. To give an IP response, use the format `a:,aaaa:`. +## Using the `hinfo` option means that some responses will be lies. +## Unfortunately, the `hinfo` option appears to be required for Android 8+ # blocked_query_response = 'refused' diff --git a/dnscrypt-proxy/plugins.go b/dnscrypt-proxy/plugins.go index 59232ea3..25c3b44d 100644 --- a/dnscrypt-proxy/plugins.go +++ b/dnscrypt-proxy/plugins.go @@ -3,6 +3,7 @@ package main import ( "errors" "net" + "strings" "sync" "time" @@ -26,7 +27,8 @@ type PluginsGlobals struct { responsePlugins *[]Plugin loggingPlugins *[]Plugin refusedCodeInResponses bool - respondWithIP net.IP + respondWithIPv4 net.IP + respondWithIPv6 net.IP } type PluginsReturnCode int @@ -137,21 +139,56 @@ func InitPluginsGlobals(pluginsGlobals *PluginsGlobals, proxy *Proxy) error { (*pluginsGlobals).responsePlugins = responsePlugins (*pluginsGlobals).loggingPlugins = loggingPlugins - // blockedQueryResponse can be 'refused', 'hinfo' or an IP address - (*pluginsGlobals).respondWithIP = net.ParseIP(proxy.blockedQueryResponse) - if (*pluginsGlobals).respondWithIP == nil { - switch proxy.blockedQueryResponse { + parseBlockedQueryResponse(proxy.blockedQueryResponse, pluginsGlobals) + + return nil +} + +// blockedQueryResponse can be 'refused', 'hinfo' or IP responses 'a:IPv4,aaaa:IPv6 +func parseBlockedQueryResponse(bockedResponse string, pluginsGlobals *PluginsGlobals) { + bockedResponse = strings.ReplaceAll(strings.ToLower(bockedResponse), " ", "") + + if strings.HasPrefix(bockedResponse, "a:") { + blockedIPStrings := strings.Split(bockedResponse, ",") + (*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:,aaaa:'", blockedIPStrings[1]) + } + } + + if (*pluginsGlobals).respondWithIPv6 == nil { + (*pluginsGlobals).respondWithIPv6 = (*pluginsGlobals).respondWithIPv4 + } + + } else { + switch bockedResponse { case "refused": (*pluginsGlobals).refusedCodeInResponses = true case "hinfo": (*pluginsGlobals).refusedCodeInResponses = false default: - dlog.Noticef("Invalid blocked_query_response option [%s], defaulting to `hinfo`", proxy.blockedQueryResponse) + dlog.Noticef("Invalid blocked_query_response option [%s], defaulting to `hinfo`", bockedResponse) (*pluginsGlobals).refusedCodeInResponses = false } } - - return nil } type Plugin interface { @@ -201,7 +238,7 @@ func (pluginsState *PluginsState) ApplyQueryPlugins(pluginsGlobals *PluginsGloba return packet, ret } if pluginsState.action == PluginsActionReject { - synth, err := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses, pluginsGlobals.respondWithIP, pluginsState.cacheMinTTL) + synth, err := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses, pluginsGlobals.respondWithIPv4, pluginsGlobals.respondWithIPv6, pluginsState.cacheMinTTL) if err != nil { return nil, err } @@ -249,7 +286,7 @@ func (pluginsState *PluginsState) ApplyResponsePlugins(pluginsGlobals *PluginsGl return packet, ret } if pluginsState.action == PluginsActionReject { - synth, err := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses, pluginsGlobals.respondWithIP, pluginsState.cacheMinTTL) + synth, err := RefusedResponseFromMessage(&msg, pluginsGlobals.refusedCodeInResponses, pluginsGlobals.respondWithIPv4, pluginsGlobals.respondWithIPv6, pluginsState.cacheMinTTL) if err != nil { return nil, err }