From dd37c9651a0a5914a5e8c9be4c2f6ad23faf1a13 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Thu, 30 Jan 2020 11:57:51 +0300 Subject: [PATCH 1/3] - client: add initial flag for getLogs --- client/src/components/Logs/index.js | 7 ++++--- client/src/reducers/queryLogs.js | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/client/src/components/Logs/index.js b/client/src/components/Logs/index.js index 1281db28..f57fb704 100644 --- a/client/src/components/Logs/index.js +++ b/client/src/components/Logs/index.js @@ -25,7 +25,8 @@ import './Logs.css'; import CellWrap from '../ui/CellWrap'; const TABLE_FIRST_PAGE = 0; -const INITIAL_REQUEST_DATA = ['', TABLE_FIRST_PAGE, TABLE_DEFAULT_PAGE_SIZE]; +const INITIAL_REQUEST = true; +const INITIAL_REQUEST_DATA = ['', TABLE_FIRST_PAGE, INITIAL_REQUEST]; const FILTERED_REASON = 'Filtered'; class Logs extends Component { @@ -36,10 +37,10 @@ class Logs extends Component { this.props.getLogsConfig(); } - getLogs = (older_than, page) => { + getLogs = (older_than, page, initial) => { if (this.props.queryLogs.enabled) { this.props.getLogs({ - older_than, page, pageSize: TABLE_DEFAULT_PAGE_SIZE, + older_than, page, pageSize: TABLE_DEFAULT_PAGE_SIZE, initial, }); } }; diff --git a/client/src/reducers/queryLogs.js b/client/src/reducers/queryLogs.js index ee1fc91b..52f98fd0 100644 --- a/client/src/reducers/queryLogs.js +++ b/client/src/reducers/queryLogs.js @@ -56,9 +56,9 @@ const queryLogs = handleActions( [actions.getLogsFailure]: state => ({ ...state, processingGetLogs: false }), [actions.getLogsSuccess]: (state, { payload }) => { const { - logs, oldest, older_than, page, pageSize, + logs, oldest, older_than, page, pageSize, initial, } = payload; - let logsWithOffset = state.allLogs.length > 0 ? state.allLogs : logs; + let logsWithOffset = state.allLogs.length > 0 && !initial ? state.allLogs : logs; let allLogs = logs; if (older_than) { From 5c814b29e186a79678c3c96f7a741f44c3f2cec9 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Thu, 30 Jan 2020 13:58:54 +0300 Subject: [PATCH 2/3] Merge: + client: handle client block and unblock from the top clients table Closes #896 Squashed commit of the following: commit 776de2ae0a62823b8968cff79a9fa7ba350d7f1c Author: Ildar Kamalov Date: Thu Jan 30 11:13:41 2020 +0300 - client: fix normalizeTextarea and blocking button commit 399e6bc3893093632b09247eaf6493521a668c84 Author: Ildar Kamalov Date: Wed Jan 29 17:19:50 2020 +0300 + client: handle client block and unblock from the top clients table --- client/.eslintrc | 3 +- client/src/__locales/en.json | 6 +- client/src/actions/access.js | 50 +++++++++++- client/src/actions/index.js | 16 +--- client/src/actions/stats.js | 7 +- .../components/Dashboard/BlockedDomains.js | 2 +- client/src/components/Dashboard/Clients.js | 81 ++++++++++++++++--- .../components/Dashboard/QueriedDomains.js | 2 +- client/src/components/Dashboard/index.js | 21 ++++- client/src/components/Logs/Logs.css | 11 ++- client/src/containers/Dashboard.js | 7 +- client/src/helpers/constants.js | 5 ++ client/src/helpers/helpers.js | 19 ++++- client/src/reducers/access.js | 4 + 14 files changed, 195 insertions(+), 39 deletions(-) diff --git a/client/.eslintrc b/client/.eslintrc index aa104244..d5d5955b 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -48,6 +48,7 @@ "camelcase": "off", "no-console": ["warn", { "allow": ["warn", "error"] }], "import/no-extraneous-dependencies": ["error", { "devDependencies": true }], - "import/prefer-default-export": "off" + "import/prefer-default-export": "off", + "no-alert": "off" } } diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 9e11009a..961b47c7 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -458,5 +458,9 @@ "check_reason": "Reason: {{reason}}", "check_rule": "Rule: {{rule}}", "check_service": "Service name: {{service}}", - "check_not_found": "Doesn't exist in any filter" + "check_not_found": "Doesn't exist in any filter", + "client_confirm_block": "Are you sure you want to block the client \"{{ip}}\"?", + "client_confirm_unblock": "Are you sure you want to unblock the client \"{{ip}}\"?", + "client_blocked": "Client \"{{ip}}\" successfully blocked", + "client_unblocked": "Client \"{{ip}}\" successfully unblocked" } diff --git a/client/src/actions/access.js b/client/src/actions/access.js index 5b5272d7..1f51cea7 100644 --- a/client/src/actions/access.js +++ b/client/src/actions/access.js @@ -1,7 +1,11 @@ import { createAction } from 'redux-actions'; +import { t } from 'i18next'; + import apiClient from '../api/Api'; import { addErrorToast, addSuccessToast } from './index'; +import { getStats, getStatsConfig } from './stats'; import { normalizeTextarea } from '../helpers/helpers'; +import { ACTION } from '../helpers/constants'; export const getAccessListRequest = createAction('GET_ACCESS_LIST_REQUEST'); export const getAccessListFailure = createAction('GET_ACCESS_LIST_FAILURE'); @@ -28,9 +32,9 @@ export const setAccessList = config => async (dispatch) => { const { allowed_clients, disallowed_clients, blocked_hosts } = config; const values = { - allowed_clients: (allowed_clients && normalizeTextarea(allowed_clients)) || [], - disallowed_clients: (disallowed_clients && normalizeTextarea(disallowed_clients)) || [], - blocked_hosts: (blocked_hosts && normalizeTextarea(blocked_hosts)) || [], + allowed_clients: normalizeTextarea(allowed_clients), + disallowed_clients: normalizeTextarea(disallowed_clients), + blocked_hosts: normalizeTextarea(blocked_hosts), }; await apiClient.setAccessList(values); @@ -41,3 +45,43 @@ export const setAccessList = config => async (dispatch) => { dispatch(setAccessListFailure()); } }; + +export const toggleClientBlockRequest = createAction('TOGGLE_CLIENT_BLOCK_REQUEST'); +export const toggleClientBlockFailure = createAction('TOGGLE_CLIENT_BLOCK_FAILURE'); +export const toggleClientBlockSuccess = createAction('TOGGLE_CLIENT_BLOCK_SUCCESS'); + +export const toggleClientBlock = (type, ip) => async (dispatch, getState) => { + dispatch(toggleClientBlockRequest()); + try { + const { allowed_clients, disallowed_clients, blocked_hosts } = getState().access; + let updatedDisallowedClients = normalizeTextarea(disallowed_clients); + + if (type === ACTION.unblock && updatedDisallowedClients.includes(ip)) { + updatedDisallowedClients = updatedDisallowedClients.filter(client => client !== ip); + } else if (type === ACTION.block && !updatedDisallowedClients.includes(ip)) { + updatedDisallowedClients.push(ip); + } + + const values = { + allowed_clients: normalizeTextarea(allowed_clients), + blocked_hosts: normalizeTextarea(blocked_hosts), + disallowed_clients: updatedDisallowedClients, + }; + + await apiClient.setAccessList(values); + dispatch(toggleClientBlockSuccess()); + + if (type === ACTION.unblock) { + dispatch(addSuccessToast(t('client_unblocked', { ip }))); + } else if (type === ACTION.block) { + dispatch(addSuccessToast(t('client_blocked', { ip }))); + } + + dispatch(getStats()); + dispatch(getStatsConfig()); + dispatch(getAccessList()); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(toggleClientBlockFailure()); + } +}; diff --git a/client/src/actions/index.js b/client/src/actions/index.js index b3e4d2bb..0d212b1b 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -289,12 +289,8 @@ export const setUpstream = config => async (dispatch) => { dispatch(setUpstreamRequest()); try { const values = { ...config }; - values.bootstrap_dns = ( - values.bootstrap_dns && normalizeTextarea(values.bootstrap_dns) - ) || []; - values.upstream_dns = ( - values.upstream_dns && normalizeTextarea(values.upstream_dns) - ) || []; + values.bootstrap_dns = normalizeTextarea(values.bootstrap_dns); + values.upstream_dns = normalizeTextarea(values.upstream_dns); await apiClient.setUpstream(values); dispatch(addSuccessToast('updated_upstream_dns_toast')); @@ -313,12 +309,8 @@ export const testUpstream = config => async (dispatch) => { dispatch(testUpstreamRequest()); try { const values = { ...config }; - values.bootstrap_dns = ( - values.bootstrap_dns && normalizeTextarea(values.bootstrap_dns) - ) || []; - values.upstream_dns = ( - values.upstream_dns && normalizeTextarea(values.upstream_dns) - ) || []; + values.bootstrap_dns = normalizeTextarea(values.bootstrap_dns); + values.upstream_dns = normalizeTextarea(values.upstream_dns); const upstreamResponse = await apiClient.testUpstream(values); const testMessages = Object.keys(upstreamResponse).map((key) => { diff --git a/client/src/actions/stats.js b/client/src/actions/stats.js index 25897aab..7a12b203 100644 --- a/client/src/actions/stats.js +++ b/client/src/actions/stats.js @@ -2,7 +2,7 @@ import { createAction } from 'redux-actions'; import apiClient from '../api/Api'; import { addErrorToast, addSuccessToast } from './index'; -import { normalizeTopStats, secondsToMilliseconds, getParamsForClientsSearch, addClientInfo } from '../helpers/helpers'; +import { normalizeTopStats, secondsToMilliseconds, getParamsForClientsSearch, addClientInfo, addClientStatus } from '../helpers/helpers'; export const getStatsConfigRequest = createAction('GET_STATS_CONFIG_REQUEST'); export const getStatsConfigFailure = createAction('GET_STATS_CONFIG_FAILURE'); @@ -46,12 +46,15 @@ export const getStats = () => async (dispatch) => { const normalizedTopClients = normalizeTopStats(stats.top_clients); const clientsParams = getParamsForClientsSearch(normalizedTopClients, 'name'); const clients = await apiClient.findClients(clientsParams); + const accessData = await apiClient.getAccessList(); + const { disallowed_clients } = accessData; const topClientsWithInfo = addClientInfo(normalizedTopClients, clients, 'name'); + const topClientsWithStatus = addClientStatus(topClientsWithInfo, disallowed_clients, 'name'); const normalizedStats = { ...stats, top_blocked_domains: normalizeTopStats(stats.top_blocked_domains), - top_clients: topClientsWithInfo, + top_clients: topClientsWithStatus, top_queried_domains: normalizeTopStats(stats.top_queried_domains), avg_processing_time: secondsToMilliseconds(stats.avg_processing_time), }; diff --git a/client/src/components/Dashboard/BlockedDomains.js b/client/src/components/Dashboard/BlockedDomains.js index 42288ca8..3823d52d 100644 --- a/client/src/components/Dashboard/BlockedDomains.js +++ b/client/src/components/Dashboard/BlockedDomains.js @@ -58,7 +58,7 @@ const BlockedDomains = ({ noDataText={t('no_domains_found')} minRows={6} defaultPageSize={100} - className="-striped -highlight card-table-overflow stats__table" + className="-highlight card-table-overflow stats__table" /> ); diff --git a/client/src/components/Dashboard/Clients.js b/client/src/components/Dashboard/Clients.js index e83addcb..04c3270b 100644 --- a/client/src/components/Dashboard/Clients.js +++ b/client/src/components/Dashboard/Clients.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Fragment } from 'react'; import ReactTable from 'react-table'; import PropTypes from 'prop-types'; import { Trans, withNamespaces } from 'react-i18next'; @@ -28,17 +28,58 @@ const countCell = dnsQueries => return ; }; -const clientCell = t => +const renderBlockingButton = (blocked, ip, handleClick, processing) => { + let buttonProps = { + className: 'btn-outline-danger', + text: 'block_btn', + type: 'block', + }; + + if (blocked) { + buttonProps = { + className: 'btn-outline-secondary', + text: 'unblock_btn', + type: 'unblock', + }; + } + + return ( +
+ +
+ ); +}; + +const clientCell = (t, toggleClientStatus, processing) => function cell(row) { + const { original, value } = row; + const { blocked } = original; + return ( -
- {formatClientCell(row, t)} -
+ +
+ {formatClientCell(row, t)} +
+ {renderBlockingButton(blocked, value, toggleClientStatus, processing)} +
); }; const Clients = ({ - t, refreshButton, topClients, subtitle, dnsQueries, + t, + refreshButton, + topClients, + subtitle, + dnsQueries, + toggleClientStatus, + processingAccessSet, }) => ( ({ + data={topClients.map(({ + name: ip, count, info, blocked, + }) => ({ ip, count, info, + blocked, }))} columns={[ { @@ -58,7 +102,7 @@ const Clients = ({ accessor: 'ip', sortMethod: (a, b) => parseInt(a.replace(/\./g, ''), 10) - parseInt(b.replace(/\./g, ''), 10), - Cell: clientCell(t), + Cell: clientCell(t, toggleClientStatus, processingAccessSet), }, { Header: requests_count, @@ -72,7 +116,24 @@ const Clients = ({ noDataText={t('no_clients_found')} minRows={6} defaultPageSize={100} - className="-striped -highlight card-table-overflow" + className="-highlight card-table-overflow clients__table" + getTrProps={(_state, rowInfo) => { + if (!rowInfo) { + return {}; + } + + const { blocked } = rowInfo.original; + + if (blocked) { + return { + className: 'red', + }; + } + + return { + className: '', + }; + }} /> ); @@ -85,6 +146,8 @@ Clients.propTypes = { autoClients: PropTypes.array.isRequired, subtitle: PropTypes.string.isRequired, t: PropTypes.func.isRequired, + toggleClientStatus: PropTypes.func.isRequired, + processingAccessSet: PropTypes.bool.isRequired, }; export default withNamespaces()(Clients); diff --git a/client/src/components/Dashboard/QueriedDomains.js b/client/src/components/Dashboard/QueriedDomains.js index 0058ed64..85c39cfb 100644 --- a/client/src/components/Dashboard/QueriedDomains.js +++ b/client/src/components/Dashboard/QueriedDomains.js @@ -59,7 +59,7 @@ const QueriedDomains = ({ noDataText={t('no_domains_found')} minRows={6} defaultPageSize={100} - className="-striped -highlight card-table-overflow stats__table" + className="-highlight card-table-overflow stats__table" /> ); diff --git a/client/src/components/Dashboard/index.js b/client/src/components/Dashboard/index.js index 2f871720..01ce0448 100644 --- a/client/src/components/Dashboard/index.js +++ b/client/src/components/Dashboard/index.js @@ -10,6 +10,7 @@ import BlockedDomains from './BlockedDomains'; import PageTitle from '../ui/PageTitle'; import Loading from '../ui/Loading'; +import { ACTION } from '../../helpers/constants'; import './Dashboard.css'; class Dashboard extends Component { @@ -39,9 +40,20 @@ class Dashboard extends Component { ); }; + toggleClientStatus = (type, ip) => { + const confirmMessage = type === ACTION.block ? 'client_confirm_block' : 'client_confirm_unblock'; + + if (window.confirm(this.props.t(confirmMessage, { ip }))) { + this.props.toggleClientBlock(type, ip); + } + }; + render() { - const { dashboard, stats, t } = this.props; - const statsProcessing = stats.processingStats || stats.processingGetConfig; + const { + dashboard, stats, access, t, + } = this.props; + const statsProcessing = stats.processingStats + || stats.processingGetConfig; const subtitle = stats.interval === 1 @@ -116,6 +128,8 @@ class Dashboard extends Component { clients={dashboard.clients} autoClients={dashboard.autoClients} refreshButton={refreshButton} + toggleClientStatus={this.toggleClientStatus} + processingAccessSet={access.processingSet} />
@@ -146,11 +160,14 @@ class Dashboard extends Component { Dashboard.propTypes = { dashboard: PropTypes.object.isRequired, stats: PropTypes.object.isRequired, + access: PropTypes.object.isRequired, getStats: PropTypes.func.isRequired, getStatsConfig: PropTypes.func.isRequired, toggleProtection: PropTypes.func.isRequired, getClients: PropTypes.func.isRequired, t: PropTypes.func.isRequired, + toggleClientBlock: PropTypes.func.isRequired, + getAccessList: PropTypes.func.isRequired, }; export default withNamespaces()(Dashboard); diff --git a/client/src/components/Logs/Logs.css b/client/src/components/Logs/Logs.css index 9b2c8b21..4eb4a0fb 100644 --- a/client/src/components/Logs/Logs.css +++ b/client/src/components/Logs/Logs.css @@ -61,9 +61,10 @@ margin-right: 5px; } -.logs__action { +.logs__action, +.table__action { position: absolute; - top: 10px; + top: 11px; right: 15px; background-color: #fff; border-radius: 4px; @@ -72,11 +73,13 @@ opacity: 0; } -.logs__table .rt-td { +.logs__table .rt-td, +.clients__table .rt-td { position: relative; } -.logs__table .rt-tr:hover .logs__action { +.logs__table .rt-tr:hover .logs__action, +.clients__table .rt-tr:hover .table__action { visibility: visible; opacity: 1; } diff --git a/client/src/containers/Dashboard.js b/client/src/containers/Dashboard.js index 8d40df18..2b2dce78 100644 --- a/client/src/containers/Dashboard.js +++ b/client/src/containers/Dashboard.js @@ -1,11 +1,12 @@ import { connect } from 'react-redux'; import { toggleProtection, getClients } from '../actions'; import { getStats, getStatsConfig, setStatsConfig } from '../actions/stats'; +import { toggleClientBlock, getAccessList } from '../actions/access'; import Dashboard from '../components/Dashboard'; const mapStateToProps = (state) => { - const { dashboard, stats } = state; - const props = { dashboard, stats }; + const { dashboard, stats, access } = state; + const props = { dashboard, stats, access }; return props; }; @@ -15,6 +16,8 @@ const mapDispatchToProps = { getStats, getStatsConfig, setStatsConfig, + toggleClientBlock, + getAccessList, }; export default connect( diff --git a/client/src/helpers/constants.js b/client/src/helpers/constants.js index 1f1ce1a4..5920552c 100644 --- a/client/src/helpers/constants.js +++ b/client/src/helpers/constants.js @@ -456,3 +456,8 @@ export const DETAILED_DATE_FORMAT_OPTIONS = { }; export const CUSTOM_FILTERING_RULES_ID = 0; + +export const ACTION = { + block: 'block', + unblock: 'unblock', +}; diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index 795ec054..e748463e 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -122,6 +122,17 @@ export const addClientInfo = (data, clients, param) => ( }) ); +export const addClientStatus = (data, disallowedClients, param) => ( + data.map((row) => { + const clientIp = row[param]; + const blocked = !!(disallowedClients && disallowedClients.includes(clientIp)); + return { + ...row, + blocked, + }; + }) +); + export const normalizeFilteringStatus = (filteringStatus) => { const { enabled, filters, user_rules: userRules, interval, @@ -275,7 +286,13 @@ export const redirectToCurrentProtocol = (values, httpPort = 80) => { } }; -export const normalizeTextarea = text => text && text.replace(/[;, ]/g, '\n').split('\n').filter(n => n); +export const normalizeTextarea = (text) => { + if (!text) { + return []; + } + + return text.replace(/[;, ]/g, '\n').split('\n').filter(n => n); +}; /** * Normalizes the topClients array diff --git a/client/src/reducers/access.js b/client/src/reducers/access.js index b8fc14ed..3ff517cf 100644 --- a/client/src/reducers/access.js +++ b/client/src/reducers/access.js @@ -31,6 +31,10 @@ const access = handleActions( }; return newState; }, + + [actions.toggleClientBlockRequest]: state => ({ ...state, processingSet: true }), + [actions.toggleClientBlockFailure]: state => ({ ...state, processingSet: false }), + [actions.toggleClientBlockSuccess]: state => ({ ...state, processingSet: false }), }, { processing: true, From 1a88a63415d83262c268beff74991dd926fa5bc5 Mon Sep 17 00:00:00 2001 From: Simon Zolin Date: Thu, 30 Jan 2020 19:06:09 +0300 Subject: [PATCH 3/3] Merge: - filtering: fix host rules matching Close #1365 Squashed commit of the following: commit 9cbca2d330ae12b222633201f4864abb7f7cd7a3 Merge: 8ce6b015 be93dc34 Author: Simon Zolin Date: Thu Jan 30 19:03:21 2020 +0300 Merge remote-tracking branch 'origin/master' into 1365-hostrules commit 8ce6b0151a2b552c4ccb3ee1f7e36ce260ba96ea Merge: c752ab33 5c814b29 Author: Simon Zolin Date: Thu Jan 30 18:57:20 2020 +0300 Merge remote-tracking branch 'origin/master' into 1365-hostrules commit c752ab33b074312f10772467436a27a90339a919 Author: Simon Zolin Date: Thu Jan 30 14:18:58 2020 +0300 use new Match() commit ce2f628aca9f934c776c8c690813efeed5d5427b Author: Simon Zolin Date: Thu Jan 30 12:03:21 2020 +0300 minor commit ebebe02a63821fedd3904db384406c30de52d515 Author: Simon Zolin Date: Thu Jan 30 11:21:47 2020 +0300 * dnsfilter: use new version of urlfilter's Match() commit 84edc44f2ee5a67316114f048740825259cc87ff Author: Simon Zolin Date: Fri Jan 24 14:10:41 2020 +0300 - filtering: fix host rules matching Match by both IPv4 and IPv6 rules, not just the first one in list. --- dnsfilter/dnsfilter.go | 82 +++++++++++++++++++++++-------------- dnsfilter/dnsfilter_test.go | 19 ++++++++- go.mod | 2 +- go.sum | 4 +- 4 files changed, 73 insertions(+), 34 deletions(-) diff --git a/dnsfilter/dnsfilter.go b/dnsfilter/dnsfilter.go index 4c84c1e8..5ae9d523 100644 --- a/dnsfilter/dnsfilter.go +++ b/dnsfilter/dnsfilter.go @@ -478,53 +478,72 @@ func (d *Dnsfilter) initFiltering(filters map[int]string) error { // matchHost is a low-level way to check only if hostname is filtered by rules, skipping expensive safebrowsing and parental lookups func (d *Dnsfilter) matchHost(host string, qtype uint16, ctags []string) (Result, error) { d.engineLock.RLock() + // Keep in mind that this lock must be held no just when calling Match() + // but also while using the rules returned by it. defer d.engineLock.RUnlock() if d.filteringEngine == nil { return Result{}, nil } - frules, ok := d.filteringEngine.Match(host, ctags) + rr, ok := d.filteringEngine.Match(host, ctags) if !ok { return Result{}, nil } - log.Tracef("%d rules matched for host '%s'", len(frules), host) + if rr.NetworkRule != nil { + log.Debug("Filtering: found rule for host '%s': '%s' list_id: %d", + host, rr.NetworkRule.Text(), rr.NetworkRule.GetFilterListID()) + res := Result{} + res.FilterID = int64(rr.NetworkRule.GetFilterListID()) + res.Rule = rr.NetworkRule.Text() - for _, rule := range frules { + res.Reason = FilteredBlackList + res.IsFiltered = true + if rr.NetworkRule.Whitelist { + res.Reason = NotFilteredWhiteList + res.IsFiltered = false + } + return res, nil + } - log.Tracef("Found rule for host '%s': '%s' list_id: %d", - host, rule.Text(), rule.GetFilterListID()) + if qtype == dns.TypeA && rr.HostRulesV4 != nil { + rule := rr.HostRulesV4[0] // note that we process only 1 matched rule + res := Result{} + res.FilterID = int64(rule.GetFilterListID()) + res.Rule = rule.Text() + res.Reason = FilteredBlackList + res.IsFiltered = true + res.IP = rule.IP.To4() + return res, nil + } + if qtype == dns.TypeAAAA && rr.HostRulesV6 != nil { + rule := rr.HostRulesV6[0] // note that we process only 1 matched rule + res := Result{} + res.FilterID = int64(rule.GetFilterListID()) + res.Rule = rule.Text() + res.Reason = FilteredBlackList + res.IsFiltered = true + res.IP = rule.IP + return res, nil + } + + if rr.HostRulesV4 != nil || rr.HostRulesV6 != nil { + // Question Type doesn't match the host rules + // Return the first matched host rule, but without an IP address res := Result{} res.Reason = FilteredBlackList res.IsFiltered = true + var rule rules.Rule + if rr.HostRulesV4 != nil { + rule = rr.HostRulesV4[0] + } else if rr.HostRulesV6 != nil { + rule = rr.HostRulesV6[0] + } res.FilterID = int64(rule.GetFilterListID()) res.Rule = rule.Text() - - if netRule, ok := rule.(*rules.NetworkRule); ok { - - if netRule.Whitelist { - res.Reason = NotFilteredWhiteList - res.IsFiltered = false - } - return res, nil - - } else if hostRule, ok := rule.(*rules.HostRule); ok { - - res.IP = net.IP{} - if qtype == dns.TypeA && hostRule.IP.To4() != nil { - // either IPv4 or IPv4-mapped IPv6 address - res.IP = hostRule.IP.To4() - - } else if qtype == dns.TypeAAAA && hostRule.IP.To4() == nil { - res.IP = hostRule.IP - } - return res, nil - - } else { - log.Tracef("Rule type is unsupported: '%s' list_id: %d", - rule.Text(), rule.GetFilterListID()) - } + res.IP = net.IP{} + return res, nil } return Result{}, nil @@ -581,6 +600,9 @@ func New(c *Config, filters map[int]string) *Dnsfilter { return d } +// Start - start the module: +// . start async filtering initializer goroutine +// . register web handlers func (d *Dnsfilter) Start() { d.filtersInitializerChan = make(chan filtersInitializerParams, 1) go d.filtersInitializer() diff --git a/dnsfilter/dnsfilter_test.go b/dnsfilter/dnsfilter_test.go index 3d1f07dd..ae431655 100644 --- a/dnsfilter/dnsfilter_test.go +++ b/dnsfilter/dnsfilter_test.go @@ -98,7 +98,13 @@ func (d *Dnsfilter) checkMatchEmpty(t *testing.T, hostname string) { func TestEtcHostsMatching(t *testing.T) { addr := "216.239.38.120" addr6 := "::1" - text := fmt.Sprintf(" %s google.com www.google.com # enforce google's safesearch \n%s ipv6.com\n0.0.0.0 block.com\n", + text := fmt.Sprintf(` %s google.com www.google.com # enforce google's safesearch +%s ipv6.com +0.0.0.0 block.com +0.0.0.1 host2 +0.0.0.2 host2 +::1 host2 +`, addr, addr6) filters := make(map[int]string) filters[0] = text @@ -116,6 +122,7 @@ func TestEtcHostsMatching(t *testing.T) { // ...but empty IPv6 ret, err := d.CheckHost("block.com", dns.TypeAAAA, &setts) assert.True(t, err == nil && ret.IsFiltered && ret.IP != nil && len(ret.IP) == 0) + assert.True(t, ret.Rule == "0.0.0.0 block.com") // IPv6 d.checkMatchIP(t, "ipv6.com", addr6, dns.TypeAAAA) @@ -123,6 +130,16 @@ func TestEtcHostsMatching(t *testing.T) { // ...but empty IPv4 ret, err = d.CheckHost("ipv6.com", dns.TypeA, &setts) assert.True(t, err == nil && ret.IsFiltered && ret.IP != nil && len(ret.IP) == 0) + + // 2 IPv4 (return only the first one) + ret, err = d.CheckHost("host2", dns.TypeA, &setts) + assert.True(t, err == nil && ret.IsFiltered) + assert.True(t, ret.IP != nil && ret.IP.Equal(net.ParseIP("0.0.0.1"))) + + // ...and 1 IPv6 address + ret, err = d.CheckHost("host2", dns.TypeAAAA, &setts) + assert.True(t, err == nil && ret.IsFiltered) + assert.True(t, ret.IP != nil && ret.IP.Equal(net.ParseIP("::1"))) } // SAFE BROWSING diff --git a/go.mod b/go.mod index 902c44c4..28b2d1a4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.13 require ( github.com/AdguardTeam/dnsproxy v0.23.7 github.com/AdguardTeam/golibs v0.3.0 - github.com/AdguardTeam/urlfilter v0.8.1 + github.com/AdguardTeam/urlfilter v0.9.1 github.com/NYTimes/gziphandler v1.1.1 github.com/etcd-io/bbolt v1.3.3 github.com/go-test/deep v1.0.4 // indirect diff --git a/go.sum b/go.sum index c1fc4421..837f305e 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/AdguardTeam/golibs v0.3.0/go.mod h1:R3M+mAg3nWG4X4Hsag5eef/TckHFH12ZY github.com/AdguardTeam/gomitmproxy v0.1.2/go.mod h1:Mrt/3EfiXIYY2aZ7KsLuCUJzUARD/fWJ119IfzOB13M= github.com/AdguardTeam/urlfilter v0.7.0 h1:ffFLt4rA3GX8PJYGL3bGcT5bSxZlML5k6cKpSeN2UI8= github.com/AdguardTeam/urlfilter v0.7.0/go.mod h1:GHXPzEG59ezyff22lXSQ7dicj1kFZBrH5kmZ6EvQzfk= -github.com/AdguardTeam/urlfilter v0.8.1 h1:9YRQOR15DU7+k01PWAgc/Ay12jjxVqSi6P0+whFm0f4= -github.com/AdguardTeam/urlfilter v0.8.1/go.mod h1:GHXPzEG59ezyff22lXSQ7dicj1kFZBrH5kmZ6EvQzfk= +github.com/AdguardTeam/urlfilter v0.9.1 h1:H0q1xig3mZjIEDH0/o2U/ezydwKGwxtQ56hz6LKPN2M= +github.com/AdguardTeam/urlfilter v0.9.1/go.mod h1:GHXPzEG59ezyff22lXSQ7dicj1kFZBrH5kmZ6EvQzfk= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/StackExchange/wmi v0.0.0-20181212234831-e0a55b97c705 h1:UUppSQnhf4Yc6xGxSkoQpPhb7RVzuv5Nb1mwJ5VId9s=