Merge: + DNS Rewrites: support wildcard domain name
Closes #922 * commit '5ba45b91c9cfde84fc0866e6b3fca679504975e7': * client: fix render field for DNS settings + client: handle wildcard domains + DNS Rewrites: support wildcard domain name
This commit is contained in:
commit
c5537968b1
|
@ -916,6 +916,8 @@ Response:
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
|
|
||||||
|
`domain` can be an exact host name (`www.host.com`) or a wildcard (`*.host.com`).
|
||||||
|
|
||||||
|
|
||||||
### API: Add a rewrite entry
|
### API: Add a rewrite entry
|
||||||
|
|
||||||
|
|
|
@ -302,7 +302,7 @@
|
||||||
"setup_guide": "Setup guide",
|
"setup_guide": "Setup guide",
|
||||||
"dns_addresses": "DNS addresses",
|
"dns_addresses": "DNS addresses",
|
||||||
"dns_start": "DNS server is starting up",
|
"dns_start": "DNS server is starting up",
|
||||||
"dns_status_error": "Error of getting DNS server status",
|
"dns_status_error": "Error checking the DNS server status",
|
||||||
"down": "Down",
|
"down": "Down",
|
||||||
"fix": "Fix",
|
"fix": "Fix",
|
||||||
"dns_providers": "Here is a <0>list of known DNS providers</0> to choose from.",
|
"dns_providers": "Here is a <0>list of known DNS providers</0> to choose from.",
|
||||||
|
@ -372,7 +372,7 @@
|
||||||
"rewrite_desc": "Allows to easily configure custom DNS response for a specific domain name.",
|
"rewrite_desc": "Allows to easily configure custom DNS response for a specific domain name.",
|
||||||
"rewrite_applied": "Applied Rewrite rule",
|
"rewrite_applied": "Applied Rewrite rule",
|
||||||
"dns_rewrites": "DNS rewrites",
|
"dns_rewrites": "DNS rewrites",
|
||||||
"form_domain": "Enter domain",
|
"form_domain": "Enter domain name or wildcard",
|
||||||
"form_answer": "Enter IP address or domain name",
|
"form_answer": "Enter IP address or domain name",
|
||||||
"form_error_domain_format": "Invalid domain format",
|
"form_error_domain_format": "Invalid domain format",
|
||||||
"form_error_answer_format": "Invalid answer format",
|
"form_error_answer_format": "Invalid answer format",
|
||||||
|
@ -428,5 +428,8 @@
|
||||||
"whois": "Whois",
|
"whois": "Whois",
|
||||||
"filtering_rules_learn_more": "<0>Learn more</0> about creating your own hosts blocklists.",
|
"filtering_rules_learn_more": "<0>Learn more</0> about creating your own hosts blocklists.",
|
||||||
"blocked_by_response": "Blocked by CNAME or IP in response",
|
"blocked_by_response": "Blocked by CNAME or IP in response",
|
||||||
"try_again": "Try again"
|
"try_again": "Try again",
|
||||||
|
"domain_desc": "Enter the domain name or wildcard you want to be rewritten.",
|
||||||
|
"example_rewrite_domain": "rewrite responses for this domain name only.",
|
||||||
|
"example_rewrite_wildcard": "rewrite responses for all <0>example.org</0> subdomains."
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ import { Trans, withNamespaces } from 'react-i18next';
|
||||||
import flow from 'lodash/flow';
|
import flow from 'lodash/flow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
renderField,
|
renderInputField,
|
||||||
renderRadioField,
|
renderRadioField,
|
||||||
renderSelectField,
|
renderSelectField,
|
||||||
required,
|
required,
|
||||||
|
@ -45,7 +45,7 @@ let Form = ({
|
||||||
<Field
|
<Field
|
||||||
name="ratelimit"
|
name="ratelimit"
|
||||||
type="number"
|
type="number"
|
||||||
component={renderField}
|
component={renderInputField}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder={t('form_enter_rate_limit')}
|
placeholder={t('form_enter_rate_limit')}
|
||||||
normalize={toNumber}
|
normalize={toNumber}
|
||||||
|
@ -90,7 +90,7 @@ let Form = ({
|
||||||
</div>
|
</div>
|
||||||
<Field
|
<Field
|
||||||
name="blocking_ipv4"
|
name="blocking_ipv4"
|
||||||
component={renderField}
|
component={renderInputField}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder={t('form_enter_ip')}
|
placeholder={t('form_enter_ip')}
|
||||||
validate={[ipv4, required]}
|
validate={[ipv4, required]}
|
||||||
|
@ -107,7 +107,7 @@ let Form = ({
|
||||||
</div>
|
</div>
|
||||||
<Field
|
<Field
|
||||||
name="blocking_ipv6"
|
name="blocking_ipv6"
|
||||||
component={renderField}
|
component={renderInputField}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder={t('form_enter_ip')}
|
placeholder={t('form_enter_ip')}
|
||||||
validate={[ipv6, required]}
|
validate={[ipv6, required]}
|
||||||
|
|
|
@ -20,6 +20,9 @@ const Form = (props) => {
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>domain_desc</Trans>
|
||||||
|
</div>
|
||||||
<div className="form__group">
|
<div className="form__group">
|
||||||
<Field
|
<Field
|
||||||
id="domain"
|
id="domain"
|
||||||
|
@ -31,6 +34,22 @@ const Form = (props) => {
|
||||||
validate={[required, domain]}
|
validate={[required, domain]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Trans>examples_title</Trans>:
|
||||||
|
<ol className="leading-loose">
|
||||||
|
<li>
|
||||||
|
<code>example.org</code> – <Trans>example_rewrite_domain</Trans>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>*.example.org</code> –
|
||||||
|
<span>
|
||||||
|
<Trans components={[<code key="0">text</code>]}>
|
||||||
|
example_rewrite_wildcard
|
||||||
|
</Trans>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<div className="form__group">
|
<div className="form__group">
|
||||||
<Field
|
<Field
|
||||||
id="answer"
|
id="answer"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export const R_URL_REQUIRES_PROTOCOL = /^https?:\/\/[^/\s]+(\/.*)?$/;
|
export const R_URL_REQUIRES_PROTOCOL = /^https?:\/\/[^/\s]+(\/.*)?$/;
|
||||||
export const R_HOST = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$/;
|
export const R_HOST = /^(\*\.)?([\w-]+\.)+[\w-]+$/;
|
||||||
export const R_IPV4 = /^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/;
|
export const R_IPV4 = /^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/;
|
||||||
export const R_IPV6 = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
|
export const R_IPV6 = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
|
||||||
export const R_CIDR = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/;
|
export const R_CIDR = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/;
|
||||||
|
|
|
@ -334,6 +334,13 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return TRUE of host name matches a wildcard pattern
|
||||||
|
func matchDomainWildcard(host, wildcard string) bool {
|
||||||
|
return len(wildcard) >= 2 &&
|
||||||
|
wildcard[0] == '*' && wildcard[1] == '.' &&
|
||||||
|
strings.HasSuffix(host, wildcard[1:])
|
||||||
|
}
|
||||||
|
|
||||||
// Process rewrites table
|
// Process rewrites table
|
||||||
// . Find CNAME for a domain name
|
// . Find CNAME for a domain name
|
||||||
// . if found, set domain name to canonical name
|
// . if found, set domain name to canonical name
|
||||||
|
@ -347,8 +354,10 @@ func (d *Dnsfilter) processRewrites(host string, qtype uint16) Result {
|
||||||
|
|
||||||
for _, r := range d.Rewrites {
|
for _, r := range d.Rewrites {
|
||||||
if r.Domain != host {
|
if r.Domain != host {
|
||||||
|
if !matchDomainWildcard(host, r.Domain) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(r.Answer)
|
ip := net.ParseIP(r.Answer)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
|
@ -362,8 +371,10 @@ func (d *Dnsfilter) processRewrites(host string, qtype uint16) Result {
|
||||||
|
|
||||||
for _, r := range d.Rewrites {
|
for _, r := range d.Rewrites {
|
||||||
if r.Domain != host {
|
if r.Domain != host {
|
||||||
|
if !matchDomainWildcard(host, r.Domain) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ip := net.ParseIP(r.Answer)
|
ip := net.ParseIP(r.Answer)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
|
|
|
@ -474,6 +474,60 @@ func TestClientSettings(t *testing.T) {
|
||||||
assert.True(t, r.IsFiltered && r.Reason == FilteredBlockedService)
|
assert.True(t, r.IsFiltered && r.Reason == FilteredBlockedService)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRewrites(t *testing.T) {
|
||||||
|
d := Dnsfilter{}
|
||||||
|
// CNAME, A, AAAA
|
||||||
|
d.Rewrites = []RewriteEntry{
|
||||||
|
RewriteEntry{"somecname", "somehost.com"},
|
||||||
|
RewriteEntry{"somehost.com", "0.0.0.0"},
|
||||||
|
|
||||||
|
RewriteEntry{"host.com", "1.2.3.4"},
|
||||||
|
RewriteEntry{"host.com", "1.2.3.5"},
|
||||||
|
RewriteEntry{"host.com", "1:2:3::4"},
|
||||||
|
RewriteEntry{"www.host.com", "host.com"},
|
||||||
|
}
|
||||||
|
r := d.processRewrites("host2.com", dns.TypeA)
|
||||||
|
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||||
|
|
||||||
|
r = d.processRewrites("www.host.com", dns.TypeA)
|
||||||
|
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||||
|
assert.Equal(t, "host.com", r.CanonName)
|
||||||
|
assert.True(t, len(r.IPList) == 2)
|
||||||
|
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
|
||||||
|
assert.True(t, r.IPList[1].Equal(net.ParseIP("1.2.3.5")))
|
||||||
|
|
||||||
|
r = d.processRewrites("www.host.com", dns.TypeAAAA)
|
||||||
|
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||||
|
assert.True(t, len(r.IPList) == 1)
|
||||||
|
assert.True(t, r.IPList[0].Equal(net.ParseIP("1:2:3::4")))
|
||||||
|
|
||||||
|
// wildcard
|
||||||
|
d.Rewrites = []RewriteEntry{
|
||||||
|
RewriteEntry{"*.host.com", "1.2.3.5"},
|
||||||
|
RewriteEntry{"host.com", "1.2.3.4"},
|
||||||
|
}
|
||||||
|
r = d.processRewrites("host.com", dns.TypeA)
|
||||||
|
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||||
|
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
|
||||||
|
|
||||||
|
r = d.processRewrites("www.host.com", dns.TypeA)
|
||||||
|
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||||
|
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.5")))
|
||||||
|
|
||||||
|
r = d.processRewrites("www.host2.com", dns.TypeA)
|
||||||
|
assert.Equal(t, NotFilteredNotFound, r.Reason)
|
||||||
|
|
||||||
|
// wildcard + CNAME
|
||||||
|
d.Rewrites = []RewriteEntry{
|
||||||
|
RewriteEntry{"*.host.com", "host.com"},
|
||||||
|
RewriteEntry{"host.com", "1.2.3.4"},
|
||||||
|
}
|
||||||
|
r = d.processRewrites("www.host.com", dns.TypeA)
|
||||||
|
assert.Equal(t, ReasonRewrite, r.Reason)
|
||||||
|
assert.Equal(t, "host.com", r.CanonName)
|
||||||
|
assert.True(t, r.IPList[0].Equal(net.ParseIP("1.2.3.4")))
|
||||||
|
}
|
||||||
|
|
||||||
// BENCHMARKS
|
// BENCHMARKS
|
||||||
|
|
||||||
func BenchmarkSafeBrowsing(b *testing.B) {
|
func BenchmarkSafeBrowsing(b *testing.B) {
|
||||||
|
|
Loading…
Reference in New Issue