Compare commits
8 Commits
v0.105.0
...
102-dns-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9efc39306 | ||
|
|
f924523f6a | ||
|
|
6e6ee9697a | ||
|
|
aff09211b2 | ||
|
|
bad1c6acdc | ||
|
|
fcb582679e | ||
|
|
6b60598025 | ||
|
|
b338bf9b3f |
@@ -1558,6 +1558,7 @@ Strict matching can be enabled by enclosing the value in double quotes: e.g. `"a
|
|||||||
* blocked_services - blocked services
|
* blocked_services - blocked services
|
||||||
* blocked_safebrowsing - blocked by safebrowsing
|
* blocked_safebrowsing - blocked by safebrowsing
|
||||||
* blocked_parental - blocked by parental control
|
* blocked_parental - blocked by parental control
|
||||||
|
* blocked_dns_rebinding - blocked by DNS rebinding protection
|
||||||
* whitelisted - whitelisted
|
* whitelisted - whitelisted
|
||||||
* rewritten - all kinds of rewrites
|
* rewritten - all kinds of rewrites
|
||||||
* safe_search - enforced safe search
|
* safe_search - enforced safe search
|
||||||
|
|||||||
@@ -587,5 +587,12 @@
|
|||||||
"port_53_faq_link": "Port 53 is often occupied by \"DNSStubListener\" or \"systemd-resolved\" services. Please read <0>this instruction</0> on how to resolve this.",
|
"port_53_faq_link": "Port 53 is often occupied by \"DNSStubListener\" or \"systemd-resolved\" services. Please read <0>this instruction</0> on how to resolve this.",
|
||||||
"adg_will_drop_dns_queries": "AdGuard Home will be dropping all DNS queries from this client.",
|
"adg_will_drop_dns_queries": "AdGuard Home will be dropping all DNS queries from this client.",
|
||||||
"client_not_in_allowed_clients": "The client is not allowed because it is not in the \"Allowed clients\" list.",
|
"client_not_in_allowed_clients": "The client is not allowed because it is not in the \"Allowed clients\" list.",
|
||||||
"experimental": "Experimental"
|
"experimental": "Experimental",
|
||||||
|
"rebinding_title": "DNS Rebinding Protection",
|
||||||
|
"rebinding_desc": "Here you can configure protection against DNS rebinding attacks",
|
||||||
|
"rebinding_protection_enabled": "Enable protection from DNS rebinding attacks",
|
||||||
|
"rebinding_protection_enabled_desc": "If enabled, AdGuard Home will block responses containing host on the local network.",
|
||||||
|
"rebinding_allowed_hosts_title": "Allowed domains",
|
||||||
|
"rebinding_allowed_hosts_desc": "A list of domains. If configured, AdGuard Home will allow responses containing host on the local network from these domains. Here you can specify the exact domain names, wildcards and urlfilter-rules, e.g. 'example.org', '*.example.org' or '||example.org^'.",
|
||||||
|
"blocked_dns_rebinding": "Blocked DNS rebinding"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ export const setDnsConfig = (config) => async (dispatch) => {
|
|||||||
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
data.upstream_dns = splitByNewLine(config.upstream_dns);
|
||||||
hasDnsSettings = true;
|
hasDnsSettings = true;
|
||||||
}
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(data, 'rebinding_allowed_hosts')) {
|
||||||
|
data.rebinding_allowed_hosts = splitByNewLine(config.rebinding_allowed_hosts);
|
||||||
|
hasDnsSettings = true;
|
||||||
|
}
|
||||||
|
|
||||||
await apiClient.setDnsConfig(data);
|
await apiClient.setDnsConfig(data);
|
||||||
|
|
||||||
|
|||||||
91
client/src/components/Settings/Dns/Rebinding/Form.js
Normal file
91
client/src/components/Settings/Dns/Rebinding/Form.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Field, reduxForm } from 'redux-form';
|
||||||
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { renderTextareaField, CheckboxField } from '../../../../helpers/form';
|
||||||
|
import { removeEmptyLines } from '../../../../helpers/helpers';
|
||||||
|
import { FORM_NAME } from '../../../../helpers/constants';
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
id: 'rebinding_allowed_hosts',
|
||||||
|
title: 'rebinding_allowed_hosts_title',
|
||||||
|
subtitle: 'rebinding_allowed_hosts_desc',
|
||||||
|
normalizeOnBlur: removeEmptyLines,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const Form = ({
|
||||||
|
handleSubmit, submitting, invalid,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const processingSetConfig = useSelector((state) => state.dnsConfig.processingSetConfig);
|
||||||
|
|
||||||
|
const renderField = ({
|
||||||
|
id, title, subtitle, disabled = processingSetConfig, normalizeOnBlur,
|
||||||
|
}) => <div key={id} className="form__group mb-5">
|
||||||
|
<label className="form__label form__label--with-desc" htmlFor={id}>
|
||||||
|
<Trans>{title}</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>{subtitle}</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
id={id}
|
||||||
|
name={id}
|
||||||
|
component={renderTextareaField}
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea font-monospace"
|
||||||
|
disabled={disabled}
|
||||||
|
normalizeOnBlur={normalizeOnBlur}
|
||||||
|
/>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
renderField.propTypes = {
|
||||||
|
id: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
|
subtitle: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
normalizeOnBlur: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<Field
|
||||||
|
name={'rebinding_protection_enabled'}
|
||||||
|
type="checkbox"
|
||||||
|
component={CheckboxField}
|
||||||
|
placeholder={t('rebinding_protection_enabled')}
|
||||||
|
subtitle={t('rebinding_protection_enabled_desc')}
|
||||||
|
disabled={processingSetConfig}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{fields.map(renderField)}
|
||||||
|
|
||||||
|
<div className="card-actions">
|
||||||
|
<div className="btn-list">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-success btn-standard"
|
||||||
|
disabled={submitting || invalid || processingSetConfig}
|
||||||
|
>
|
||||||
|
<Trans>save_config</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Form.propTypes = {
|
||||||
|
handleSubmit: PropTypes.func.isRequired,
|
||||||
|
submitting: PropTypes.bool.isRequired,
|
||||||
|
invalid: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default reduxForm({ form: FORM_NAME.REBINDING })(Form);
|
||||||
36
client/src/components/Settings/Dns/Rebinding/index.js
Normal file
36
client/src/components/Settings/Dns/Rebinding/index.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
|
import Form from './Form';
|
||||||
|
import Card from '../../../ui/Card';
|
||||||
|
import { setDnsConfig } from '../../../../actions/dnsConfig';
|
||||||
|
|
||||||
|
const RebindingConfig = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
rebinding_protection_enabled, rebinding_allowed_hosts,
|
||||||
|
} = useSelector((state) => state.dnsConfig, shallowEqual);
|
||||||
|
|
||||||
|
const handleFormSubmit = (values) => {
|
||||||
|
dispatch(setDnsConfig(values));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={t('rebinding_title')}
|
||||||
|
subtitle={t('rebinding_desc')}
|
||||||
|
bodyType="card-body box-body--settings"
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
initialValues={{
|
||||||
|
rebinding_protection_enabled,
|
||||||
|
rebinding_allowed_hosts,
|
||||||
|
}}
|
||||||
|
onSubmit={handleFormSubmit}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RebindingConfig;
|
||||||
@@ -8,6 +8,7 @@ import Config from './Config';
|
|||||||
import PageTitle from '../../ui/PageTitle';
|
import PageTitle from '../../ui/PageTitle';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import CacheConfig from './Cache';
|
import CacheConfig from './Cache';
|
||||||
|
import RebindingConfig from './Rebinding';
|
||||||
import { getDnsConfig } from '../../../actions/dnsConfig';
|
import { getDnsConfig } from '../../../actions/dnsConfig';
|
||||||
import { getAccessList } from '../../../actions/access';
|
import { getAccessList } from '../../../actions/access';
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ const Dns = () => {
|
|||||||
<Config />
|
<Config />
|
||||||
<CacheConfig />
|
<CacheConfig />
|
||||||
<Access />
|
<Access />
|
||||||
|
<RebindingConfig />
|
||||||
</>}
|
</>}
|
||||||
</>;
|
</>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -341,6 +341,7 @@ export const FILTERED_STATUS = {
|
|||||||
REWRITE_HOSTS: 'RewriteEtcHosts',
|
REWRITE_HOSTS: 'RewriteEtcHosts',
|
||||||
FILTERED_SAFE_SEARCH: 'FilteredSafeSearch',
|
FILTERED_SAFE_SEARCH: 'FilteredSafeSearch',
|
||||||
FILTERED_SAFE_BROWSING: 'FilteredSafeBrowsing',
|
FILTERED_SAFE_BROWSING: 'FilteredSafeBrowsing',
|
||||||
|
FILTERED_REBIND: 'FilteredRebind',
|
||||||
FILTERED_PARENTAL: 'FilteredParental',
|
FILTERED_PARENTAL: 'FilteredParental',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -373,6 +374,10 @@ export const RESPONSE_FILTER = {
|
|||||||
QUERY: 'blocked_parental',
|
QUERY: 'blocked_parental',
|
||||||
LABEL: 'blocked_adult_websites',
|
LABEL: 'blocked_adult_websites',
|
||||||
},
|
},
|
||||||
|
BLOCKED_DNS_REBINDING: {
|
||||||
|
QUERY: 'blocked_dns_rebinding',
|
||||||
|
LABEL: 'blocked_dns_rebinding',
|
||||||
|
},
|
||||||
ALLOWED: {
|
ALLOWED: {
|
||||||
QUERY: 'whitelisted',
|
QUERY: 'whitelisted',
|
||||||
LABEL: 'allowed',
|
LABEL: 'allowed',
|
||||||
@@ -414,6 +419,10 @@ export const FILTERED_STATUS_TO_META_MAP = {
|
|||||||
LABEL: 'blocked_service',
|
LABEL: 'blocked_service',
|
||||||
COLOR: QUERY_STATUS_COLORS.RED,
|
COLOR: QUERY_STATUS_COLORS.RED,
|
||||||
},
|
},
|
||||||
|
[FILTERED_STATUS.FILTERED_REBIND]: {
|
||||||
|
LABEL: RESPONSE_FILTER.BLOCKED_DNS_REBINDING.LABEL,
|
||||||
|
COLOR: QUERY_STATUS_COLORS.RED,
|
||||||
|
},
|
||||||
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: {
|
[FILTERED_STATUS.FILTERED_SAFE_SEARCH]: {
|
||||||
LABEL: RESPONSE_FILTER.SAFE_SEARCH.LABEL,
|
LABEL: RESPONSE_FILTER.SAFE_SEARCH.LABEL,
|
||||||
COLOR: QUERY_STATUS_COLORS.YELLOW,
|
COLOR: QUERY_STATUS_COLORS.YELLOW,
|
||||||
@@ -509,6 +518,7 @@ export const FORM_NAME = {
|
|||||||
INSTALL: 'install',
|
INSTALL: 'install',
|
||||||
LOGIN: 'login',
|
LOGIN: 'login',
|
||||||
CACHE: 'cache',
|
CACHE: 'cache',
|
||||||
|
REBINDING: 'rebinding',
|
||||||
...DHCP_FORM_NAMES,
|
...DHCP_FORM_NAMES,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const dnsConfig = handleActions(
|
|||||||
blocking_ipv6,
|
blocking_ipv6,
|
||||||
upstream_dns,
|
upstream_dns,
|
||||||
bootstrap_dns,
|
bootstrap_dns,
|
||||||
|
rebinding_allowed_hosts,
|
||||||
...values
|
...values
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
@@ -24,8 +25,9 @@ const dnsConfig = handleActions(
|
|||||||
...values,
|
...values,
|
||||||
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
|
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
|
||||||
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
|
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
|
||||||
upstream_dns: (upstream_dns && upstream_dns.join('\n')) || '',
|
upstream_dns: upstream_dns?.join('\n') || '',
|
||||||
bootstrap_dns: (bootstrap_dns && bootstrap_dns.join('\n')) || '',
|
bootstrap_dns: bootstrap_dns?.join('\n') || '',
|
||||||
|
rebinding_allowed_hosts: rebinding_allowed_hosts?.join('\n') || '',
|
||||||
processingGetConfig: false,
|
processingGetConfig: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ const (
|
|||||||
FilteredSafeSearch
|
FilteredSafeSearch
|
||||||
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
// FilteredBlockedService - the host is blocked by "blocked services" settings
|
||||||
FilteredBlockedService
|
FilteredBlockedService
|
||||||
|
// FilteredRebind - the request was blocked due to DNS rebinding protection
|
||||||
|
FilteredRebind
|
||||||
|
|
||||||
// ReasonRewrite - rewrite rule was applied
|
// ReasonRewrite - rewrite rule was applied
|
||||||
ReasonRewrite
|
ReasonRewrite
|
||||||
@@ -165,6 +167,7 @@ var reasonNames = []string{
|
|||||||
"FilteredInvalid",
|
"FilteredInvalid",
|
||||||
"FilteredSafeSearch",
|
"FilteredSafeSearch",
|
||||||
"FilteredBlockedService",
|
"FilteredBlockedService",
|
||||||
|
"FilteredRebind",
|
||||||
|
|
||||||
"Rewrite",
|
"Rewrite",
|
||||||
"RewriteEtcHosts",
|
"RewriteEtcHosts",
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ type FilteringConfig struct {
|
|||||||
CacheMinTTL uint32 `yaml:"cache_ttl_min"` // override TTL value (minimum) received from upstream server
|
CacheMinTTL uint32 `yaml:"cache_ttl_min"` // override TTL value (minimum) received from upstream server
|
||||||
CacheMaxTTL uint32 `yaml:"cache_ttl_max"` // override TTL value (maximum) received from upstream server
|
CacheMaxTTL uint32 `yaml:"cache_ttl_max"` // override TTL value (maximum) received from upstream server
|
||||||
|
|
||||||
|
// DNS rebinding protection settings
|
||||||
|
// --
|
||||||
|
RebindingProtectionEnabled bool `yaml:"rebinding_protection_enabled"`
|
||||||
|
RebindingAllowedHosts []string `yaml:"rebinding_allowed_hosts"`
|
||||||
|
|
||||||
// Other settings
|
// Other settings
|
||||||
// --
|
// --
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ func (s *Server) handleDNSRequest(_ *proxy.Proxy, d *proxy.DNSContext) error {
|
|||||||
processFilteringBeforeRequest,
|
processFilteringBeforeRequest,
|
||||||
processUpstream,
|
processUpstream,
|
||||||
processDNSSECAfterResponse,
|
processDNSSECAfterResponse,
|
||||||
|
processRebindingFilteringAfterResponse,
|
||||||
processFilteringAfterResponse,
|
processFilteringAfterResponse,
|
||||||
s.ipset.process,
|
s.ipset.process,
|
||||||
processQueryLogsAndStats,
|
processQueryLogsAndStats,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ type Server struct {
|
|||||||
queryLog querylog.QueryLog // Query log instance
|
queryLog querylog.QueryLog // Query log instance
|
||||||
stats stats.Stats
|
stats stats.Stats
|
||||||
access *accessCtx
|
access *accessCtx
|
||||||
|
rebinding *dnsRebindChecker
|
||||||
|
|
||||||
ipset ipsetCtx
|
ipset ipsetCtx
|
||||||
|
|
||||||
@@ -122,6 +123,7 @@ func (s *Server) WriteDiskConfig(c *FilteringConfig) {
|
|||||||
c.DisallowedClients = stringArrayDup(sc.DisallowedClients)
|
c.DisallowedClients = stringArrayDup(sc.DisallowedClients)
|
||||||
c.BlockedHosts = stringArrayDup(sc.BlockedHosts)
|
c.BlockedHosts = stringArrayDup(sc.BlockedHosts)
|
||||||
c.UpstreamDNS = stringArrayDup(sc.UpstreamDNS)
|
c.UpstreamDNS = stringArrayDup(sc.UpstreamDNS)
|
||||||
|
c.RebindingAllowedHosts = stringArrayDup(sc.RebindingAllowedHosts)
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +223,13 @@ func (s *Server) Prepare(config *ServerConfig) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize DNS rebinding module
|
||||||
|
// --
|
||||||
|
s.rebinding, err = newRebindChecker(s.conf.RebindingAllowedHosts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Register web handlers if necessary
|
// Register web handlers if necessary
|
||||||
// --
|
// --
|
||||||
if !webRegistered && s.conf.HTTPRegister != nil {
|
if !webRegistered && s.conf.HTTPRegister != nil {
|
||||||
|
|||||||
@@ -738,6 +738,100 @@ func TestRewrite(t *testing.T) {
|
|||||||
_ = s.Stop()
|
_ = s.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBlockedDNSRebinding(t *testing.T) {
|
||||||
|
s := createTestServer(t)
|
||||||
|
|
||||||
|
err := s.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to start server: %s", err)
|
||||||
|
}
|
||||||
|
addr := s.dnsProxy.Addr(proxy.ProtoUDP)
|
||||||
|
|
||||||
|
//
|
||||||
|
// DNS rebinding protection
|
||||||
|
//
|
||||||
|
req := dns.Msg{}
|
||||||
|
req.Id = dns.Id()
|
||||||
|
req.RecursionDesired = true
|
||||||
|
req.Question = []dns.Question{
|
||||||
|
{Name: "192-168-1-250.nip.io.", Qtype: dns.TypeA, Qclass: dns.ClassINET},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
reply, err := dns.Exchange(&req, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.Answer) != 1 {
|
||||||
|
t.Fatalf("DNS server %s returned reply with wrong number of answers - %d", addr, len(reply.Answer))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, ok := reply.Answer[0].(*dns.A)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer type instead of A: %v", addr, reply.Answer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !net.IPv4zero.Equal(a.A) {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer instead of 0.0.0.0: %v", addr, a.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.conf.RebindingProtectionEnabled = false
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
reply, err = dns.Exchange(&req, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.Answer) != 1 {
|
||||||
|
t.Fatalf("DNS server %s returned reply with wrong number of answers - %d", addr, len(reply.Answer))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, ok = reply.Answer[0].(*dns.A)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer type instead of A: %v", addr, reply.Answer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !net.IPv4(192, 168, 1, 250).Equal(a.A) {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer instead of 192.168.1.250: %v", addr, a.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
s.rebinding, _ = newRebindChecker([]string{
|
||||||
|
"||nip.io^",
|
||||||
|
})
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
reply, err = dns.Exchange(&req, addr.String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Couldn't talk to server %s: %s", addr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.Answer) != 1 {
|
||||||
|
t.Fatalf("DNS server %s returned reply with wrong number of answers - %d", addr, len(reply.Answer))
|
||||||
|
}
|
||||||
|
|
||||||
|
a, ok = reply.Answer[0].(*dns.A)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer type instead of A: %v", addr, reply.Answer[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !net.IPv4(192, 168, 1, 250).Equal(a.A) {
|
||||||
|
t.Fatalf("DNS server %s returned wrong answer instead of 192.168.1.250: %v", addr, a.A)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.Stop()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("DNS server failed to stop: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createTestServer(t *testing.T) *Server {
|
func createTestServer(t *testing.T) *Server {
|
||||||
rules := `||nxdomain.example.org
|
rules := `||nxdomain.example.org
|
||||||
||null.example.org^
|
||null.example.org^
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ func (s *Server) filterDNSResponse(ctx *dnsContext) (*dnsfilter.Result, error) {
|
|||||||
|
|
||||||
switch v := a.(type) {
|
switch v := a.(type) {
|
||||||
case *dns.CNAME:
|
case *dns.CNAME:
|
||||||
log.Debug("DNSFwd: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
|
||||||
host = strings.TrimSuffix(v.Target, ".")
|
host = strings.TrimSuffix(v.Target, ".")
|
||||||
|
log.Debug("DNSFwd: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
||||||
|
|
||||||
case *dns.A:
|
case *dns.A:
|
||||||
host = v.A.String()
|
host = v.A.String()
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ type dnsConfig struct {
|
|||||||
CacheSize *uint32 `json:"cache_size"`
|
CacheSize *uint32 `json:"cache_size"`
|
||||||
CacheMinTTL *uint32 `json:"cache_ttl_min"`
|
CacheMinTTL *uint32 `json:"cache_ttl_min"`
|
||||||
CacheMaxTTL *uint32 `json:"cache_ttl_max"`
|
CacheMaxTTL *uint32 `json:"cache_ttl_max"`
|
||||||
|
|
||||||
|
RebindingProtectionEnabled *bool `json:"rebinding_protection_enabled"`
|
||||||
|
RebindingAllowedHosts *[]string `json:"rebinding_allowed_hosts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getDNSConfig() dnsConfig {
|
func (s *Server) getDNSConfig() dnsConfig {
|
||||||
@@ -61,23 +64,27 @@ func (s *Server) getDNSConfig() dnsConfig {
|
|||||||
} else if s.conf.AllServers {
|
} else if s.conf.AllServers {
|
||||||
upstreamMode = "parallel"
|
upstreamMode = "parallel"
|
||||||
}
|
}
|
||||||
|
rebindingEnabled := s.conf.RebindingProtectionEnabled
|
||||||
|
rebindingAllowedHosts := stringArrayDup(s.conf.RebindingAllowedHosts)
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
return dnsConfig{
|
return dnsConfig{
|
||||||
Upstreams: &upstreams,
|
Upstreams: &upstreams,
|
||||||
UpstreamsFile: &upstreamFile,
|
UpstreamsFile: &upstreamFile,
|
||||||
Bootstraps: &bootstraps,
|
Bootstraps: &bootstraps,
|
||||||
ProtectionEnabled: &protectionEnabled,
|
ProtectionEnabled: &protectionEnabled,
|
||||||
BlockingMode: &blockingMode,
|
BlockingMode: &blockingMode,
|
||||||
BlockingIPv4: &BlockingIPv4,
|
BlockingIPv4: &BlockingIPv4,
|
||||||
BlockingIPv6: &BlockingIPv6,
|
BlockingIPv6: &BlockingIPv6,
|
||||||
RateLimit: &Ratelimit,
|
RateLimit: &Ratelimit,
|
||||||
EDNSCSEnabled: &EnableEDNSClientSubnet,
|
EDNSCSEnabled: &EnableEDNSClientSubnet,
|
||||||
DNSSECEnabled: &EnableDNSSEC,
|
DNSSECEnabled: &EnableDNSSEC,
|
||||||
DisableIPv6: &AAAADisabled,
|
DisableIPv6: &AAAADisabled,
|
||||||
CacheSize: &CacheSize,
|
CacheSize: &CacheSize,
|
||||||
CacheMinTTL: &CacheMinTTL,
|
CacheMinTTL: &CacheMinTTL,
|
||||||
CacheMaxTTL: &CacheMaxTTL,
|
CacheMaxTTL: &CacheMaxTTL,
|
||||||
UpstreamMode: &upstreamMode,
|
UpstreamMode: &upstreamMode,
|
||||||
|
RebindingProtectionEnabled: &rebindingEnabled,
|
||||||
|
RebindingAllowedHosts: &rebindingAllowedHosts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,6 +308,8 @@ func (s *Server) setConfig(dc dnsConfig) (restart bool) {
|
|||||||
s.conf.FastestAddr = false
|
s.conf.FastestAddr = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restart = restart || s.setRebindingConfig(dc)
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
s.conf.ConfigModified()
|
s.conf.ConfigModified()
|
||||||
return restart
|
return restart
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
|||||||
conf: func() ServerConfig {
|
conf: func() ServerConfig {
|
||||||
return defaultConf
|
return defaultConf
|
||||||
},
|
},
|
||||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "fastest_addr",
|
name: "fastest_addr",
|
||||||
conf: func() ServerConfig {
|
conf: func() ServerConfig {
|
||||||
@@ -37,7 +37,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
|||||||
conf.FastestAddr = true
|
conf.FastestAddr = true
|
||||||
return conf
|
return conf
|
||||||
},
|
},
|
||||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "parallel",
|
name: "parallel",
|
||||||
conf: func() ServerConfig {
|
conf: func() ServerConfig {
|
||||||
@@ -45,7 +45,7 @@ func TestDNSForwardHTTTP_handleGetConfig(t *testing.T) {
|
|||||||
conf.AllServers = true
|
conf.AllServers = true
|
||||||
return conf
|
return conf
|
||||||
},
|
},
|
||||||
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
want: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@@ -73,7 +73,7 @@ func TestDNSForwardHTTTP_handleSetConfig(t *testing.T) {
|
|||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
const defaultConfJSON = "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n"
|
const defaultConfJSON = "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n"
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
req string
|
req string
|
||||||
@@ -83,52 +83,52 @@ func TestDNSForwardHTTTP_handleSetConfig(t *testing.T) {
|
|||||||
name: "upstream_dns",
|
name: "upstream_dns",
|
||||||
req: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"]}",
|
req: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"]}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:77\",\"8.8.4.4:77\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "bootstraps",
|
name: "bootstraps",
|
||||||
req: "{\"bootstrap_dns\":[\"9.9.9.10\"]}",
|
req: "{\"bootstrap_dns\":[\"9.9.9.10\"]}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "blocking_mode_good",
|
name: "blocking_mode_good",
|
||||||
req: "{\"blocking_mode\":\"refused\"}",
|
req: "{\"blocking_mode\":\"refused\"}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"refused\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"refused\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "blocking_mode_bad",
|
name: "blocking_mode_bad",
|
||||||
req: "{\"blocking_mode\":\"custom_ip\"}",
|
req: "{\"blocking_mode\":\"custom_ip\"}",
|
||||||
wantSet: "blocking_mode: incorrect value\n",
|
wantSet: "blocking_mode: incorrect value\n",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "ratelimit",
|
name: "ratelimit",
|
||||||
req: "{\"ratelimit\":6}",
|
req: "{\"ratelimit\":6}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":6,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":6,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "edns_cs_enabled",
|
name: "edns_cs_enabled",
|
||||||
req: "{\"edns_cs_enabled\":true}",
|
req: "{\"edns_cs_enabled\":true}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":true,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":true,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "dnssec_enabled",
|
name: "dnssec_enabled",
|
||||||
req: "{\"dnssec_enabled\":true}",
|
req: "{\"dnssec_enabled\":true}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":true,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":true,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "cache_size",
|
name: "cache_size",
|
||||||
req: "{\"cache_size\":1024}",
|
req: "{\"cache_size\":1024}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":1024,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"\",\"cache_size\":1024,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_mode_parallel",
|
name: "upstream_mode_parallel",
|
||||||
req: "{\"upstream_mode\":\"parallel\"}",
|
req: "{\"upstream_mode\":\"parallel\"}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"parallel\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_mode_fastest_addr",
|
name: "upstream_mode_fastest_addr",
|
||||||
req: "{\"upstream_mode\":\"fastest_addr\"}",
|
req: "{\"upstream_mode\":\"fastest_addr\"}",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0}\n",
|
wantGet: "{\"upstream_dns\":[\"8.8.8.8:53\",\"8.8.4.4:53\"],\"upstream_dns_file\":\"\",\"bootstrap_dns\":[\"9.9.9.10\",\"149.112.112.10\",\"2620:fe::10\",\"2620:fe::fe:10\"],\"protection_enabled\":true,\"ratelimit\":0,\"blocking_mode\":\"\",\"blocking_ipv4\":\"\",\"blocking_ipv6\":\"\",\"edns_cs_enabled\":false,\"dnssec_enabled\":false,\"disable_ipv6\":false,\"upstream_mode\":\"fastest_addr\",\"cache_size\":0,\"cache_ttl_min\":0,\"cache_ttl_max\":0,\"rebinding_protection_enabled\":false,\"rebinding_allowed_hosts\":[]}\n",
|
||||||
}, {
|
}, {
|
||||||
name: "upstream_dns_bad",
|
name: "upstream_dns_bad",
|
||||||
req: "{\"upstream_dns\":[\"\"]}",
|
req: "{\"upstream_dns\":[\"\"]}",
|
||||||
|
|||||||
227
internal/dnsforward/rebind.go
Normal file
227
internal/dnsforward/rebind.go
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
// DNS Rebinding protection
|
||||||
|
|
||||||
|
package dnsforward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
|
||||||
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/urlfilter"
|
||||||
|
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dnsRebindChecker struct {
|
||||||
|
allowDomainEngine *urlfilter.DNSEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRebindChecker(allowedHosts []string) (*dnsRebindChecker, error) {
|
||||||
|
buf := strings.Builder{}
|
||||||
|
for _, s := range allowedHosts {
|
||||||
|
buf.WriteString(s)
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
rulesStorage, err := filterlist.NewRuleStorage([]filterlist.RuleList{
|
||||||
|
&filterlist.StringRuleList{
|
||||||
|
ID: int(0),
|
||||||
|
RulesText: buf.String(),
|
||||||
|
IgnoreCosmetic: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dnsRebindChecker{
|
||||||
|
allowDomainEngine: urlfilter.NewDNSEngine(rulesStorage),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isAllowedDomain(domain string) bool {
|
||||||
|
_, ok := c.allowDomainEngine.Match(domain)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPrivate reports whether ip is a private address, according to
|
||||||
|
// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses).
|
||||||
|
func (*dnsRebindChecker) isPrivate(ip net.IP) bool {
|
||||||
|
//TODO: remove once https://github.com/golang/go/pull/42793 makes it to stdlib
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
return ip4[0] == 10 ||
|
||||||
|
(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
|
||||||
|
(ip4[0] == 192 && ip4[1] == 168)
|
||||||
|
}
|
||||||
|
return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isRebindHost(host string) bool {
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
return c.isRebindIP(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
return host == "localhost"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isLocalNetworkV4(ip4 net.IP) bool {
|
||||||
|
switch {
|
||||||
|
case ip4[0] == 0:
|
||||||
|
/* 0.0.0.0/8 (RFC 5735 section 3. "here" network) */
|
||||||
|
case ip4[0] == 10:
|
||||||
|
/* 10.0.0.0/8 (private) */
|
||||||
|
case ip4[0] == 172 && ip4[1]&0x10 == 0x10:
|
||||||
|
/* 172.16.0.0/12 (private) */
|
||||||
|
case ip4[0] == 169 && ip4[1] == 254:
|
||||||
|
/* 169.254.0.0/16 (zeroconf) */
|
||||||
|
case ip4[0] == 192 && ip4[1] == 0 && ip4[2] == 2:
|
||||||
|
/* 192.0.2.0/24 (test-net) */
|
||||||
|
case ip4[0] == 198 && ip4[1] == 51 && ip4[2] == 100:
|
||||||
|
/* 198.51.100.0/24(test-net) */
|
||||||
|
case ip4[0] == 203 && ip4[1] == 0 && ip4[2] == 113:
|
||||||
|
/* 203.0.113.0/24 (test-net) */
|
||||||
|
case ip4.Equal(net.IPv4bcast):
|
||||||
|
/* 255.255.255.255/32 (broadcast)*/
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isLocalNetworkV6(ip6 net.IP) bool {
|
||||||
|
return ip6.Equal(net.IPv6zero) ||
|
||||||
|
ip6.Equal(net.IPv6unspecified) ||
|
||||||
|
ip6.Equal(net.IPv6interfacelocalallnodes) ||
|
||||||
|
ip6.Equal(net.IPv6linklocalallnodes) ||
|
||||||
|
ip6.Equal(net.IPv6linklocalallrouters)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsRebindChecker) isRebindIP(ip net.IP) bool {
|
||||||
|
// This is compatible with dnsmasq definition
|
||||||
|
// See: https://github.com/imp/dnsmasq/blob/4e7694d7107d2299f4aaededf8917fceb5dfb924/src/rfc1035.c#L412
|
||||||
|
|
||||||
|
rebind := false
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
rebind = c.isLocalNetworkV4(ip4)
|
||||||
|
} else {
|
||||||
|
rebind = c.isLocalNetworkV6(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rebind || c.isPrivate(ip) || ip.IsLoopback()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks DNS rebinding attacks
|
||||||
|
// Note both whitelisted and cached hosts will bypass rebinding check (see: processFilteringAfterResponse()).
|
||||||
|
func (s *Server) isResponseRebind(domain, host string) bool {
|
||||||
|
if !s.conf.RebindingProtectionEnabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.GetLevel() >= log.DEBUG {
|
||||||
|
timer := log.StartTimer()
|
||||||
|
defer timer.LogElapsed("DNS Rebinding check for %s -> %s", domain, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.rebinding.isAllowedDomain(domain) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.rebinding.isRebindHost(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processRebindingFilteringAfterResponse(ctx *dnsContext) int {
|
||||||
|
s := ctx.srv
|
||||||
|
d := ctx.proxyCtx
|
||||||
|
res := ctx.result
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if !ctx.responseFromUpstream || res.Reason == dnsfilter.ReasonRewrite {
|
||||||
|
return resultDone
|
||||||
|
}
|
||||||
|
|
||||||
|
originalRes := d.Res
|
||||||
|
ctx.result, err = s.preventRebindResponse(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ctx.err = err
|
||||||
|
return resultError
|
||||||
|
}
|
||||||
|
if ctx.result != nil {
|
||||||
|
ctx.origResp = originalRes // matched by response
|
||||||
|
} else {
|
||||||
|
ctx.result = &dnsfilter.Result{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultDone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) setRebindingConfig(dc dnsConfig) bool {
|
||||||
|
restart := false
|
||||||
|
|
||||||
|
if dc.RebindingProtectionEnabled != nil {
|
||||||
|
s.conf.RebindingProtectionEnabled = *dc.RebindingProtectionEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc.RebindingAllowedHosts != nil {
|
||||||
|
s.conf.RebindingAllowedHosts = *dc.RebindingAllowedHosts
|
||||||
|
restart = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return restart
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) preventRebindResponse(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||||
|
d := ctx.proxyCtx
|
||||||
|
|
||||||
|
for _, a := range d.Res.Answer {
|
||||||
|
m := ""
|
||||||
|
domainName := ""
|
||||||
|
host := ""
|
||||||
|
|
||||||
|
switch v := a.(type) {
|
||||||
|
case *dns.CNAME:
|
||||||
|
host = strings.TrimSuffix(v.Target, ".")
|
||||||
|
domainName = v.Hdr.Name
|
||||||
|
m = fmt.Sprintf("DNSRebind: Checking CNAME %s for %s", v.Target, v.Hdr.Name)
|
||||||
|
|
||||||
|
case *dns.A:
|
||||||
|
host = v.A.String()
|
||||||
|
domainName = v.Hdr.Name
|
||||||
|
m = fmt.Sprintf("DNSRebind: Checking record A (%s) for %s", host, v.Hdr.Name)
|
||||||
|
|
||||||
|
case *dns.AAAA:
|
||||||
|
host = v.AAAA.String()
|
||||||
|
domainName = v.Hdr.Name
|
||||||
|
m = fmt.Sprintf("DNSRebind: Checking record AAAA (%s) for %s", host, v.Hdr.Name)
|
||||||
|
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s.RLock()
|
||||||
|
if !s.conf.RebindingProtectionEnabled {
|
||||||
|
s.RUnlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug(m)
|
||||||
|
blocked := s.isResponseRebind(strings.TrimSuffix(domainName, "."), host)
|
||||||
|
s.RUnlock()
|
||||||
|
|
||||||
|
if blocked {
|
||||||
|
res := &dnsfilter.Result{
|
||||||
|
IsFiltered: true,
|
||||||
|
Reason: dnsfilter.FilteredRebind,
|
||||||
|
Rule: "adguard-rebind-protection",
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Res = s.genDNSFilterMessage(d, res)
|
||||||
|
log.Debug("DNSRebind: Matched %s by response: %s", d.Req.Question[0].Name, host)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
113
internal/dnsforward/rebind_test.go
Normal file
113
internal/dnsforward/rebind_test.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package dnsforward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRebindingPrivateAddresses(t *testing.T) {
|
||||||
|
c, _ := newRebindChecker(nil)
|
||||||
|
|
||||||
|
r1 := byte(rand.Int31() & 0xFE)
|
||||||
|
r2 := byte(rand.Int31() & 0xFE)
|
||||||
|
r3 := byte(rand.Int31() & 0xFE)
|
||||||
|
|
||||||
|
for _, ip := range []net.IP{
|
||||||
|
net.IPv4(0, r1, r2, r3), /* 0.0.0.0/8 (RFC 5735 section 3. "here" network) */
|
||||||
|
net.IPv4(127, r1, r2, r3), /* 127.0.0.0/8 (loopback) */
|
||||||
|
net.IPv4(10, r1, r2, r3), /* 10.0.0.0/8 (private) */
|
||||||
|
net.IPv4(172, 0x10|byte(1&rand.Int31()), r2, r3), /* 172.16.0.0/12 (private) */
|
||||||
|
net.IPv4(192, 168, r2, r3), /* 192.168.0.0/16 (private) */
|
||||||
|
net.IPv4(169, 254, r2, r3), /* 169.254.0.0/16 (zeroconf) */
|
||||||
|
net.IPv4(192, 0, 2, r3), /* 192.0.2.0/24 (test-net) */
|
||||||
|
net.IPv4(198, 51, 100, r3), /* 198.51.100.0/24(test-net) */
|
||||||
|
net.IPv4(203, 0, 113, r3), /* 203.0.113.0/24 (test-net) */
|
||||||
|
net.IPv4(255, 255, 255, 255), /* 255.255.255.255/32 (broadcast)*/
|
||||||
|
|
||||||
|
/* RFC 6303 4.3 (unspecified & loopback) */
|
||||||
|
net.IPv6zero,
|
||||||
|
net.IPv6unspecified,
|
||||||
|
|
||||||
|
/* RFC 6303 4.4 */
|
||||||
|
/* RFC 6303 4.5 */
|
||||||
|
/* RFC 6303 4.6 */
|
||||||
|
net.IPv6interfacelocalallnodes,
|
||||||
|
net.IPv6linklocalallnodes,
|
||||||
|
net.IPv6linklocalallrouters,
|
||||||
|
|
||||||
|
/* (TODO) Check IPv4-mapped IPv6 addresses */
|
||||||
|
} {
|
||||||
|
assert.Truef(t, c.isRebindIP(ip), "%s is not a rebind", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRebindLocalhost(t *testing.T) {
|
||||||
|
c := &dnsRebindChecker{}
|
||||||
|
assert.False(t, c.isRebindHost("example.com"))
|
||||||
|
assert.False(t, c.isRebindHost("200.0.0.1"))
|
||||||
|
assert.True(t, c.isRebindHost("127.0.0.1"))
|
||||||
|
assert.True(t, c.isRebindHost("localhost"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsResponseRebind(t *testing.T) {
|
||||||
|
c, _ := newRebindChecker([]string{
|
||||||
|
"||totally-safe.com^",
|
||||||
|
})
|
||||||
|
s := &Server{
|
||||||
|
rebinding: c,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range []string{
|
||||||
|
"0.1.2.3", /* 0.0.0.0/8 (RFC 5735 section 3. "here" network) */
|
||||||
|
"127.1.2.3", /* 127.0.0.0/8 (loopback) */
|
||||||
|
"10.1.2.3", /* 10.0.0.0/8 (private) */
|
||||||
|
"172.16.2.3", /* 172.16.0.0/12 (private) */
|
||||||
|
"192.168.2.3", /* 192.168.0.0/16 (private) */
|
||||||
|
"169.254.2.3", /* 169.254.0.0/16 (zeroconf) */
|
||||||
|
"192.0.2.3", /* 192.0.2.0/24 (test-net) */
|
||||||
|
"198.51.100.3", /* 198.51.100.0/24(test-net) */
|
||||||
|
"203.0.113.3", /* 203.0.113.0/24 (test-net) */
|
||||||
|
"255.255.255.255", /* 255.255.255.255/32 (broadcast)*/
|
||||||
|
|
||||||
|
/* RFC 6303 4.3 (unspecified & loopback) */
|
||||||
|
net.IPv6zero.String(),
|
||||||
|
net.IPv6unspecified.String(),
|
||||||
|
|
||||||
|
/* RFC 6303 4.4 */
|
||||||
|
/* RFC 6303 4.5 */
|
||||||
|
/* RFC 6303 4.6 */
|
||||||
|
net.IPv6interfacelocalallnodes.String(),
|
||||||
|
net.IPv6linklocalallnodes.String(),
|
||||||
|
net.IPv6linklocalallrouters.String(),
|
||||||
|
|
||||||
|
"localhost",
|
||||||
|
} {
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
assert.Truef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-safe.com", host), "host: %s", host)
|
||||||
|
|
||||||
|
s.conf.RebindingProtectionEnabled = false
|
||||||
|
assert.Falsef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-safe.com", host), "host: %s", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, host := range []string{
|
||||||
|
"200.168.2.3",
|
||||||
|
"another-example.com",
|
||||||
|
} {
|
||||||
|
s.conf.RebindingProtectionEnabled = true
|
||||||
|
assert.Falsef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-legit.com", host), "host: %s", host)
|
||||||
|
|
||||||
|
s.conf.RebindingProtectionEnabled = false
|
||||||
|
assert.Falsef(t, s.isResponseRebind("example.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("totally-safe.com", host), "host: %s", host)
|
||||||
|
assert.Falsef(t, s.isResponseRebind("absolutely.totally-legit.com", host), "host: %s", host)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,14 +17,15 @@ const (
|
|||||||
filteringStatusAll = "all"
|
filteringStatusAll = "all"
|
||||||
filteringStatusFiltered = "filtered" // all kinds of filtering
|
filteringStatusFiltered = "filtered" // all kinds of filtering
|
||||||
|
|
||||||
filteringStatusBlocked = "blocked" // blocked or blocked services
|
filteringStatusBlocked = "blocked" // blocked or blocked services
|
||||||
filteringStatusBlockedService = "blocked_services" // blocked
|
filteringStatusBlockedService = "blocked_services" // blocked
|
||||||
filteringStatusBlockedSafebrowsing = "blocked_safebrowsing" // blocked by safebrowsing
|
filteringStatusBlockedSafebrowsing = "blocked_safebrowsing" // blocked by safebrowsing
|
||||||
filteringStatusBlockedParental = "blocked_parental" // blocked by parental control
|
filteringStatusBlockedParental = "blocked_parental" // blocked by parental control
|
||||||
filteringStatusWhitelisted = "whitelisted" // whitelisted
|
filteringStatusBlockedRebind = "blocked_dns_rebinding" // blocked by DNS rebinding protection
|
||||||
filteringStatusRewritten = "rewritten" // all kinds of rewrites
|
filteringStatusWhitelisted = "whitelisted" // whitelisted
|
||||||
filteringStatusSafeSearch = "safe_search" // enforced safe search
|
filteringStatusRewritten = "rewritten" // all kinds of rewrites
|
||||||
filteringStatusProcessed = "processed" // not blocked, not white-listed entries
|
filteringStatusSafeSearch = "safe_search" // enforced safe search
|
||||||
|
filteringStatusProcessed = "processed" // not blocked, not white-listed entries
|
||||||
)
|
)
|
||||||
|
|
||||||
// filteringStatusValues -- array with all possible filteringStatus values
|
// filteringStatusValues -- array with all possible filteringStatus values
|
||||||
@@ -32,7 +33,7 @@ var filteringStatusValues = []string{
|
|||||||
filteringStatusAll, filteringStatusFiltered, filteringStatusBlocked,
|
filteringStatusAll, filteringStatusFiltered, filteringStatusBlocked,
|
||||||
filteringStatusBlockedService, filteringStatusBlockedSafebrowsing, filteringStatusBlockedParental,
|
filteringStatusBlockedService, filteringStatusBlockedSafebrowsing, filteringStatusBlockedParental,
|
||||||
filteringStatusWhitelisted, filteringStatusRewritten, filteringStatusSafeSearch,
|
filteringStatusWhitelisted, filteringStatusRewritten, filteringStatusSafeSearch,
|
||||||
filteringStatusProcessed,
|
filteringStatusProcessed, filteringStatusBlockedRebind,
|
||||||
}
|
}
|
||||||
|
|
||||||
// searchCriteria - every search request may contain a list of different search criteria
|
// searchCriteria - every search request may contain a list of different search criteria
|
||||||
|
|||||||
@@ -190,6 +190,7 @@
|
|||||||
- 'blocked'
|
- 'blocked'
|
||||||
- 'blocked_safebrowsing'
|
- 'blocked_safebrowsing'
|
||||||
- 'blocked_parental'
|
- 'blocked_parental'
|
||||||
|
- 'blocked_dns_rebinding'
|
||||||
- 'whitelisted'
|
- 'whitelisted'
|
||||||
- 'rewritten'
|
- 'rewritten'
|
||||||
- 'safe_search'
|
- 'safe_search'
|
||||||
|
|||||||
Reference in New Issue
Block a user