+ client: handle EDNS Client Subnet setting

This commit is contained in:
Ildar Kamalov 2019-12-05 16:00:20 +03:00
parent 19a94bf789
commit 97e77cab64
10 changed files with 115 additions and 42 deletions

View File

@ -24,6 +24,7 @@
"form_error_ip_format": "Invalid IP format", "form_error_ip_format": "Invalid IP format",
"form_error_mac_format": "Invalid MAC format", "form_error_mac_format": "Invalid MAC format",
"form_error_positive": "Must be greater than 0", "form_error_positive": "Must be greater than 0",
"form_error_negative": "Must be equal to 0 or greater",
"dhcp_form_gateway_input": "Gateway IP", "dhcp_form_gateway_input": "Gateway IP",
"dhcp_form_subnet_input": "Subnet mask", "dhcp_form_subnet_input": "Subnet mask",
"dhcp_form_range_title": "Range of IP addresses", "dhcp_form_range_title": "Range of IP addresses",
@ -187,7 +188,7 @@
"query_log_disabled": "The query log is disabled and can be configured in the <0>settings</0>", "query_log_disabled": "The query log is disabled and can be configured in the <0>settings</0>",
"query_log_strict_search": "Use double quotes for strict search", "query_log_strict_search": "Use double quotes for strict search",
"query_log_retention_confirm": "Are you sure you want to change query log retention? If you decrease the interval value, some data will be lost", "query_log_retention_confirm": "Are you sure you want to change query log retention? If you decrease the interval value, some data will be lost",
"dns_config": "DNS configuration", "dns_config": "DNS server configuration",
"blocking_mode": "Blocking mode", "blocking_mode": "Blocking mode",
"nxdomain": "NXDOMAIN", "nxdomain": "NXDOMAIN",
"null_ip": "Null IP", "null_ip": "Null IP",
@ -196,6 +197,13 @@
"blocking_ipv6": "Blocking IPv6", "blocking_ipv6": "Blocking IPv6",
"form_enter_rate_limit": "Enter rate limit", "form_enter_rate_limit": "Enter rate limit",
"rate_limit": "Rate limit", "rate_limit": "Rate limit",
"edns_enable": "Enable EDNS Client Subnet",
"edns_cs_desc": "If enabled, AdGuard Home will be sending clients' subnets to the DNS servers.",
"rate_limit_desc": "The number of requests per second that a single client is allowed to make (0: unlimited)",
"blocking_ipv4_desc": "IP address to be returned for a blocked A request",
"blocking_ipv6_desc": "IP address to be returned for a blocked AAAA request",
"blocking_mode_desc": "<0>NXDOMAIN Respond with NXDOMAIN code;</0> <0>Null IP Respond with zero IP address (0.0.0.0 for A; :: for AAAA);</0> <0>Custom IP - Respond with a manually set IP address.</0>",
"upstream_dns_client_desc": "If you keep this field empty, AdGuard Home will use the servers configured in the <0>DNS settings</0>.",
"source_label": "Source", "source_label": "Source",
"found_in_known_domain_db": "Found in the known domains database.", "found_in_known_domain_db": "Found in the known domains database.",
"category_label": "Category", "category_label": "Category",

View File

@ -225,6 +225,11 @@ let Form = (props) => {
</div> </div>
</div> </div>
<div label="upstream" title={props.t('upstream_dns')}> <div label="upstream" title={props.t('upstream_dns')}>
<div className="form__desc mb-3">
<Trans components={[<a href="#dns" key="0">link</a>]}>
upstream_dns_client_desc
</Trans>
</div>
<Field <Field
id="upstreams" id="upstreams"
name="upstreams" name="upstreams"

View File

@ -5,8 +5,17 @@ import { Field, reduxForm, formValueSelector } from 'redux-form';
import { Trans, withNamespaces } from 'react-i18next'; import { Trans, withNamespaces } from 'react-i18next';
import flow from 'lodash/flow'; import flow from 'lodash/flow';
import { renderField, renderRadioField, required, ipv4, ipv6, isPositive, toNumber } from '../../../helpers/form'; import {
import { BLOCKING_MODES } from '../../../helpers/constants'; renderField,
renderRadioField,
renderSelectField,
required,
ipv4,
ipv6,
biggerOrEqualZero,
toNumber,
} from '../../../../helpers/form';
import { BLOCKING_MODES } from '../../../../helpers/constants';
const getFields = (processing, t) => Object.values(BLOCKING_MODES).map(mode => ( const getFields = (processing, t) => Object.values(BLOCKING_MODES).map(mode => (
<Field <Field
@ -27,9 +36,12 @@ let Form = ({
<div className="row"> <div className="row">
<div className="col-12 col-sm-6"> <div className="col-12 col-sm-6">
<div className="form__group form__group--settings"> <div className="form__group form__group--settings">
<label htmlFor="ratelimit"> <label htmlFor="ratelimit" className="form__label form__label--with-desc">
<Trans>rate_limit</Trans> <Trans>rate_limit</Trans>
</label> </label>
<div className="form__desc form__desc--top">
<Trans>rate_limit_desc</Trans>
</div>
<Field <Field
name="ratelimit" name="ratelimit"
type="number" type="number"
@ -37,15 +49,30 @@ let Form = ({
className="form-control" className="form-control"
placeholder={t('form_enter_rate_limit')} placeholder={t('form_enter_rate_limit')}
normalize={toNumber} normalize={toNumber}
validate={[required, isPositive]} validate={[required, biggerOrEqualZero]}
/> />
</div> </div>
</div> </div>
<div className="col-12"> <div className="col-12">
<div className="form__group form__group--settings mb-3"> <div className="form__group form__group--settings">
<label className="form__label"> <Field
name="edns_cs_enabled"
type="checkbox"
component={renderSelectField}
placeholder={t('edns_enable')}
disabled={processing}
subtitle={t('edns_cs_desc')}
/>
</div>
</div>
<div className="col-12">
<div className="form__group form__group--settings mb-4">
<label className="form__label form__label--with-desc">
<Trans>blocking_mode</Trans> <Trans>blocking_mode</Trans>
</label> </label>
<div className="form__desc form__desc--top">
<Trans components={[<div key="0">text</div>]}>blocking_mode_desc</Trans>
</div>
<div className="custom-controls-stacked"> <div className="custom-controls-stacked">
{getFields(processing, t)} {getFields(processing, t)}
</div> </div>
@ -55,9 +82,12 @@ let Form = ({
<Fragment> <Fragment>
<div className="col-12 col-sm-6"> <div className="col-12 col-sm-6">
<div className="form__group form__group--settings"> <div className="form__group form__group--settings">
<label htmlFor="blocking_ipv4"> <label htmlFor="blocking_ipv4" className="form__label form__label--with-desc">
<Trans>blocking_ipv4</Trans> <Trans>blocking_ipv4</Trans>
</label> </label>
<div className="form__desc form__desc--top">
<Trans>blocking_ipv4_desc</Trans>
</div>
<Field <Field
name="blocking_ipv4" name="blocking_ipv4"
component={renderField} component={renderField}
@ -69,9 +99,12 @@ let Form = ({
</div> </div>
<div className="col-12 col-sm-6"> <div className="col-12 col-sm-6">
<div className="form__group form__group--settings"> <div className="form__group form__group--settings">
<label htmlFor="ip_address"> <label htmlFor="ip_address" className="form__label form__label--with-desc">
<Trans>blocking_ipv6</Trans> <Trans>blocking_ipv6</Trans>
</label> </label>
<div className="form__desc form__desc--top">
<Trans>blocking_ipv6_desc</Trans>
</div>
<Field <Field
name="blocking_ipv6" name="blocking_ipv6"
component={renderField} component={renderField}

View File

@ -2,10 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { withNamespaces } from 'react-i18next'; import { withNamespaces } from 'react-i18next';
import Card from '../../ui/Card'; import Card from '../../../ui/Card';
import Form from './Form'; import Form from './Form';
const DnsConfig = ({ t, dnsConfig, setDnsConfig }) => { const Config = ({ t, dnsConfig, setDnsConfig }) => {
const handleFormSubmit = (values) => { const handleFormSubmit = (values) => {
setDnsConfig(values); setDnsConfig(values);
}; };
@ -15,6 +15,7 @@ const DnsConfig = ({ t, dnsConfig, setDnsConfig }) => {
ratelimit, ratelimit,
blocking_ipv4, blocking_ipv4,
blocking_ipv6, blocking_ipv6,
edns_cs_enabled,
processingSetConfig, processingSetConfig,
} = dnsConfig; } = dnsConfig;
@ -31,6 +32,7 @@ const DnsConfig = ({ t, dnsConfig, setDnsConfig }) => {
blocking_mode, blocking_mode,
blocking_ipv4, blocking_ipv4,
blocking_ipv6, blocking_ipv6,
edns_cs_enabled,
}} }}
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
processing={processingSetConfig} processing={processingSetConfig}
@ -40,10 +42,10 @@ const DnsConfig = ({ t, dnsConfig, setDnsConfig }) => {
); );
}; };
DnsConfig.propTypes = { Config.propTypes = {
dnsConfig: PropTypes.object.isRequired, dnsConfig: PropTypes.object.isRequired,
setDnsConfig: PropTypes.func.isRequired, setDnsConfig: PropTypes.func.isRequired,
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
}; };
export default withNamespaces()(DnsConfig); export default withNamespaces()(Config);

View File

@ -5,6 +5,7 @@ import { withNamespaces } from 'react-i18next';
import Upstream from './Upstream'; import Upstream from './Upstream';
import Access from './Access'; import Access from './Access';
import Rewrites from './Rewrites'; import Rewrites from './Rewrites';
import Config from './Config';
import PageTitle from '../../ui/PageTitle'; import PageTitle from '../../ui/PageTitle';
import Loading from '../../ui/Loading'; import Loading from '../../ui/Loading';
@ -13,6 +14,7 @@ class Dns extends Component {
this.props.getDnsSettings(); this.props.getDnsSettings();
this.props.getAccessList(); this.props.getAccessList();
this.props.getRewritesList(); this.props.getRewritesList();
this.props.getDnsConfig();
} }
render() { render() {
@ -29,12 +31,18 @@ class Dns extends Component {
addRewrite, addRewrite,
deleteRewrite, deleteRewrite,
toggleRewritesModal, toggleRewritesModal,
dnsConfig,
setDnsConfig,
} = this.props; } = this.props;
const isDataLoading = const isDataLoading = dashboard.processingDnsSettings
dashboard.processingDnsSettings || access.processing || rewrites.processing; || access.processing
const isDataReady = || rewrites.processing
!dashboard.processingDnsSettings && !access.processing && !rewrites.processing; || dnsConfig.processingGetConfig;
const isDataReady = !dashboard.processingDnsSettings
&& !access.processing
&& !rewrites.processing
&& !dnsConfig.processingGetConfig;
return ( return (
<Fragment> <Fragment>
@ -42,6 +50,10 @@ class Dns extends Component {
{isDataLoading && <Loading />} {isDataLoading && <Loading />}
{isDataReady && ( {isDataReady && (
<Fragment> <Fragment>
<Config
dnsConfig={dnsConfig}
setDnsConfig={setDnsConfig}
/>
<Upstream <Upstream
upstreamDns={dashboard.upstreamDns} upstreamDns={dashboard.upstreamDns}
bootstrapDns={dashboard.bootstrapDns} bootstrapDns={dashboard.bootstrapDns}
@ -80,6 +92,9 @@ Dns.propTypes = {
deleteRewrite: PropTypes.func.isRequired, deleteRewrite: PropTypes.func.isRequired,
toggleRewritesModal: PropTypes.func.isRequired, toggleRewritesModal: PropTypes.func.isRequired,
getDnsSettings: PropTypes.func.isRequired, getDnsSettings: PropTypes.func.isRequired,
dnsConfig: PropTypes.object.isRequired,
setDnsConfig: PropTypes.func.isRequired,
getDnsConfig: PropTypes.func.isRequired,
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
}; };

View File

@ -6,7 +6,6 @@ import Services from './Services';
import StatsConfig from './StatsConfig'; import StatsConfig from './StatsConfig';
import LogsConfig from './LogsConfig'; import LogsConfig from './LogsConfig';
import FiltersConfig from './FiltersConfig'; import FiltersConfig from './FiltersConfig';
import DnsConfig from './DnsConfig';
import Checkbox from '../ui/Checkbox'; import Checkbox from '../ui/Checkbox';
import Loading from '../ui/Loading'; import Loading from '../ui/Loading';
@ -40,7 +39,6 @@ class Settings extends Component {
this.props.getStatsConfig(); this.props.getStatsConfig();
this.props.getLogsConfig(); this.props.getLogsConfig();
this.props.getFilteringStatus(); this.props.getFilteringStatus();
this.props.getDnsConfig();
} }
renderSettings = (settings) => { renderSettings = (settings) => {
@ -71,12 +69,10 @@ class Settings extends Component {
resetStats, resetStats,
stats, stats,
queryLogs, queryLogs,
dnsConfig,
setLogsConfig, setLogsConfig,
clearLogs, clearLogs,
filtering, filtering,
setFiltersConfig, setFiltersConfig,
setDnsConfig,
t, t,
} = this.props; } = this.props;
@ -106,12 +102,6 @@ class Settings extends Component {
</div> </div>
</Card> </Card>
</div> </div>
<div className="col-md-12">
<DnsConfig
dnsConfig={dnsConfig}
setDnsConfig={setDnsConfig}
/>
</div>
<div className="col-md-12"> <div className="col-md-12">
<LogsConfig <LogsConfig
enabled={queryLogs.enabled} enabled={queryLogs.enabled}
@ -154,7 +144,6 @@ Settings.propTypes = {
resetStats: PropTypes.func.isRequired, resetStats: PropTypes.func.isRequired,
setFiltersConfig: PropTypes.func.isRequired, setFiltersConfig: PropTypes.func.isRequired,
getFilteringStatus: PropTypes.func.isRequired, getFilteringStatus: PropTypes.func.isRequired,
getDnsConfig: PropTypes.func.isRequired,
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
}; };

View File

@ -7,17 +7,19 @@ import {
deleteRewrite, deleteRewrite,
toggleRewritesModal, toggleRewritesModal,
} from '../actions/rewrites'; } from '../actions/rewrites';
import { getDnsConfig, setDnsConfig } from '../actions/dnsConfig';
import Dns from '../components/Settings/Dns'; import Dns from '../components/Settings/Dns';
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
const { const {
dashboard, settings, access, rewrites, dashboard, settings, access, rewrites, dnsConfig,
} = state; } = state;
const props = { const props = {
dashboard, dashboard,
settings, settings,
access, access,
rewrites, rewrites,
dnsConfig,
}; };
return props; return props;
}; };
@ -33,6 +35,8 @@ const mapDispatchToProps = {
deleteRewrite, deleteRewrite,
toggleRewritesModal, toggleRewritesModal,
getDnsSettings, getDnsSettings,
getDnsConfig,
setDnsConfig,
}; };
export default connect( export default connect(

View File

@ -4,12 +4,11 @@ import { getBlockedServices, setBlockedServices } from '../actions/services';
import { getStatsConfig, setStatsConfig, resetStats } from '../actions/stats'; import { getStatsConfig, setStatsConfig, resetStats } from '../actions/stats';
import { clearLogs, getLogsConfig, setLogsConfig } from '../actions/queryLogs'; import { clearLogs, getLogsConfig, setLogsConfig } from '../actions/queryLogs';
import { getFilteringStatus, setFiltersConfig } from '../actions/filtering'; import { getFilteringStatus, setFiltersConfig } from '../actions/filtering';
import { getDnsConfig, setDnsConfig } from '../actions/dnsConfig';
import Settings from '../components/Settings'; import Settings from '../components/Settings';
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
const { const {
settings, services, stats, queryLogs, filtering, dnsConfig, settings, services, stats, queryLogs, filtering,
} = state; } = state;
const props = { const props = {
settings, settings,
@ -17,7 +16,6 @@ const mapStateToProps = (state) => {
stats, stats,
queryLogs, queryLogs,
filtering, filtering,
dnsConfig,
}; };
return props; return props;
}; };
@ -35,8 +33,6 @@ const mapDispatchToProps = {
setLogsConfig, setLogsConfig,
getFilteringStatus, getFilteringStatus,
setFiltersConfig, setFiltersConfig,
getDnsConfig,
setDnsConfig,
}; };
export default connect( export default connect(

View File

@ -189,6 +189,13 @@ export const isPositive = (value) => {
return false; return false;
}; };
export const biggerOrEqualZero = (value) => {
if (value < 0) {
return <Trans>form_error_negative</Trans>;
}
return false;
};
export const port = (value) => { export const port = (value) => {
if ((value || value === 0) && (value < 80 || value > 65535)) { if ((value || value === 0) && (value < 80 || value > 65535)) {
return <Trans>form_error_port_range</Trans>; return <Trans>form_error_port_range</Trans>;

View File

@ -3,16 +3,29 @@ import { handleActions } from 'redux-actions';
import * as actions from '../actions/dnsConfig'; import * as actions from '../actions/dnsConfig';
import { BLOCKING_MODES } from '../helpers/constants'; import { BLOCKING_MODES } from '../helpers/constants';
const DEFAULT_BLOCKING_IPV4 = '0.0.0.0';
const DEFAULT_BLOCKING_IPV6 = '::';
const dnsConfig = handleActions( const dnsConfig = handleActions(
{ {
[actions.getDnsConfigRequest]: state => ({ ...state, processingGetConfig: true }), [actions.getDnsConfigRequest]: state => ({ ...state, processingGetConfig: true }),
[actions.getDnsConfigFailure]: state => [actions.getDnsConfigFailure]: state =>
({ ...state, processingGetConfig: false }), ({ ...state, processingGetConfig: false }),
[actions.getDnsConfigSuccess]: (state, { payload }) => ({ [actions.getDnsConfigSuccess]: (state, { payload }) => {
...state, const {
...payload, blocking_ipv4,
processingGetConfig: false, blocking_ipv6,
}), ...values
} = payload;
return {
...state,
...values,
blocking_ipv4: blocking_ipv4 || DEFAULT_BLOCKING_IPV4,
blocking_ipv6: blocking_ipv6 || DEFAULT_BLOCKING_IPV6,
processingGetConfig: false,
};
},
[actions.setDnsConfigRequest]: state => ({ ...state, processingSetConfig: true }), [actions.setDnsConfigRequest]: state => ({ ...state, processingSetConfig: true }),
[actions.setDnsConfigFailure]: state => [actions.setDnsConfigFailure]: state =>
@ -28,8 +41,9 @@ const dnsConfig = handleActions(
processingSetConfig: false, processingSetConfig: false,
blocking_mode: BLOCKING_MODES.nxdomain, blocking_mode: BLOCKING_MODES.nxdomain,
ratelimit: 20, ratelimit: 20,
blocking_ipv4: '', blocking_ipv4: DEFAULT_BLOCKING_IPV4,
blocking_ipv6: '', blocking_ipv6: DEFAULT_BLOCKING_IPV6,
edns_cs_enabled: false,
}, },
); );