diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 44a64bea..bb83a92d 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -24,6 +24,7 @@ "form_error_ip_format": "Invalid IP format", "form_error_mac_format": "Invalid MAC format", "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_subnet_input": "Subnet mask", "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_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", - "dns_config": "DNS configuration", + "dns_config": "DNS server configuration", "blocking_mode": "Blocking mode", "nxdomain": "NXDOMAIN", "null_ip": "Null IP", @@ -196,6 +197,13 @@ "blocking_ipv6": "Blocking IPv6", "form_enter_rate_limit": "Enter 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", "found_in_known_domain_db": "Found in the known domains database.", "category_label": "Category", diff --git a/client/src/components/Settings/Clients/Form.js b/client/src/components/Settings/Clients/Form.js index 63199d76..897e1fc5 100644 --- a/client/src/components/Settings/Clients/Form.js +++ b/client/src/components/Settings/Clients/Form.js @@ -225,6 +225,11 @@ let Form = (props) => { </div> </div> <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 id="upstreams" name="upstreams" diff --git a/client/src/components/Settings/DnsConfig/Form.js b/client/src/components/Settings/Dns/Config/Form.js similarity index 67% rename from client/src/components/Settings/DnsConfig/Form.js rename to client/src/components/Settings/Dns/Config/Form.js index 4cfdff35..d6139fc6 100644 --- a/client/src/components/Settings/DnsConfig/Form.js +++ b/client/src/components/Settings/Dns/Config/Form.js @@ -5,8 +5,17 @@ import { Field, reduxForm, formValueSelector } from 'redux-form'; import { Trans, withNamespaces } from 'react-i18next'; import flow from 'lodash/flow'; -import { renderField, renderRadioField, required, ipv4, ipv6, isPositive, toNumber } from '../../../helpers/form'; -import { BLOCKING_MODES } from '../../../helpers/constants'; +import { + 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 => ( <Field @@ -27,9 +36,12 @@ let Form = ({ <div className="row"> <div className="col-12 col-sm-6"> <div className="form__group form__group--settings"> - <label htmlFor="ratelimit"> + <label htmlFor="ratelimit" className="form__label form__label--with-desc"> <Trans>rate_limit</Trans> - </label> + </label> + <div className="form__desc form__desc--top"> + <Trans>rate_limit_desc</Trans> + </div> <Field name="ratelimit" type="number" @@ -37,15 +49,30 @@ let Form = ({ className="form-control" placeholder={t('form_enter_rate_limit')} normalize={toNumber} - validate={[required, isPositive]} + validate={[required, biggerOrEqualZero]} /> </div> </div> <div className="col-12"> - <div className="form__group form__group--settings mb-3"> - <label className="form__label"> + <div className="form__group form__group--settings"> + <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> </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"> {getFields(processing, t)} </div> @@ -55,9 +82,12 @@ let Form = ({ <Fragment> <div className="col-12 col-sm-6"> <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> </label> + <div className="form__desc form__desc--top"> + <Trans>blocking_ipv4_desc</Trans> + </div> <Field name="blocking_ipv4" component={renderField} @@ -69,9 +99,12 @@ let Form = ({ </div> <div className="col-12 col-sm-6"> <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> </label> + <div className="form__desc form__desc--top"> + <Trans>blocking_ipv6_desc</Trans> + </div> <Field name="blocking_ipv6" component={renderField} diff --git a/client/src/components/Settings/DnsConfig/index.js b/client/src/components/Settings/Dns/Config/index.js similarity index 83% rename from client/src/components/Settings/DnsConfig/index.js rename to client/src/components/Settings/Dns/Config/index.js index c0f0fca0..6404a416 100644 --- a/client/src/components/Settings/DnsConfig/index.js +++ b/client/src/components/Settings/Dns/Config/index.js @@ -2,10 +2,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import { withNamespaces } from 'react-i18next'; -import Card from '../../ui/Card'; +import Card from '../../../ui/Card'; import Form from './Form'; -const DnsConfig = ({ t, dnsConfig, setDnsConfig }) => { +const Config = ({ t, dnsConfig, setDnsConfig }) => { const handleFormSubmit = (values) => { setDnsConfig(values); }; @@ -15,6 +15,7 @@ const DnsConfig = ({ t, dnsConfig, setDnsConfig }) => { ratelimit, blocking_ipv4, blocking_ipv6, + edns_cs_enabled, processingSetConfig, } = dnsConfig; @@ -31,6 +32,7 @@ const DnsConfig = ({ t, dnsConfig, setDnsConfig }) => { blocking_mode, blocking_ipv4, blocking_ipv6, + edns_cs_enabled, }} onSubmit={handleFormSubmit} processing={processingSetConfig} @@ -40,10 +42,10 @@ const DnsConfig = ({ t, dnsConfig, setDnsConfig }) => { ); }; -DnsConfig.propTypes = { +Config.propTypes = { dnsConfig: PropTypes.object.isRequired, setDnsConfig: PropTypes.func.isRequired, t: PropTypes.func.isRequired, }; -export default withNamespaces()(DnsConfig); +export default withNamespaces()(Config); diff --git a/client/src/components/Settings/Dns/index.js b/client/src/components/Settings/Dns/index.js index f64b3f3c..77d27b58 100644 --- a/client/src/components/Settings/Dns/index.js +++ b/client/src/components/Settings/Dns/index.js @@ -5,6 +5,7 @@ import { withNamespaces } from 'react-i18next'; import Upstream from './Upstream'; import Access from './Access'; import Rewrites from './Rewrites'; +import Config from './Config'; import PageTitle from '../../ui/PageTitle'; import Loading from '../../ui/Loading'; @@ -13,6 +14,7 @@ class Dns extends Component { this.props.getDnsSettings(); this.props.getAccessList(); this.props.getRewritesList(); + this.props.getDnsConfig(); } render() { @@ -29,12 +31,18 @@ class Dns extends Component { addRewrite, deleteRewrite, toggleRewritesModal, + dnsConfig, + setDnsConfig, } = this.props; - const isDataLoading = - dashboard.processingDnsSettings || access.processing || rewrites.processing; - const isDataReady = - !dashboard.processingDnsSettings && !access.processing && !rewrites.processing; + const isDataLoading = dashboard.processingDnsSettings + || access.processing + || rewrites.processing + || dnsConfig.processingGetConfig; + const isDataReady = !dashboard.processingDnsSettings + && !access.processing + && !rewrites.processing + && !dnsConfig.processingGetConfig; return ( <Fragment> @@ -42,6 +50,10 @@ class Dns extends Component { {isDataLoading && <Loading />} {isDataReady && ( <Fragment> + <Config + dnsConfig={dnsConfig} + setDnsConfig={setDnsConfig} + /> <Upstream upstreamDns={dashboard.upstreamDns} bootstrapDns={dashboard.bootstrapDns} @@ -80,6 +92,9 @@ Dns.propTypes = { deleteRewrite: PropTypes.func.isRequired, toggleRewritesModal: PropTypes.func.isRequired, getDnsSettings: PropTypes.func.isRequired, + dnsConfig: PropTypes.object.isRequired, + setDnsConfig: PropTypes.func.isRequired, + getDnsConfig: PropTypes.func.isRequired, t: PropTypes.func.isRequired, }; diff --git a/client/src/components/Settings/index.js b/client/src/components/Settings/index.js index 5fcc72cd..0603f2cd 100644 --- a/client/src/components/Settings/index.js +++ b/client/src/components/Settings/index.js @@ -6,7 +6,6 @@ import Services from './Services'; import StatsConfig from './StatsConfig'; import LogsConfig from './LogsConfig'; import FiltersConfig from './FiltersConfig'; -import DnsConfig from './DnsConfig'; import Checkbox from '../ui/Checkbox'; import Loading from '../ui/Loading'; @@ -40,7 +39,6 @@ class Settings extends Component { this.props.getStatsConfig(); this.props.getLogsConfig(); this.props.getFilteringStatus(); - this.props.getDnsConfig(); } renderSettings = (settings) => { @@ -71,12 +69,10 @@ class Settings extends Component { resetStats, stats, queryLogs, - dnsConfig, setLogsConfig, clearLogs, filtering, setFiltersConfig, - setDnsConfig, t, } = this.props; @@ -106,12 +102,6 @@ class Settings extends Component { </div> </Card> </div> - <div className="col-md-12"> - <DnsConfig - dnsConfig={dnsConfig} - setDnsConfig={setDnsConfig} - /> - </div> <div className="col-md-12"> <LogsConfig enabled={queryLogs.enabled} @@ -154,7 +144,6 @@ Settings.propTypes = { resetStats: PropTypes.func.isRequired, setFiltersConfig: PropTypes.func.isRequired, getFilteringStatus: PropTypes.func.isRequired, - getDnsConfig: PropTypes.func.isRequired, t: PropTypes.func.isRequired, }; diff --git a/client/src/containers/Dns.js b/client/src/containers/Dns.js index 9969382c..f32e1510 100644 --- a/client/src/containers/Dns.js +++ b/client/src/containers/Dns.js @@ -7,17 +7,19 @@ import { deleteRewrite, toggleRewritesModal, } from '../actions/rewrites'; +import { getDnsConfig, setDnsConfig } from '../actions/dnsConfig'; import Dns from '../components/Settings/Dns'; const mapStateToProps = (state) => { const { - dashboard, settings, access, rewrites, + dashboard, settings, access, rewrites, dnsConfig, } = state; const props = { dashboard, settings, access, rewrites, + dnsConfig, }; return props; }; @@ -33,6 +35,8 @@ const mapDispatchToProps = { deleteRewrite, toggleRewritesModal, getDnsSettings, + getDnsConfig, + setDnsConfig, }; export default connect( diff --git a/client/src/containers/Settings.js b/client/src/containers/Settings.js index f3199303..866765de 100644 --- a/client/src/containers/Settings.js +++ b/client/src/containers/Settings.js @@ -4,12 +4,11 @@ import { getBlockedServices, setBlockedServices } from '../actions/services'; import { getStatsConfig, setStatsConfig, resetStats } from '../actions/stats'; import { clearLogs, getLogsConfig, setLogsConfig } from '../actions/queryLogs'; import { getFilteringStatus, setFiltersConfig } from '../actions/filtering'; -import { getDnsConfig, setDnsConfig } from '../actions/dnsConfig'; import Settings from '../components/Settings'; const mapStateToProps = (state) => { const { - settings, services, stats, queryLogs, filtering, dnsConfig, + settings, services, stats, queryLogs, filtering, } = state; const props = { settings, @@ -17,7 +16,6 @@ const mapStateToProps = (state) => { stats, queryLogs, filtering, - dnsConfig, }; return props; }; @@ -35,8 +33,6 @@ const mapDispatchToProps = { setLogsConfig, getFilteringStatus, setFiltersConfig, - getDnsConfig, - setDnsConfig, }; export default connect( diff --git a/client/src/helpers/form.js b/client/src/helpers/form.js index 55e0b0a0..283f0975 100644 --- a/client/src/helpers/form.js +++ b/client/src/helpers/form.js @@ -189,6 +189,13 @@ export const isPositive = (value) => { return false; }; +export const biggerOrEqualZero = (value) => { + if (value < 0) { + return <Trans>form_error_negative</Trans>; + } + return false; +}; + export const port = (value) => { if ((value || value === 0) && (value < 80 || value > 65535)) { return <Trans>form_error_port_range</Trans>; diff --git a/client/src/reducers/dnsConfig.js b/client/src/reducers/dnsConfig.js index c2807261..5c7c2b88 100644 --- a/client/src/reducers/dnsConfig.js +++ b/client/src/reducers/dnsConfig.js @@ -3,16 +3,29 @@ import { handleActions } from 'redux-actions'; import * as actions from '../actions/dnsConfig'; import { BLOCKING_MODES } from '../helpers/constants'; +const DEFAULT_BLOCKING_IPV4 = '0.0.0.0'; +const DEFAULT_BLOCKING_IPV6 = '::'; + const dnsConfig = handleActions( { [actions.getDnsConfigRequest]: state => ({ ...state, processingGetConfig: true }), [actions.getDnsConfigFailure]: state => ({ ...state, processingGetConfig: false }), - [actions.getDnsConfigSuccess]: (state, { payload }) => ({ - ...state, - ...payload, - processingGetConfig: false, - }), + [actions.getDnsConfigSuccess]: (state, { payload }) => { + const { + blocking_ipv4, + 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.setDnsConfigFailure]: state => @@ -28,8 +41,9 @@ const dnsConfig = handleActions( processingSetConfig: false, blocking_mode: BLOCKING_MODES.nxdomain, ratelimit: 20, - blocking_ipv4: '', - blocking_ipv6: '', + blocking_ipv4: DEFAULT_BLOCKING_IPV4, + blocking_ipv6: DEFAULT_BLOCKING_IPV6, + edns_cs_enabled: false, }, );