Merge pull request #154 in DNS/adguard-dns from fix/596 to master
* commit 'b74eded414cf3f31da6feb185619afab85233b5a': (34 commits) [change] control: fix issues from review [change] config: fix default upstreams list * client: typo * client: remove log * client: fix grammar [change] control: add upstreams validation before dns config test [change] control: add upstreams validation [change] control: update bootstrap DNS check * client: remove empty elements from upstream and bootstrap * client: locales and pass object to testUpstream [change] config, control, openapi: fix issues from reviw [fix] control: fix json decode for upstream config * client: upstream form [change] control, openapi: Handle upstreams test with JSON [change] upgrade_test: rework tests [change] upgrade_test: add test for upgrade [change] control: Remove unuseful check [change] control: Fix issues from review [change] dnsforward: Add comments for public fields [change] control: Handle upstream config with JSON ...
This commit is contained in:
commit
f857ed74ec
|
@ -79,7 +79,7 @@
|
||||||
"no_settings": "No settings",
|
"no_settings": "No settings",
|
||||||
"general_settings": "General settings",
|
"general_settings": "General settings",
|
||||||
"upstream_dns": "Upstream DNS servers",
|
"upstream_dns": "Upstream DNS servers",
|
||||||
"upstream_dns_hint": "If you keep this field empty, AdGuard Home will use <a href='https:\/\/1.1.1.1\/' target='_blank'>Cloudflare DNS<\/a> as an upstream. Use tls:\/\/ prefix for DNS over TLS servers.",
|
"upstream_dns_hint": "If you keep this field empty, AdGuard Home will use <a href='https:\/\/1.1.1.1\/' target='_blank'>Cloudflare DNS<\/a> as an upstream.",
|
||||||
"test_upstream_btn": "Test upstreams",
|
"test_upstream_btn": "Test upstreams",
|
||||||
"apply_btn": "Apply",
|
"apply_btn": "Apply",
|
||||||
"disabled_filtering_toast": "Disabled filtering",
|
"disabled_filtering_toast": "Disabled filtering",
|
||||||
|
@ -246,5 +246,8 @@
|
||||||
"form_error_equal": "Shouldn't be equal",
|
"form_error_equal": "Shouldn't be equal",
|
||||||
"form_error_password": "Password mismatched",
|
"form_error_password": "Password mismatched",
|
||||||
"reset_settings": "Reset settings",
|
"reset_settings": "Reset settings",
|
||||||
"update_announcement": "AdGuard Home {{version}} is now available! <0>Click here<\/0> for more info."
|
"update_announcement": "AdGuard Home {{version}} is now available! <0>Click here</0> for more info.",
|
||||||
}
|
"upstream_parallel": "Use parallel queries to speed up resolving by simultaneously querying all upstream servers",
|
||||||
|
"bootstrap_dns": "Bootstrap DNS servers",
|
||||||
|
"bootstrap_dns_desc": "Bootstrap DNS servers are used to resolve IP addresses of the DOH/DOT resolvers you specify as upstreams."
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import round from 'lodash/round';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { showLoading, hideLoading } from 'react-redux-loading-bar';
|
import { showLoading, hideLoading } from 'react-redux-loading-bar';
|
||||||
|
|
||||||
import { normalizeHistory, normalizeFilteringStatus, normalizeLogs } from '../helpers/helpers';
|
import { normalizeHistory, normalizeFilteringStatus, normalizeLogs, normalizeTextarea } from '../helpers/helpers';
|
||||||
import { SETTINGS_NAMES } from '../helpers/constants';
|
import { SETTINGS_NAMES } from '../helpers/constants';
|
||||||
import Api from '../api/Api';
|
import Api from '../api/Api';
|
||||||
|
|
||||||
|
@ -452,10 +452,18 @@ export const setUpstreamRequest = createAction('SET_UPSTREAM_REQUEST');
|
||||||
export const setUpstreamFailure = createAction('SET_UPSTREAM_FAILURE');
|
export const setUpstreamFailure = createAction('SET_UPSTREAM_FAILURE');
|
||||||
export const setUpstreamSuccess = createAction('SET_UPSTREAM_SUCCESS');
|
export const setUpstreamSuccess = createAction('SET_UPSTREAM_SUCCESS');
|
||||||
|
|
||||||
export const setUpstream = url => async (dispatch) => {
|
export const setUpstream = config => async (dispatch) => {
|
||||||
dispatch(setUpstreamRequest());
|
dispatch(setUpstreamRequest());
|
||||||
try {
|
try {
|
||||||
await apiClient.setUpstream(url);
|
const values = { ...config };
|
||||||
|
values.bootstrap_dns = (
|
||||||
|
values.bootstrap_dns && normalizeTextarea(values.bootstrap_dns)
|
||||||
|
) || [];
|
||||||
|
values.upstream_dns = (
|
||||||
|
values.upstream_dns && normalizeTextarea(values.upstream_dns)
|
||||||
|
) || [];
|
||||||
|
|
||||||
|
await apiClient.setUpstream(values);
|
||||||
dispatch(addSuccessToast('updated_upstream_dns_toast'));
|
dispatch(addSuccessToast('updated_upstream_dns_toast'));
|
||||||
dispatch(setUpstreamSuccess());
|
dispatch(setUpstreamSuccess());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -468,11 +476,18 @@ export const testUpstreamRequest = createAction('TEST_UPSTREAM_REQUEST');
|
||||||
export const testUpstreamFailure = createAction('TEST_UPSTREAM_FAILURE');
|
export const testUpstreamFailure = createAction('TEST_UPSTREAM_FAILURE');
|
||||||
export const testUpstreamSuccess = createAction('TEST_UPSTREAM_SUCCESS');
|
export const testUpstreamSuccess = createAction('TEST_UPSTREAM_SUCCESS');
|
||||||
|
|
||||||
export const testUpstream = servers => async (dispatch) => {
|
export const testUpstream = config => async (dispatch) => {
|
||||||
dispatch(testUpstreamRequest());
|
dispatch(testUpstreamRequest());
|
||||||
try {
|
try {
|
||||||
const upstreamResponse = await apiClient.testUpstream(servers);
|
const values = { ...config };
|
||||||
|
values.bootstrap_dns = (
|
||||||
|
values.bootstrap_dns && normalizeTextarea(values.bootstrap_dns)
|
||||||
|
) || [];
|
||||||
|
values.upstream_dns = (
|
||||||
|
values.upstream_dns && normalizeTextarea(values.upstream_dns)
|
||||||
|
) || [];
|
||||||
|
|
||||||
|
const upstreamResponse = await apiClient.testUpstream(values);
|
||||||
const testMessages = Object.keys(upstreamResponse).map((key) => {
|
const testMessages = Object.keys(upstreamResponse).map((key) => {
|
||||||
const message = upstreamResponse[key];
|
const message = upstreamResponse[key];
|
||||||
if (message !== 'OK') {
|
if (message !== 'OK') {
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default class Api {
|
||||||
GLOBAL_QUERY_LOG = { path: 'querylog', method: 'GET' };
|
GLOBAL_QUERY_LOG = { path: 'querylog', method: 'GET' };
|
||||||
GLOBAL_QUERY_LOG_ENABLE = { path: 'querylog_enable', method: 'POST' };
|
GLOBAL_QUERY_LOG_ENABLE = { path: 'querylog_enable', method: 'POST' };
|
||||||
GLOBAL_QUERY_LOG_DISABLE = { path: 'querylog_disable', method: 'POST' };
|
GLOBAL_QUERY_LOG_DISABLE = { path: 'querylog_disable', method: 'POST' };
|
||||||
GLOBAL_SET_UPSTREAM_DNS = { path: 'set_upstream_dns', method: 'POST' };
|
GLOBAL_SET_UPSTREAM_DNS = { path: 'set_upstreams_config', method: 'POST' };
|
||||||
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
|
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
|
||||||
GLOBAL_VERSION = { path: 'version.json', method: 'GET' };
|
GLOBAL_VERSION = { path: 'version.json', method: 'GET' };
|
||||||
GLOBAL_ENABLE_PROTECTION = { path: 'enable_protection', method: 'POST' };
|
GLOBAL_ENABLE_PROTECTION = { path: 'enable_protection', method: 'POST' };
|
||||||
|
@ -110,7 +110,7 @@ export default class Api {
|
||||||
const { path, method } = this.GLOBAL_SET_UPSTREAM_DNS;
|
const { path, method } = this.GLOBAL_SET_UPSTREAM_DNS;
|
||||||
const config = {
|
const config = {
|
||||||
data: url,
|
data: url,
|
||||||
header: { 'Content-Type': 'text/plain' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
};
|
};
|
||||||
return this.makeRequest(path, method, config);
|
return this.makeRequest(path, method, config);
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ export default class Api {
|
||||||
const { path, method } = this.GLOBAL_TEST_UPSTREAM_DNS;
|
const { path, method } = this.GLOBAL_TEST_UPSTREAM_DNS;
|
||||||
const config = {
|
const config = {
|
||||||
data: servers,
|
data: servers,
|
||||||
header: { 'Content-Type': 'text/plain' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
};
|
};
|
||||||
return this.makeRequest(path, method, config);
|
return this.makeRequest(path, method, config);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import { Trans, withNamespaces } from 'react-i18next';
|
|
||||||
import Card from '../ui/Card';
|
|
||||||
|
|
||||||
class Upstream extends Component {
|
|
||||||
handleChange = (e) => {
|
|
||||||
const { value } = e.currentTarget;
|
|
||||||
this.props.handleUpstreamChange(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleSubmit = (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
this.props.handleUpstreamSubmit();
|
|
||||||
};
|
|
||||||
|
|
||||||
handleTest = () => {
|
|
||||||
this.props.handleUpstreamTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const testButtonClass = classnames({
|
|
||||||
'btn btn-primary btn-standard mr-2': true,
|
|
||||||
'btn btn-primary btn-standard mr-2 btn-loading': this.props.processingTestUpstream,
|
|
||||||
});
|
|
||||||
const { t } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
title={ t('upstream_dns') }
|
|
||||||
subtitle={ t('upstream_dns_hint') }
|
|
||||||
bodyType="card-body box-body--settings"
|
|
||||||
>
|
|
||||||
<div className="row">
|
|
||||||
<div className="col">
|
|
||||||
<form>
|
|
||||||
<textarea
|
|
||||||
className="form-control form-control--textarea"
|
|
||||||
value={this.props.upstreamDns}
|
|
||||||
onChange={this.handleChange}
|
|
||||||
/>
|
|
||||||
<div className="card-actions">
|
|
||||||
<button
|
|
||||||
className={testButtonClass}
|
|
||||||
type="button"
|
|
||||||
onClick={this.handleTest}
|
|
||||||
>
|
|
||||||
<Trans>test_upstream_btn</Trans>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="btn btn-success btn-standard"
|
|
||||||
type="submit"
|
|
||||||
onClick={this.handleSubmit}
|
|
||||||
>
|
|
||||||
<Trans>apply_btn</Trans>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<hr/>
|
|
||||||
<div className="list leading-loose">
|
|
||||||
<Trans>examples_title</Trans>:
|
|
||||||
<ol className="leading-loose">
|
|
||||||
<li>
|
|
||||||
<code>1.1.1.1</code> - { t('example_upstream_regular') }
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<code>tls://1dot1dot1dot1.cloudflare-dns.com</code> - <span dangerouslySetInnerHTML={{ __html: t('example_upstream_dot') }} />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<code>https://cloudflare-dns.com/dns-query</code> - <span dangerouslySetInnerHTML={{ __html: t('example_upstream_doh') }} />
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<code>tcp://1.1.1.1</code> - { t('example_upstream_tcp') }
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<code>sdns://...</code> - <span dangerouslySetInnerHTML={{ __html: t('example_upstream_sdns') }} />
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Upstream.propTypes = {
|
|
||||||
upstreamDns: PropTypes.string,
|
|
||||||
processingTestUpstream: PropTypes.bool,
|
|
||||||
handleUpstreamChange: PropTypes.func,
|
|
||||||
handleUpstreamSubmit: PropTypes.func,
|
|
||||||
handleUpstreamTest: PropTypes.func,
|
|
||||||
t: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default withNamespaces()(Upstream);
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Trans, withNamespaces } from 'react-i18next';
|
||||||
|
|
||||||
|
const Examples = props => (
|
||||||
|
<div className="list leading-loose">
|
||||||
|
<Trans>examples_title</Trans>:
|
||||||
|
<ol className="leading-loose">
|
||||||
|
<li>
|
||||||
|
<code>1.1.1.1</code> - { props.t('example_upstream_regular') }
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>tls://1dot1dot1dot1.cloudflare-dns.com</code> - <span dangerouslySetInnerHTML={{ __html: props.t('example_upstream_dot') }} />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>https://cloudflare-dns.com/dns-query</code> - <span dangerouslySetInnerHTML={{ __html: props.t('example_upstream_doh') }} />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>tcp://1.1.1.1</code> - { props.t('example_upstream_tcp') }
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>sdns://...</code> - <span dangerouslySetInnerHTML={{ __html: props.t('example_upstream_sdns') }} />
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
Examples.propTypes = {
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withNamespaces()(Examples);
|
|
@ -0,0 +1,139 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Field, reduxForm, formValueSelector } from 'redux-form';
|
||||||
|
import { Trans, withNamespaces } from 'react-i18next';
|
||||||
|
import flow from 'lodash/flow';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import { renderSelectField } from '../../../helpers/form';
|
||||||
|
|
||||||
|
let Form = (props) => {
|
||||||
|
const {
|
||||||
|
t,
|
||||||
|
handleSubmit,
|
||||||
|
testUpstream,
|
||||||
|
upstreamDns,
|
||||||
|
bootstrapDns,
|
||||||
|
allServers,
|
||||||
|
submitting,
|
||||||
|
invalid,
|
||||||
|
processingSetUpstream,
|
||||||
|
processingTestUpstream,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const testButtonClass = classnames({
|
||||||
|
'btn btn-primary btn-standard mr-2': true,
|
||||||
|
'btn btn-primary btn-standard mr-2 btn-loading': processingTestUpstream,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<label className="form__label" htmlFor="upstream_dns">
|
||||||
|
<Trans>upstream_dns</Trans>
|
||||||
|
</label>
|
||||||
|
<Field
|
||||||
|
id="upstream_dns"
|
||||||
|
name="upstream_dns"
|
||||||
|
component="textarea"
|
||||||
|
type="text"
|
||||||
|
className="form-control form-control--textarea"
|
||||||
|
placeholder={t('upstream_dns')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group form__group--settings">
|
||||||
|
<Field
|
||||||
|
name="all_servers"
|
||||||
|
type="checkbox"
|
||||||
|
component={renderSelectField}
|
||||||
|
placeholder={t('upstream_parallel')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-12">
|
||||||
|
<div className="form__group">
|
||||||
|
<label className="form__label" htmlFor="bootstrap_dns">
|
||||||
|
<Trans>bootstrap_dns</Trans>
|
||||||
|
</label>
|
||||||
|
<div className="form__desc form__desc--top">
|
||||||
|
<Trans>bootstrap_dns_desc</Trans>
|
||||||
|
</div>
|
||||||
|
<Field
|
||||||
|
id="bootstrap_dns"
|
||||||
|
name="bootstrap_dns"
|
||||||
|
component="textarea"
|
||||||
|
type="text"
|
||||||
|
className="form-control"
|
||||||
|
placeholder={t('bootstrap_dns')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="card-actions">
|
||||||
|
<div className="btn-list">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={testButtonClass}
|
||||||
|
onClick={() => testUpstream({
|
||||||
|
upstream_dns: upstreamDns,
|
||||||
|
bootstrap_dns: bootstrapDns,
|
||||||
|
all_servers: allServers,
|
||||||
|
})}
|
||||||
|
disabled={!upstreamDns || processingTestUpstream}
|
||||||
|
>
|
||||||
|
<Trans>test_upstream_btn</Trans>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-success btn-standard"
|
||||||
|
disabled={
|
||||||
|
submitting
|
||||||
|
|| invalid
|
||||||
|
|| processingSetUpstream
|
||||||
|
|| processingTestUpstream
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Trans>apply_btn</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Form.propTypes = {
|
||||||
|
handleSubmit: PropTypes.func,
|
||||||
|
testUpstream: PropTypes.func,
|
||||||
|
submitting: PropTypes.bool,
|
||||||
|
invalid: PropTypes.bool,
|
||||||
|
initialValues: PropTypes.object,
|
||||||
|
upstreamDns: PropTypes.string,
|
||||||
|
bootstrapDns: PropTypes.string,
|
||||||
|
allServers: PropTypes.bool,
|
||||||
|
processingTestUpstream: PropTypes.bool,
|
||||||
|
processingSetUpstream: PropTypes.bool,
|
||||||
|
t: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
const selector = formValueSelector('upstreamForm');
|
||||||
|
|
||||||
|
Form = connect((state) => {
|
||||||
|
const upstreamDns = selector(state, 'upstream_dns');
|
||||||
|
const bootstrapDns = selector(state, 'bootstrap_dns');
|
||||||
|
const allServers = selector(state, 'all_servers');
|
||||||
|
return {
|
||||||
|
upstreamDns,
|
||||||
|
bootstrapDns,
|
||||||
|
allServers,
|
||||||
|
};
|
||||||
|
})(Form);
|
||||||
|
|
||||||
|
export default flow([
|
||||||
|
withNamespaces(),
|
||||||
|
reduxForm({ form: 'upstreamForm' }),
|
||||||
|
])(Form);
|
|
@ -0,0 +1,67 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { withNamespaces } from 'react-i18next';
|
||||||
|
|
||||||
|
import Form from './Form';
|
||||||
|
import Examples from './Examples';
|
||||||
|
import Card from '../../ui/Card';
|
||||||
|
|
||||||
|
class Upstream extends Component {
|
||||||
|
handleSubmit = (values) => {
|
||||||
|
this.props.setUpstream(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleTest = (values) => {
|
||||||
|
this.props.testUpstream(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
t,
|
||||||
|
upstreamDns: upstream_dns,
|
||||||
|
bootstrapDns: bootstrap_dns,
|
||||||
|
allServers: all_servers,
|
||||||
|
processingSetUpstream,
|
||||||
|
processingTestUpstream,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={ t('upstream_dns') }
|
||||||
|
subtitle={ t('upstream_dns_hint') }
|
||||||
|
bodyType="card-body box-body--settings"
|
||||||
|
>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col">
|
||||||
|
<Form
|
||||||
|
initialValues={{
|
||||||
|
upstream_dns,
|
||||||
|
bootstrap_dns,
|
||||||
|
all_servers,
|
||||||
|
}}
|
||||||
|
testUpstream={this.handleTest}
|
||||||
|
onSubmit={this.handleSubmit}
|
||||||
|
processingTestUpstream={processingTestUpstream}
|
||||||
|
processingSetUpstream={processingSetUpstream}
|
||||||
|
/>
|
||||||
|
<hr/>
|
||||||
|
<Examples />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Upstream.propTypes = {
|
||||||
|
upstreamDns: PropTypes.string,
|
||||||
|
bootstrapDns: PropTypes.string,
|
||||||
|
allServers: PropTypes.bool,
|
||||||
|
setUpstream: PropTypes.func.isRequired,
|
||||||
|
testUpstream: PropTypes.func.isRequired,
|
||||||
|
processingSetUpstream: PropTypes.bool.isRequired,
|
||||||
|
processingTestUpstream: PropTypes.bool.isRequired,
|
||||||
|
t: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withNamespaces()(Upstream);
|
|
@ -41,22 +41,6 @@ class Settings extends Component {
|
||||||
this.props.getTlsStatus();
|
this.props.getTlsStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUpstreamChange = (value) => {
|
|
||||||
this.props.handleUpstreamChange({ upstreamDns: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleUpstreamSubmit = () => {
|
|
||||||
this.props.setUpstream(this.props.dashboard.upstreamDns);
|
|
||||||
};
|
|
||||||
|
|
||||||
handleUpstreamTest = () => {
|
|
||||||
if (this.props.dashboard.upstreamDns.length > 0) {
|
|
||||||
this.props.testUpstream(this.props.dashboard.upstreamDns);
|
|
||||||
} else {
|
|
||||||
this.props.addErrorToast({ error: this.props.t('no_servers_specified') });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSettings = (settings) => {
|
renderSettings = (settings) => {
|
||||||
if (Object.keys(settings).length > 0) {
|
if (Object.keys(settings).length > 0) {
|
||||||
return Object.keys(settings).map((key) => {
|
return Object.keys(settings).map((key) => {
|
||||||
|
@ -75,8 +59,7 @@ class Settings extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { settings, t } = this.props;
|
const { settings, dashboard, t } = this.props;
|
||||||
const { upstreamDns } = this.props.dashboard;
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<PageTitle title={ t('settings') } />
|
<PageTitle title={ t('settings') } />
|
||||||
|
@ -91,11 +74,13 @@ class Settings extends Component {
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<Upstream
|
<Upstream
|
||||||
upstreamDns={upstreamDns}
|
upstreamDns={dashboard.upstreamDns}
|
||||||
|
bootstrapDns={dashboard.bootstrapDns}
|
||||||
|
allServers={dashboard.allServers}
|
||||||
|
setUpstream={this.props.setUpstream}
|
||||||
|
testUpstream={this.props.testUpstream}
|
||||||
processingTestUpstream={settings.processingTestUpstream}
|
processingTestUpstream={settings.processingTestUpstream}
|
||||||
handleUpstreamChange={this.handleUpstreamChange}
|
processingSetUpstream={settings.processingSetUpstream}
|
||||||
handleUpstreamSubmit={this.handleUpstreamSubmit}
|
|
||||||
handleUpstreamTest={this.handleUpstreamTest}
|
|
||||||
/>
|
/>
|
||||||
<Encryption
|
<Encryption
|
||||||
encryption={this.props.encryption}
|
encryption={this.props.encryption}
|
||||||
|
@ -125,7 +110,6 @@ Settings.propTypes = {
|
||||||
toggleSetting: PropTypes.func,
|
toggleSetting: PropTypes.func,
|
||||||
handleUpstreamChange: PropTypes.func,
|
handleUpstreamChange: PropTypes.func,
|
||||||
setUpstream: PropTypes.func,
|
setUpstream: PropTypes.func,
|
||||||
upstream: PropTypes.string,
|
|
||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,10 @@
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox__label-text--long {
|
||||||
|
max-width: initial;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox__label-title {
|
.checkbox__label-title {
|
||||||
display: block;
|
display: block;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
|
@ -32,7 +32,7 @@ export const renderSelectField = ({
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<span className="checkbox__label">
|
<span className="checkbox__label">
|
||||||
<span className="checkbox__label-text">
|
<span className="checkbox__label-text checkbox__label-text--long">
|
||||||
<span className="checkbox__label-title">{placeholder}</span>
|
<span className="checkbox__label-title">{placeholder}</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -201,3 +201,5 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => {
|
||||||
window.location.replace(`http://${hostname}:${httpPort}/${hash}`);
|
window.location.replace(`http://${hostname}:${httpPort}/${hash}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const normalizeTextarea = text => text && text.replace(/[;, ]/g, '\n').split('\n').filter(n => n);
|
||||||
|
|
|
@ -51,6 +51,8 @@ const dashboard = handleActions({
|
||||||
dns_address: dnsAddress,
|
dns_address: dnsAddress,
|
||||||
querylog_enabled: queryLogEnabled,
|
querylog_enabled: queryLogEnabled,
|
||||||
upstream_dns: upstreamDns,
|
upstream_dns: upstreamDns,
|
||||||
|
bootstrap_dns: bootstrapDns,
|
||||||
|
all_servers: allServers,
|
||||||
protection_enabled: protectionEnabled,
|
protection_enabled: protectionEnabled,
|
||||||
language,
|
language,
|
||||||
http_port: httpPort,
|
http_port: httpPort,
|
||||||
|
@ -64,6 +66,8 @@ const dashboard = handleActions({
|
||||||
dnsAddress,
|
dnsAddress,
|
||||||
queryLogEnabled,
|
queryLogEnabled,
|
||||||
upstreamDns: upstreamDns.join('\n'),
|
upstreamDns: upstreamDns.join('\n'),
|
||||||
|
bootstrapDns: bootstrapDns.join('\n'),
|
||||||
|
allServers,
|
||||||
protectionEnabled,
|
protectionEnabled,
|
||||||
language,
|
language,
|
||||||
httpPort,
|
httpPort,
|
||||||
|
@ -171,7 +175,9 @@ const dashboard = handleActions({
|
||||||
logStatusProcessing: false,
|
logStatusProcessing: false,
|
||||||
processingVersion: true,
|
processingVersion: true,
|
||||||
processingFiltering: true,
|
processingFiltering: true,
|
||||||
upstreamDns: [],
|
upstreamDns: '',
|
||||||
|
bootstrapDns: '',
|
||||||
|
allServers: false,
|
||||||
protectionEnabled: false,
|
protectionEnabled: false,
|
||||||
processingProtection: false,
|
processingProtection: false,
|
||||||
httpPort: 80,
|
httpPort: 80,
|
||||||
|
|
|
@ -60,7 +60,8 @@ type dnsConfig struct {
|
||||||
UpstreamDNS []string `yaml:"upstream_dns"`
|
UpstreamDNS []string `yaml:"upstream_dns"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"}
|
var defaultDNS = []string{"https://dns.cloudflare.com/dns-query"}
|
||||||
|
var defaultBootstrap = []string{"1.1.1.1"}
|
||||||
|
|
||||||
type tlsConfigSettings struct {
|
type tlsConfigSettings struct {
|
||||||
Enabled bool `yaml:"enabled" json:"enabled"` // Enabled is the encryption (DOT/DOH/HTTPS) status
|
Enabled bool `yaml:"enabled" json:"enabled"` // Enabled is the encryption (DOT/DOH/HTTPS) status
|
||||||
|
@ -114,7 +115,8 @@ var config = configuration{
|
||||||
QueryLogEnabled: true,
|
QueryLogEnabled: true,
|
||||||
Ratelimit: 20,
|
Ratelimit: 20,
|
||||||
RefuseAny: true,
|
RefuseAny: true,
|
||||||
BootstrapDNS: "8.8.8.8:53",
|
BootstrapDNS: defaultBootstrap,
|
||||||
|
AllServers: false,
|
||||||
},
|
},
|
||||||
UpstreamDNS: defaultDNS,
|
UpstreamDNS: defaultDNS,
|
||||||
},
|
},
|
||||||
|
|
313
control.go
313
control.go
|
@ -27,6 +27,8 @@ const updatePeriod = time.Minute * 30
|
||||||
var versionCheckJSON []byte
|
var versionCheckJSON []byte
|
||||||
var versionCheckLastTime time.Time
|
var versionCheckLastTime time.Time
|
||||||
|
|
||||||
|
var protocols = []string{"tls://", "https://", "tcp://", "sdns://"}
|
||||||
|
|
||||||
const versionCheckURL = "https://adguardteam.github.io/AdGuardHome/version.json"
|
const versionCheckURL = "https://adguardteam.github.io/AdGuardHome/version.json"
|
||||||
const versionCheckPeriod = time.Hour * 8
|
const versionCheckPeriod = time.Hour * 8
|
||||||
|
|
||||||
|
@ -41,9 +43,7 @@ var client = &http.Client{
|
||||||
func returnOK(w http.ResponseWriter) {
|
func returnOK(w http.ResponseWriter) {
|
||||||
_, err := fmt.Fprintf(w, "OK\n")
|
_, err := fmt.Fprintf(w, "OK\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't write body: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,23 +85,20 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
"running": isRunning(),
|
"running": isRunning(),
|
||||||
"bootstrap_dns": config.DNS.BootstrapDNS,
|
"bootstrap_dns": config.DNS.BootstrapDNS,
|
||||||
"upstream_dns": config.DNS.UpstreamDNS,
|
"upstream_dns": config.DNS.UpstreamDNS,
|
||||||
|
"all_servers": config.DNS.AllServers,
|
||||||
"version": VersionString,
|
"version": VersionString,
|
||||||
"language": config.Language,
|
"language": config.Language,
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonVal, err := json.Marshal(data)
|
jsonVal, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to marshal status json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(jsonVal)
|
_, err = w.Write(jsonVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to write response json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,18 +136,14 @@ func handleQueryLog(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
jsonVal, err := json.Marshal(data)
|
jsonVal, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't marshal data into json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't marshal data into json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(jsonVal)
|
_, err = w.Write(jsonVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to write response json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,9 +189,7 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err := w.Write(statsJSON.Bytes())
|
_, err := w.Write(statsJSON.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't write body: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,9 +199,7 @@ func handleStatsReset(w http.ResponseWriter, r *http.Request) {
|
||||||
dnsServer.PurgeStats()
|
dnsServer.PurgeStats()
|
||||||
_, err := fmt.Fprintf(w, "OK\n")
|
_, err := fmt.Fprintf(w, "OK\n")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't write body: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,17 +210,13 @@ func handleStats(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
statsJSON, err := json.Marshal(summed)
|
statsJSON, err := json.Marshal(summed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to marshal status json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(statsJSON)
|
_, err = w.Write(statsJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to write response json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,40 +244,31 @@ func handleStatsHistory(w http.ResponseWriter, r *http.Request) {
|
||||||
// parse start and end time
|
// parse start and end time
|
||||||
startTime, err := time.Parse(time.RFC3339, r.URL.Query().Get("start_time"))
|
startTime, err := time.Parse(time.RFC3339, r.URL.Query().Get("start_time"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Must specify valid start_time parameter: %s", err)
|
httpError(w, http.StatusBadRequest, "Must specify valid start_time parameter: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
endTime, err := time.Parse(time.RFC3339, r.URL.Query().Get("end_time"))
|
endTime, err := time.Parse(time.RFC3339, r.URL.Query().Get("end_time"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Must specify valid end_time parameter: %s", err)
|
httpError(w, http.StatusBadRequest, "Must specify valid end_time parameter: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := dnsServer.GetStatsHistory(timeUnit, startTime, endTime)
|
data, err := dnsServer.GetStatsHistory(timeUnit, startTime, endTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Cannot get stats history: %s", err)
|
httpError(w, http.StatusBadRequest, "Cannot get stats history: %s", err)
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
statsJSON, err := json.Marshal(data)
|
statsJSON, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to marshal status json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(statsJSON)
|
_, err = w.Write(statsJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to write response json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -322,68 +298,108 @@ func sortByValue(m map[string]int) []string {
|
||||||
// upstreams configuration
|
// upstreams configuration
|
||||||
// -----------------------
|
// -----------------------
|
||||||
|
|
||||||
func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) {
|
// TODO this struct will become unnecessary after config file rework
|
||||||
|
type upstreamConfig struct {
|
||||||
|
Upstreams []string `json:"upstream_dns"` // Upstreams
|
||||||
|
BootstrapDNS []string `json:"bootstrap_dns"` // Bootstrap DNS
|
||||||
|
AllServers bool `json:"all_servers"` // --all-servers param for dnsproxy
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSetUpstreamConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Tracef("%s %v", r.Method, r.URL)
|
log.Tracef("%s %v", r.Method, r.URL)
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
newconfig := upstreamConfig{}
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&newconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Failed to read request body: %s", err)
|
httpError(w, http.StatusBadRequest, "Failed to parse new upstreams config json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// if empty body -- user is asking for default servers
|
|
||||||
hosts := strings.Fields(string(body))
|
|
||||||
|
|
||||||
if len(hosts) == 0 {
|
|
||||||
config.DNS.UpstreamDNS = defaultDNS
|
|
||||||
} else {
|
|
||||||
config.DNS.UpstreamDNS = hosts
|
|
||||||
}
|
|
||||||
|
|
||||||
err = writeAllConfigs()
|
for _, u := range newconfig.Upstreams {
|
||||||
if err != nil {
|
if err = validateUpstream(u); err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't write config file: %s", err)
|
httpError(w, http.StatusBadRequest, "%s can not be used as upstream cause: %s", u, err)
|
||||||
log.Error(errorText)
|
return
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
err = reconfigureDNSServer()
|
|
||||||
if err != nil {
|
config.DNS.UpstreamDNS = defaultDNS
|
||||||
errorText := fmt.Sprintf("Couldn't reconfigure the DNS server: %s", err)
|
if len(newconfig.Upstreams) > 0 {
|
||||||
log.Error(errorText)
|
config.DNS.UpstreamDNS = newconfig.Upstreams
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
_, err = fmt.Fprintf(w, "OK %d servers\n", len(hosts))
|
|
||||||
if err != nil {
|
// bootstrap servers are plain DNS only.
|
||||||
errorText := fmt.Sprintf("Couldn't write body: %s", err)
|
for _, host := range newconfig.BootstrapDNS {
|
||||||
log.Error(errorText)
|
if err := checkPlainDNS(host); err != nil {
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
httpError(w, http.StatusBadRequest, "%s can not be used as bootstrap dns cause: %s", host, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.DNS.BootstrapDNS = defaultBootstrap
|
||||||
|
if len(newconfig.BootstrapDNS) > 0 {
|
||||||
|
config.DNS.BootstrapDNS = newconfig.BootstrapDNS
|
||||||
|
}
|
||||||
|
|
||||||
|
config.DNS.AllServers = newconfig.AllServers
|
||||||
|
httpUpdateConfigReloadDNSReturnOK(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateUpstream(upstream string) error {
|
||||||
|
for _, proto := range protocols {
|
||||||
|
if strings.HasPrefix(upstream, proto) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(upstream, "://") {
|
||||||
|
return fmt.Errorf("wrong protocol")
|
||||||
|
}
|
||||||
|
|
||||||
|
return checkPlainDNS(upstream)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkPlainDNS checks if host is plain DNS
|
||||||
|
func checkPlainDNS(upstream string) error {
|
||||||
|
// Check if host is ip without port
|
||||||
|
if net.ParseIP(upstream) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if host is ip with port
|
||||||
|
ip, port, err := net.SplitHostPort(upstream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if net.ParseIP(ip) == nil {
|
||||||
|
return fmt.Errorf("%s is not a valid IP", ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = strconv.ParseInt(port, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s is not a valid port: %s", port, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
|
func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Tracef("%s %v", r.Method, r.URL)
|
log.Tracef("%s %v", r.Method, r.URL)
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
upstreamConfig := upstreamConfig{}
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&upstreamConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Failed to read request body: %s", err)
|
httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 400)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hosts := strings.Fields(string(body))
|
|
||||||
|
|
||||||
if len(hosts) == 0 {
|
if len(upstreamConfig.Upstreams) == 0 {
|
||||||
errorText := fmt.Sprintf("No servers specified")
|
httpError(w, http.StatusBadRequest, "No servers specified")
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result := map[string]string{}
|
result := map[string]string{}
|
||||||
|
|
||||||
for _, host := range hosts {
|
for _, host := range upstreamConfig.Upstreams {
|
||||||
err = checkDNS(host)
|
err = checkDNS(host, upstreamConfig.BootstrapDNS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("%v", err)
|
log.Info("%v", err)
|
||||||
result[host] = err.Error()
|
result[host] = err.Error()
|
||||||
|
@ -394,24 +410,28 @@ func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
jsonVal, err := json.Marshal(result)
|
jsonVal, err := json.Marshal(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to marshal status json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(jsonVal)
|
_, err = w.Write(jsonVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't write body: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDNS(input string) error {
|
func checkDNS(input string, bootstrap []string) error {
|
||||||
|
if err := validateUpstream(input); err != nil {
|
||||||
|
return fmt.Errorf("wrong upstream format: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bootstrap) == 0 {
|
||||||
|
bootstrap = defaultBootstrap
|
||||||
|
}
|
||||||
|
|
||||||
log.Debug("Checking if DNS %s works...", input)
|
log.Debug("Checking if DNS %s works...", input)
|
||||||
u, err := upstream.AddressToUpstream(input, upstream.Options{Timeout: dnsforward.DefaultTimeout})
|
u, err := upstream.AddressToUpstream(input, upstream.Options{Bootstrap: bootstrap, Timeout: dnsforward.DefaultTimeout})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to choose upstream for %s: %s", input, err)
|
return fmt.Errorf("failed to choose upstream for %s: %s", input, err)
|
||||||
}
|
}
|
||||||
|
@ -451,9 +471,7 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
resp, err := client.Get(versionCheckURL)
|
resp, err := client.Get(versionCheckURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't get version check json from %s: %T %s\n", versionCheckURL, err, err)
|
httpError(w, http.StatusBadGateway, "Couldn't get version check json from %s: %T %s\n", versionCheckURL, err, err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadGateway)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if resp != nil && resp.Body != nil {
|
if resp != nil && resp.Body != nil {
|
||||||
|
@ -463,18 +481,14 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
|
||||||
// read the body entirely
|
// read the body entirely
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't read response body from %s: %s", versionCheckURL, err)
|
httpError(w, http.StatusBadGateway, "Couldn't read response body from %s: %s", versionCheckURL, err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadGateway)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(body)
|
_, err = w.Write(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't write body: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
versionCheckLastTime = now
|
versionCheckLastTime = now
|
||||||
|
@ -510,18 +524,14 @@ func handleFilteringStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
config.RUnlock()
|
config.RUnlock()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to marshal status json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(jsonVal)
|
_, err = w.Write(jsonVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to write response json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -536,21 +546,19 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(f.URL) == 0 {
|
if len(f.URL) == 0 {
|
||||||
http.Error(w, "URL parameter was not specified", 400)
|
http.Error(w, "URL parameter was not specified", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if valid := govalidator.IsRequestURL(f.URL); !valid {
|
if valid := govalidator.IsRequestURL(f.URL); !valid {
|
||||||
http.Error(w, "URL parameter is not valid request URL", 400)
|
http.Error(w, "URL parameter is not valid request URL", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicates
|
// Check for duplicates
|
||||||
for i := range config.Filters {
|
for i := range config.Filters {
|
||||||
if config.Filters[i].URL == f.URL {
|
if config.Filters[i].URL == f.URL {
|
||||||
errorText := fmt.Sprintf("Filter URL already added -- %s", f.URL)
|
httpError(w, http.StatusBadRequest, "Filter URL already added -- %s", f.URL)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -562,30 +570,22 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) {
|
||||||
// Download the filter contents
|
// Download the filter contents
|
||||||
ok, err := f.update(true)
|
ok, err := f.update(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't fetch filter from url %s: %s", f.URL, err)
|
httpError(w, http.StatusBadRequest, "Couldn't fetch filter from url %s: %s", f.URL, err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.RulesCount == 0 {
|
if f.RulesCount == 0 {
|
||||||
errorText := fmt.Sprintf("Filter at the url %s has no rules (maybe it points to blank page?)", f.URL)
|
httpError(w, http.StatusBadRequest, "Filter at the url %s has no rules (maybe it points to blank page?)", f.URL)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
errorText := fmt.Sprintf("Filter at the url %s is invalid (maybe it points to blank page?)", f.URL)
|
httpError(w, http.StatusBadRequest, "Filter at the url %s is invalid (maybe it points to blank page?)", f.URL)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the filter contents
|
// Save the filter contents
|
||||||
err = f.save()
|
err = f.save()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Failed to save filter %d due to %s", f.ID, err)
|
httpError(w, http.StatusBadRequest, "Failed to save filter %d due to %s", f.ID, err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,24 +594,18 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) {
|
||||||
config.Filters = append(config.Filters, f)
|
config.Filters = append(config.Filters, f)
|
||||||
err = writeAllConfigs()
|
err = writeAllConfigs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't write config file: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = reconfigureDNSServer()
|
err = reconfigureDNSServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't reconfigure the DNS server: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't reconfigure the DNS server: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = fmt.Fprintf(w, "OK %d rules\n", f.RulesCount)
|
_, err = fmt.Fprintf(w, "OK %d rules\n", f.RulesCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Couldn't write body: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,20 +613,18 @@ func handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Tracef("%s %v", r.Method, r.URL)
|
log.Tracef("%s %v", r.Method, r.URL)
|
||||||
parameters, err := parseParametersFromBody(r.Body)
|
parameters, err := parseParametersFromBody(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("failed to parse parameters from body: %s", err)
|
httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 400)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url, ok := parameters["url"]
|
url, ok := parameters["url"]
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Error(w, "URL parameter was not specified", 400)
|
http.Error(w, "URL parameter was not specified", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if valid := govalidator.IsRequestURL(url); !valid {
|
if valid := govalidator.IsRequestURL(url); !valid {
|
||||||
http.Error(w, "URL parameter is not valid request URL", 400)
|
http.Error(w, "URL parameter is not valid request URL", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,8 +637,7 @@ func handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) {
|
||||||
// Remove the filter file
|
// Remove the filter file
|
||||||
err := os.Remove(filter.Path())
|
err := os.Remove(filter.Path())
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
errorText := fmt.Sprintf("Couldn't remove the filter file: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't remove the filter file: %s", err)
|
||||||
http.Error(w, errorText, http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -660,15 +651,13 @@ func handleFilteringEnableURL(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Tracef("%s %v", r.Method, r.URL)
|
log.Tracef("%s %v", r.Method, r.URL)
|
||||||
parameters, err := parseParametersFromBody(r.Body)
|
parameters, err := parseParametersFromBody(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("failed to parse parameters from body: %s", err)
|
httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 400)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url, ok := parameters["url"]
|
url, ok := parameters["url"]
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Error(w, "URL parameter was not specified", 400)
|
http.Error(w, "URL parameter was not specified", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,15 +689,13 @@ func handleFilteringDisableURL(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Tracef("%s %v", r.Method, r.URL)
|
log.Tracef("%s %v", r.Method, r.URL)
|
||||||
parameters, err := parseParametersFromBody(r.Body)
|
parameters, err := parseParametersFromBody(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("failed to parse parameters from body: %s", err)
|
httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 400)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url, ok := parameters["url"]
|
url, ok := parameters["url"]
|
||||||
if !ok {
|
if !ok {
|
||||||
http.Error(w, "URL parameter was not specified", 400)
|
http.Error(w, "URL parameter was not specified", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,9 +725,7 @@ func handleFilteringSetRules(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Tracef("%s %v", r.Method, r.URL)
|
log.Tracef("%s %v", r.Method, r.URL)
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Failed to read request body: %s", err)
|
httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 400)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -778,17 +763,13 @@ func handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
jsonVal, err := json.Marshal(data)
|
jsonVal, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to marshal status json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(jsonVal)
|
_, err = w.Write(jsonVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to write response json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -800,9 +781,7 @@ func handleParentalEnable(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Tracef("%s %v", r.Method, r.URL)
|
log.Tracef("%s %v", r.Method, r.URL)
|
||||||
parameters, err := parseParametersFromBody(r.Body)
|
parameters, err := parseParametersFromBody(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("failed to parse parameters from body: %s", err)
|
httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 400)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -859,18 +838,14 @@ func handleParentalStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
jsonVal, err := json.Marshal(data)
|
jsonVal, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to marshal status json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(jsonVal)
|
_, err = w.Write(jsonVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to write response json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -898,18 +873,14 @@ func handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
jsonVal, err := json.Marshal(data)
|
jsonVal, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to marshal status json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
_, err = w.Write(jsonVal)
|
_, err = w.Write(jsonVal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorText := fmt.Sprintf("Unable to write response json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err)
|
||||||
log.Error(errorText)
|
|
||||||
http.Error(w, errorText, 500)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1053,7 +1024,7 @@ func registerControlHandlers() {
|
||||||
http.HandleFunc("/control/querylog", postInstall(optionalAuth(ensureGET(handleQueryLog))))
|
http.HandleFunc("/control/querylog", postInstall(optionalAuth(ensureGET(handleQueryLog))))
|
||||||
http.HandleFunc("/control/querylog_enable", postInstall(optionalAuth(ensurePOST(handleQueryLogEnable))))
|
http.HandleFunc("/control/querylog_enable", postInstall(optionalAuth(ensurePOST(handleQueryLogEnable))))
|
||||||
http.HandleFunc("/control/querylog_disable", postInstall(optionalAuth(ensurePOST(handleQueryLogDisable))))
|
http.HandleFunc("/control/querylog_disable", postInstall(optionalAuth(ensurePOST(handleQueryLogDisable))))
|
||||||
http.HandleFunc("/control/set_upstream_dns", postInstall(optionalAuth(ensurePOST(handleSetUpstreamDNS))))
|
http.HandleFunc("/control/set_upstreams_config", postInstall(optionalAuth(ensurePOST(handleSetUpstreamConfig))))
|
||||||
http.HandleFunc("/control/test_upstream_dns", postInstall(optionalAuth(ensurePOST(handleTestUpstreamDNS))))
|
http.HandleFunc("/control/test_upstream_dns", postInstall(optionalAuth(ensurePOST(handleTestUpstreamDNS))))
|
||||||
http.HandleFunc("/control/i18n/change_language", postInstall(optionalAuth(ensurePOST(handleI18nChangeLanguage))))
|
http.HandleFunc("/control/i18n/change_language", postInstall(optionalAuth(ensurePOST(handleI18nChangeLanguage))))
|
||||||
http.HandleFunc("/control/i18n/current_language", postInstall(optionalAuth(ensureGET(handleI18nCurrentLanguage))))
|
http.HandleFunc("/control/i18n/current_language", postInstall(optionalAuth(ensureGET(handleI18nCurrentLanguage))))
|
||||||
|
|
3
dns.go
3
dns.go
|
@ -61,7 +61,7 @@ func generateServerConfig() dnsforward.ServerConfig {
|
||||||
for _, u := range config.DNS.UpstreamDNS {
|
for _, u := range config.DNS.UpstreamDNS {
|
||||||
opts := upstream.Options{
|
opts := upstream.Options{
|
||||||
Timeout: dnsforward.DefaultTimeout,
|
Timeout: dnsforward.DefaultTimeout,
|
||||||
Bootstrap: []string{config.DNS.BootstrapDNS},
|
Bootstrap: config.DNS.BootstrapDNS,
|
||||||
}
|
}
|
||||||
dnsUpstream, err := upstream.AddressToUpstream(u, opts)
|
dnsUpstream, err := upstream.AddressToUpstream(u, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -71,6 +71,7 @@ func generateServerConfig() dnsforward.ServerConfig {
|
||||||
}
|
}
|
||||||
newconfig.Upstreams = append(newconfig.Upstreams, dnsUpstream)
|
newconfig.Upstreams = append(newconfig.Upstreams, dnsUpstream)
|
||||||
}
|
}
|
||||||
|
newconfig.AllServers = config.DNS.AllServers
|
||||||
return newconfig
|
return newconfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bluele/gcache"
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/bluele/gcache"
|
||||||
"golang.org/x/net/publicsuffix"
|
"golang.org/x/net/publicsuffix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -62,11 +62,12 @@ type FilteringConfig struct {
|
||||||
ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of dnsfilter features
|
ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of dnsfilter features
|
||||||
FilteringEnabled bool `yaml:"filtering_enabled"` // whether or not use filter lists
|
FilteringEnabled bool `yaml:"filtering_enabled"` // whether or not use filter lists
|
||||||
BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600)
|
BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600)
|
||||||
QueryLogEnabled bool `yaml:"querylog_enabled"`
|
QueryLogEnabled bool `yaml:"querylog_enabled"` // if true, query log is enabled
|
||||||
Ratelimit int `yaml:"ratelimit"`
|
Ratelimit int `yaml:"ratelimit"` // max number of requests per second from a given IP (0 to disable)
|
||||||
RatelimitWhitelist []string `yaml:"ratelimit_whitelist"`
|
RatelimitWhitelist []string `yaml:"ratelimit_whitelist"` // a list of whitelisted client IP addresses
|
||||||
RefuseAny bool `yaml:"refuse_any"`
|
RefuseAny bool `yaml:"refuse_any"` // if true, refuse ANY requests
|
||||||
BootstrapDNS string `yaml:"bootstrap_dns"`
|
BootstrapDNS []string `yaml:"bootstrap_dns"` // a list of bootstrap DNS for DoH and DoT (plain DNS only)
|
||||||
|
AllServers bool `yaml:"all_servers"` // if true, parallel queries to all configured upstream servers are enabled
|
||||||
|
|
||||||
dnsfilter.Config `yaml:",inline"`
|
dnsfilter.Config `yaml:",inline"`
|
||||||
}
|
}
|
||||||
|
@ -163,6 +164,7 @@ func (s *Server) startInternal(config *ServerConfig) error {
|
||||||
CacheEnabled: true,
|
CacheEnabled: true,
|
||||||
Upstreams: s.Upstreams,
|
Upstreams: s.Upstreams,
|
||||||
Handler: s.handleDNSRequest,
|
Handler: s.handleDNSRequest,
|
||||||
|
AllServers: s.AllServers,
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.TLSListenAddr != nil && s.CertificateChain != "" && s.PrivateKey != "" {
|
if s.TLSListenAddr != nil && s.CertificateChain != "" && s.PrivateKey != "" {
|
||||||
|
|
|
@ -87,26 +87,21 @@ paths:
|
||||||
200:
|
200:
|
||||||
description: OK
|
description: OK
|
||||||
|
|
||||||
/set_upstream_dns:
|
/set_upstreams_config:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- global
|
- global
|
||||||
operationId: setUpstreamDNS
|
operationId: setUpstreamsConfig
|
||||||
summary: 'Set upstream DNS for coredns, empty value will reset it to default values'
|
summary: "Updates the current upstreams configuration"
|
||||||
consumes:
|
consumes:
|
||||||
- text/plain
|
- application/json
|
||||||
parameters:
|
parameters:
|
||||||
- in: body
|
- in: "body"
|
||||||
name: upstream
|
name: "body"
|
||||||
description: 'Upstream servers, separated by newline or space, port is optional after colon'
|
description: "Upstreams configuration JSON"
|
||||||
schema:
|
required: true
|
||||||
# TODO: use JSON
|
schema:
|
||||||
type: string
|
$ref: "#/definitions/UpstreamsConfig"
|
||||||
example: |
|
|
||||||
1.1.1.1
|
|
||||||
1.0.0.1
|
|
||||||
8.8.8.8 8.8.4.4
|
|
||||||
192.168.1.104:53535
|
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: OK
|
description: OK
|
||||||
|
@ -116,21 +111,15 @@ paths:
|
||||||
tags:
|
tags:
|
||||||
- global
|
- global
|
||||||
operationId: testUpstreamDNS
|
operationId: testUpstreamDNS
|
||||||
summary: 'Test upstream DNS'
|
summary: "Test upstream configuration"
|
||||||
consumes:
|
consumes:
|
||||||
- text/plain
|
- application/json
|
||||||
parameters:
|
parameters:
|
||||||
- in: body
|
- in: "body"
|
||||||
name: upstream
|
name: "body"
|
||||||
description: 'Upstream servers, separated by newline or space, port is optional after colon'
|
description: "Upstream configuration to be tested"
|
||||||
schema:
|
schema:
|
||||||
# TODO: use JSON
|
$ref: "#/definitions/UpstreamsConfig"
|
||||||
type: string
|
|
||||||
example: |
|
|
||||||
1.1.1.1
|
|
||||||
1.0.0.1
|
|
||||||
8.8.8.8 8.8.4.4
|
|
||||||
192.168.1.104:53535
|
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: 'Status of testing each requested server, with "OK" meaning that server works, any other text means an error.'
|
description: 'Status of testing each requested server, with "OK" meaning that server works, any other text means an error.'
|
||||||
|
@ -798,6 +787,33 @@ definitions:
|
||||||
language:
|
language:
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "en"
|
example: "en"
|
||||||
|
UpstreamsConfig:
|
||||||
|
type: "object"
|
||||||
|
description: "Upstreams configuration"
|
||||||
|
required:
|
||||||
|
- "bootstrap_dns"
|
||||||
|
- "upstream_dns"
|
||||||
|
- "all_servers"
|
||||||
|
properties:
|
||||||
|
bootstrap_dns:
|
||||||
|
type: "array"
|
||||||
|
description: 'Bootstrap servers, port is optional after colon. Empty value will reset it to default values'
|
||||||
|
items:
|
||||||
|
type: "string"
|
||||||
|
example:
|
||||||
|
- "8.8.8.8:53"
|
||||||
|
- "1.1.1.1:53"
|
||||||
|
upstream_dns:
|
||||||
|
type: "array"
|
||||||
|
description: 'Upstream servers, port is optional after colon. Empty value will reset it to default values'
|
||||||
|
items:
|
||||||
|
type: "string"
|
||||||
|
example:
|
||||||
|
- "tls://1.1.1.1"
|
||||||
|
- "tls://1.0.0.1"
|
||||||
|
all_servers:
|
||||||
|
type: "boolean"
|
||||||
|
description: "If true, parallel queries to all configured upstream servers are enabled"
|
||||||
Filter:
|
Filter:
|
||||||
type: "object"
|
type: "object"
|
||||||
description: "Filter subscription info"
|
description: "Filter subscription info"
|
||||||
|
|
65
upgrade.go
65
upgrade.go
|
@ -10,7 +10,7 @@ import (
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const currentSchemaVersion = 2 // used for upgrading from old configs to new config
|
const currentSchemaVersion = 3 // used for upgrading from old configs to new config
|
||||||
|
|
||||||
// Performs necessary upgrade operations if needed
|
// Performs necessary upgrade operations if needed
|
||||||
func upgradeConfig() error {
|
func upgradeConfig() error {
|
||||||
|
@ -59,12 +59,17 @@ func upgradeConfig() error {
|
||||||
func upgradeConfigSchema(oldVersion int, diskConfig *map[string]interface{}) error {
|
func upgradeConfigSchema(oldVersion int, diskConfig *map[string]interface{}) error {
|
||||||
switch oldVersion {
|
switch oldVersion {
|
||||||
case 0:
|
case 0:
|
||||||
err := upgradeSchema0to2(diskConfig)
|
err := upgradeSchema0to3(diskConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
err := upgradeSchema1to2(diskConfig)
|
err := upgradeSchema1to3(diskConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
err := upgradeSchema2to3(diskConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -135,12 +140,60 @@ func upgradeSchema1to2(diskConfig *map[string]interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// jump two schemas at once -- this time we just do it sequentially
|
// Third schema upgrade:
|
||||||
func upgradeSchema0to2(diskConfig *map[string]interface{}) error {
|
// Bootstrap DNS becomes an array
|
||||||
|
func upgradeSchema2to3(diskConfig *map[string]interface{}) error {
|
||||||
|
log.Printf("%s(): called", _Func())
|
||||||
|
|
||||||
|
// Let's read dns configuration from diskConfig
|
||||||
|
dnsConfig, ok := (*diskConfig)["dns"]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("no DNS configuration in config file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert interface{} to map[string]interface{}
|
||||||
|
newDNSConfig := make(map[string]interface{})
|
||||||
|
|
||||||
|
switch v := dnsConfig.(type) {
|
||||||
|
case map[interface{}]interface{}:
|
||||||
|
for k, v := range v {
|
||||||
|
newDNSConfig[fmt.Sprint(k)] = v
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("DNS configuration is not a map")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace bootstrap_dns value filed with new array contains old bootstrap_dns inside
|
||||||
|
if bootstrapDNS, ok := (newDNSConfig)["bootstrap_dns"]; ok {
|
||||||
|
newBootstrapConfig := []string{fmt.Sprint(bootstrapDNS)}
|
||||||
|
(newDNSConfig)["bootstrap_dns"] = newBootstrapConfig
|
||||||
|
(*diskConfig)["dns"] = newDNSConfig
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("no bootstrap DNS in DNS config")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bump schema version
|
||||||
|
(*diskConfig)["schema_version"] = 3
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// jump three schemas at once -- this time we just do it sequentially
|
||||||
|
func upgradeSchema0to3(diskConfig *map[string]interface{}) error {
|
||||||
err := upgradeSchema0to1(diskConfig)
|
err := upgradeSchema0to1(diskConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return upgradeSchema1to2(diskConfig)
|
return upgradeSchema1to3(diskConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// jump two schemas at once -- this time we just do it sequentially
|
||||||
|
func upgradeSchema1to3(diskConfig *map[string]interface{}) error {
|
||||||
|
err := upgradeSchema1to2(diskConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return upgradeSchema2to3(diskConfig)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpgrade1to2(t *testing.T) {
|
||||||
|
// let's create test config for 1 schema version
|
||||||
|
diskConfig := createTestDiskConfig(1)
|
||||||
|
|
||||||
|
// update config
|
||||||
|
err := upgradeSchema1to2(&diskConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't upgrade schema version from 1 to 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that schema version was bumped
|
||||||
|
compareSchemaVersion(t, diskConfig["schema_version"], 2)
|
||||||
|
|
||||||
|
// old coredns entry should be removed
|
||||||
|
_, ok := diskConfig["coredns"]
|
||||||
|
if ok {
|
||||||
|
t.Fatalf("Core DNS config was not removed after upgrade schema version from 1 to 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// pull out new dns config
|
||||||
|
dnsMap, ok := diskConfig["dns"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("No DNS config after upgrade schema version from 1 to 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// cast dns configurations to maps and compare them
|
||||||
|
oldDNSConfig := castInterfaceToMap(t, createTestDNSConfig(1))
|
||||||
|
newDNSConfig := castInterfaceToMap(t, dnsMap)
|
||||||
|
compareConfigs(t, &oldDNSConfig, &newDNSConfig)
|
||||||
|
|
||||||
|
// exclude dns config and schema version from disk config comparison
|
||||||
|
oldExcludedEntries := []string{"coredns", "schema_version"}
|
||||||
|
newExcludedEntries := []string{"dns", "schema_version"}
|
||||||
|
oldDiskConfig := createTestDiskConfig(1)
|
||||||
|
compareConfigsWithoutEntries(t, &oldDiskConfig, &diskConfig, oldExcludedEntries, newExcludedEntries)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpgrade2to3(t *testing.T) {
|
||||||
|
// let's create test config
|
||||||
|
diskConfig := createTestDiskConfig(2)
|
||||||
|
|
||||||
|
// upgrade schema from 2 to 3
|
||||||
|
err := upgradeSchema2to3(&diskConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Can't update schema version from 2 to 3: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check new schema version
|
||||||
|
compareSchemaVersion(t, diskConfig["schema_version"], 3)
|
||||||
|
|
||||||
|
// pull out new dns configuration
|
||||||
|
dnsMap, ok := diskConfig["dns"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("No dns config in new configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
// cast dns configuration to map
|
||||||
|
newDNSConfig := castInterfaceToMap(t, dnsMap)
|
||||||
|
|
||||||
|
// check if bootstrap DNS becomes an array
|
||||||
|
bootstrapDNS := newDNSConfig["bootstrap_dns"]
|
||||||
|
switch v := bootstrapDNS.(type) {
|
||||||
|
case []string:
|
||||||
|
if len(v) != 1 {
|
||||||
|
t.Fatalf("Wrong count of bootsrap DNS servers: %d", len(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v[0] != "8.8.8.8:53" {
|
||||||
|
t.Fatalf("Bootsrap DNS server is not 8.8.8.8:53 : %s", v[0])
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Fatalf("Wrong type for bootsrap DNS: %T", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// exclude bootstrap DNS from DNS configs comparison
|
||||||
|
excludedEntries := []string{"bootstrap_dns"}
|
||||||
|
oldDNSConfig := castInterfaceToMap(t, createTestDNSConfig(2))
|
||||||
|
compareConfigsWithoutEntries(t, &oldDNSConfig, &newDNSConfig, excludedEntries, excludedEntries)
|
||||||
|
|
||||||
|
// excluded dns config and schema version from disk config comparison
|
||||||
|
excludedEntries = []string{"dns", "schema_version"}
|
||||||
|
oldDiskConfig := createTestDiskConfig(2)
|
||||||
|
compareConfigsWithoutEntries(t, &oldDiskConfig, &diskConfig, excludedEntries, excludedEntries)
|
||||||
|
}
|
||||||
|
|
||||||
|
func castInterfaceToMap(t *testing.T, oldConfig interface{}) (newConfig map[string]interface{}) {
|
||||||
|
newConfig = make(map[string]interface{})
|
||||||
|
switch v := oldConfig.(type) {
|
||||||
|
case map[interface{}]interface{}:
|
||||||
|
for key, value := range v {
|
||||||
|
newConfig[fmt.Sprint(key)] = value
|
||||||
|
}
|
||||||
|
case map[string]interface{}:
|
||||||
|
for key, value := range v {
|
||||||
|
newConfig[key] = value
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Fatalf("DNS configuration is not a map")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// compareConfigsWithoutEntry removes entries from configs and returns result of compareConfigs
|
||||||
|
func compareConfigsWithoutEntries(t *testing.T, oldConfig, newConfig *map[string]interface{}, oldKey, newKey []string) {
|
||||||
|
for _, k := range oldKey {
|
||||||
|
delete(*oldConfig, k)
|
||||||
|
}
|
||||||
|
for _, k := range newKey {
|
||||||
|
delete(*newConfig, k)
|
||||||
|
}
|
||||||
|
compareConfigs(t, oldConfig, newConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// compares configs before and after schema upgrade
|
||||||
|
func compareConfigs(t *testing.T, oldConfig, newConfig *map[string]interface{}) {
|
||||||
|
if len(*oldConfig) != len(*newConfig) {
|
||||||
|
t.Fatalf("wrong config entries count! Before upgrade: %d; After upgrade: %d", len(*oldConfig), len(*oldConfig))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check old and new entries
|
||||||
|
for k, v := range *newConfig {
|
||||||
|
switch value := v.(type) {
|
||||||
|
case string:
|
||||||
|
if value != (*oldConfig)[k] {
|
||||||
|
t.Fatalf("wrong value for string %s. Before update: %s; After update: %s", k, (*oldConfig)[k], value)
|
||||||
|
}
|
||||||
|
case int:
|
||||||
|
if value != (*oldConfig)[k] {
|
||||||
|
t.Fatalf("wrong value for int %s. Before update: %d; After update: %d", k, (*oldConfig)[k], value)
|
||||||
|
}
|
||||||
|
case []string:
|
||||||
|
for i, line := range value {
|
||||||
|
if len((*oldConfig)[k].([]string)) != len(value) {
|
||||||
|
t.Fatalf("wrong array length for %s. Before update: %d; After update: %d", k, len((*oldConfig)[k].([]string)), len(value))
|
||||||
|
}
|
||||||
|
if (*oldConfig)[k].([]string)[i] != line {
|
||||||
|
t.Fatalf("wrong data for string array %s. Before update: %s; After update: %s", k, (*oldConfig)[k].([]string)[i], line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case bool:
|
||||||
|
if v != (*oldConfig)[k].(bool) {
|
||||||
|
t.Fatalf("wrong boolean value for %s", k)
|
||||||
|
}
|
||||||
|
case []filter:
|
||||||
|
if len((*oldConfig)[k].([]filter)) != len(value) {
|
||||||
|
t.Fatalf("wrong filters count. Before update: %d; After update: %d", len((*oldConfig)[k].([]filter)), len(value))
|
||||||
|
}
|
||||||
|
for i, newFilter := range value {
|
||||||
|
oldFilter := (*oldConfig)[k].([]filter)[i]
|
||||||
|
if oldFilter.Enabled != newFilter.Enabled || oldFilter.Name != newFilter.Name || oldFilter.RulesCount != newFilter.RulesCount {
|
||||||
|
t.Fatalf("old filter %s not equals new filter %s", oldFilter.Name, newFilter.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Fatalf("uknown data type for %s: %T", k, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compareSchemaVersion check if newSchemaVersion equals schemaVersion
|
||||||
|
func compareSchemaVersion(t *testing.T, newSchemaVersion interface{}, schemaVersion int) {
|
||||||
|
switch v := newSchemaVersion.(type) {
|
||||||
|
case int:
|
||||||
|
if v != schemaVersion {
|
||||||
|
t.Fatalf("Wrong schema version in new config file")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Fatalf("Schema version is not an integer after update")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestDiskConfig(schemaVersion int) (diskConfig map[string]interface{}) {
|
||||||
|
diskConfig = make(map[string]interface{})
|
||||||
|
diskConfig["language"] = "en"
|
||||||
|
diskConfig["filters"] = []filter{
|
||||||
|
{
|
||||||
|
URL: "https://filters.adtidy.org/android/filters/111_optimized.txt",
|
||||||
|
Name: "Latvian filter",
|
||||||
|
RulesCount: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
URL: "https://easylist.to/easylistgermany/easylistgermany.txt",
|
||||||
|
Name: "Germany filter",
|
||||||
|
RulesCount: 200,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
diskConfig["user_rules"] = []string{}
|
||||||
|
diskConfig["schema_version"] = schemaVersion
|
||||||
|
diskConfig["bind_host"] = "0.0.0.0"
|
||||||
|
diskConfig["bind_port"] = 80
|
||||||
|
diskConfig["auth_name"] = "name"
|
||||||
|
diskConfig["auth_pass"] = "pass"
|
||||||
|
dnsConfig := createTestDNSConfig(schemaVersion)
|
||||||
|
if schemaVersion > 1 {
|
||||||
|
diskConfig["dns"] = dnsConfig
|
||||||
|
} else {
|
||||||
|
diskConfig["coredns"] = dnsConfig
|
||||||
|
}
|
||||||
|
return diskConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestDNSConfig(schemaVersion int) map[interface{}]interface{} {
|
||||||
|
dnsConfig := make(map[interface{}]interface{})
|
||||||
|
dnsConfig["port"] = 53
|
||||||
|
dnsConfig["blocked_response_ttl"] = 10
|
||||||
|
dnsConfig["querylog_enabled"] = true
|
||||||
|
dnsConfig["ratelimit"] = 20
|
||||||
|
dnsConfig["bootstrap_dns"] = "8.8.8.8:53"
|
||||||
|
if schemaVersion > 2 {
|
||||||
|
dnsConfig["bootstrap_dns"] = []string{"8.8.8.8:53"}
|
||||||
|
}
|
||||||
|
dnsConfig["parental_sensitivity"] = 13
|
||||||
|
dnsConfig["ratelimit_whitelist"] = []string{}
|
||||||
|
dnsConfig["upstream_dns"] = []string{"tls://1.1.1.1", "tls://1.0.0.1", "8.8.8.8"}
|
||||||
|
dnsConfig["filtering_enabled"] = true
|
||||||
|
dnsConfig["refuse_any"] = true
|
||||||
|
dnsConfig["parental_enabled"] = true
|
||||||
|
dnsConfig["bind_host"] = "0.0.0.0"
|
||||||
|
dnsConfig["protection_enabled"] = true
|
||||||
|
dnsConfig["safesearch_enabled"] = true
|
||||||
|
dnsConfig["safebrowsing_enabled"] = true
|
||||||
|
return dnsConfig
|
||||||
|
}
|
Loading…
Reference in New Issue