unbound: overrides: migrate to mvc model (#5488)

* unbound: overrides: migrate to mvc model
* unbound: overrides: generate host_entries via model, revert template generation
* unbound: overrides migration: fix missing include
* unbound: overrides: clean up

Co-authored-by: Stephan de Wit <stephan.de.wit@deciso.com>
This commit is contained in:
Stephan de Wit 2022-01-24 20:19:06 +01:00 committed by GitHub
parent 4ca55d9dc6
commit 161d24650b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 927 additions and 925 deletions

12
plist
View File

@ -377,9 +377,13 @@
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/Api/SettingsController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/DnsblController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/DotController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/OverridesController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/StatsController.php
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/dialogDot.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/dnsbl.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/dialogDomainOverride.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/dialogHostAlias.xml
/usr/local/opnsense/mvc/app/controllers/OPNsense/Unbound/forms/dialogHostOverride.xml
/usr/local/opnsense/mvc/app/library/Google/API/Drive.php
/usr/local/opnsense/mvc/app/library/OPNsense/Auth/API.php
/usr/local/opnsense/mvc/app/library/OPNsense/Auth/AuthenticationFactory.php
@ -564,6 +568,7 @@
/usr/local/opnsense/mvc/app/models/OPNsense/Unbound/ACL/ACL.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Unbound/Menu/Menu.xml
/usr/local/opnsense/mvc/app/models/OPNsense/Unbound/Migrations/M1_0_0.php
/usr/local/opnsense/mvc/app/models/OPNsense/Unbound/Migrations/M1_0_1.php
/usr/local/opnsense/mvc/app/models/OPNsense/Unbound/Unbound.php
/usr/local/opnsense/mvc/app/models/OPNsense/Unbound/Unbound.xml
/usr/local/opnsense/mvc/app/views/OPNsense/CaptivePortal/clients.volt
@ -608,6 +613,7 @@
/usr/local/opnsense/mvc/app/views/OPNsense/TrafficShaper/statistics.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Unbound/dnsbl.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Unbound/dot.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Unbound/overrides.volt
/usr/local/opnsense/mvc/app/views/OPNsense/Unbound/stats.volt
/usr/local/opnsense/mvc/app/views/layout_partials/base_dialog.volt
/usr/local/opnsense/mvc/app/views/layout_partials/base_dialog_processing.volt
@ -989,6 +995,9 @@
/usr/local/opnsense/service/templates/OPNsense/Syslog/syslog-ng.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/+TARGETS
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/blocklists.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/domainoverrides.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/host_entries.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/private_domains.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/dot.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/miscellaneous.conf
/usr/local/opnsense/service/templates/OPNsense/Unbound/core/root.min.hints
@ -1804,9 +1813,6 @@
/usr/local/www/services_unbound.php
/usr/local/www/services_unbound_acls.php
/usr/local/www/services_unbound_advanced.php
/usr/local/www/services_unbound_domainoverride_edit.php
/usr/local/www/services_unbound_host_edit.php
/usr/local/www/services_unbound_overrides.php
/usr/local/www/status_dhcp_leases.php
/usr/local/www/status_dhcpv6_leases.php
/usr/local/www/status_habackup.php

View File

@ -219,14 +219,7 @@ EOF;
}
}
/* allow DNS Rebind for forwarded domains */
if (!empty($config['unbound']['domainoverrides'])) {
$private_domains = "# Set private domains in case authoritative name server returns a Private IP address\n";
$private_domains .= unbound_add_domain_overrides(true);
}
unbound_add_host_entries($ifconfig_details);
unbound_add_domain_overrides();
unbound_acls_config();
$port = is_port($config['unbound']['port']) ? $config['unbound']['port'] : "53";
@ -359,7 +352,9 @@ prefetch-key: {$prefetch_key}
# DNS Rebinding
{$private_addr}
{$private_domains}
# Private domains (DNS Rebinding)
include: /var/unbound/private_domains.conf
# Access lists
include: /var/unbound/access_lists.conf
@ -370,9 +365,6 @@ include: /var/unbound/host_entries.conf
# DHCP leases (if configured)
{$include_dhcpleases}
# Domain overrides
include: /var/unbound/domainoverrides.conf
# Custom includes
include: /var/unbound/etc/*.conf
@ -439,56 +431,6 @@ function unbound_configure_do($verbose = false, $interface = '')
}
}
function unbound_add_domain_overrides($pvt = false)
{
global $config;
$domains = config_read_array('unbound', 'domainoverrides');
usort($domains, function ($a, $b) {
return strcasecmp($a['domain'], $b['domain']);
});
$result = array();
$forward_local = '';
foreach ($domains as $domain) {
$domain_key = current($domain);
if (!isset($result[$domain_key])) {
$result[$domain_key] = array();
}
$result[$domain_key][] = $domain['ip'];
}
$domain_entries = '';
foreach ($result as $domain => $ips) {
if ($pvt == true) {
$domain_entries .= "domain-insecure: \"$domain\"\n";
if (preg_match('/.+\.(in-addr|ip6)\.arpa\.?$/', $domain)) {
$domain_entries .= "local-zone: \"$domain\" typetransparent\n";
} elseif (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
$domain_entries .= "private-domain: \"$domain\"\n";
}
} else {
$domain_entries .= "forward-zone:\n";
$domain_entries .= "\tname: \"$domain\"\n";
foreach ($ips as $ip) {
if (strpos($ip, '127.') === 0 || $ip == '::1') {
$forward_local = "server:\n\tdo-not-query-localhost: no\n";
}
$domain_entries .= "\tforward-addr: $ip\n";
}
}
}
if ($pvt == true) {
return $domain_entries;
} else {
$domain_entries .= $forward_local;
file_put_contents('/var/unbound/domainoverrides.conf', $domain_entries);
}
}
function unbound_add_host_entries($ifconfig_details = null)
{
global $config;
@ -577,55 +519,59 @@ function unbound_add_host_entries($ifconfig_details = null)
unset($tmp_known_domains); // remove temporary variable
}
if (isset($config['unbound']['hosts'])) {
foreach ($config['unbound']['hosts'] as $host) {
$aliases = array(array(
'domain' => $host['domain'],
'descr' => $host['descr'],
'host' => $host['host'],
));
$unbound_mdl = new \OPNsense\Unbound\Unbound();
$hosts = iterator_to_array($unbound_mdl->hosts->host->iterateItems());
$aliases = iterator_to_array($unbound_mdl->aliases->alias->iterateItems());
if (!empty($host['aliases']['item'])) {
foreach ($host['aliases']['item'] as $alias) {
$aliases[] = array(
'domain' => !empty($alias['domain']) ? $alias['domain'] : $aliases[0]['domain'],
'descr' => !empty($alias['descr']) ? $alias['descr'] : $aliases[0]['descr'],
'host' => !empty($alias['host']) ? $alias['host'] : $aliases[0]['host'],
);
}
}
if (!empty($hosts)) {
foreach ($hosts as $host) {
if ($host->enabled == '1') {
$tmp_aliases = array(array(
'domain' => (string)$host->domain,
'description' => (string)$host->description,
'hostname' => (string)$host->hostname
));
/* Backwards compatibility for records created before introducing RR types. */
if (!isset($host['rr'])) {
$host['rr'] = (is_ipaddrv6($host['ip'])) ? 'AAAA' : 'A';
}
foreach ($aliases as $alias) {
if ($alias['host'] != '') {
$alias['host'] .= '.';
}
switch ($host['rr']) {
case 'A':
case 'AAAA':
/* Handle wildcard entries which have "*" as a hostname. Since we added a . above, we match on "*.". */
if ($alias['host'] == '*.') {
$unbound_entries .= "local-zone: \"{$alias['domain']}\" redirect\n";
$unbound_entries .= "local-data: \"{$alias['domain']} IN {$host['rr']} {$host['ip']}\"\n";
} else {
$unbound_entries .= "local-data-ptr: \"{$host['ip']} {$alias['host']}{$alias['domain']}\"\n";
$unbound_entries .= "local-data: \"{$alias['host']}{$alias['domain']} IN {$host['rr']} {$host['ip']}\"\n";
if (!empty($aliases)) {
foreach ($aliases as $alias) {
if ($alias->enabled == '1' && $alias->host == $host->getAttribute('uuid')) {
$tmp_aliases[] = array(
'domain' => !empty((string)$alias->domain) ? $alias->domain : $tmp_aliases[0]['domain'],
'description' => !empty((string)$alias->description) ? $alias->description : $tmp_aliases[0]['description'],
'hostname' => !empty((string)$alias->hostname) ? $alias->hostname : $tmp_aliases[0]['hostname']
);
}
break;
case 'MX':
$unbound_entries .= "local-data: \"{$alias['host']}{$alias['domain']} IN MX {$host['mxprio']} {$host['mx']}\"\n";
break;
}
}
if (!empty($alias['descr']) && isset($config['unbound']['txtsupport'])) {
$unbound_entries .= "local-data: '{$alias['host']}{$alias['domain']} TXT \"" . addslashes($alias['descr']) . "\"'\n";
foreach ($tmp_aliases as $alias) {
if ($alias['hostname'] != '') {
$alias['hostname'] .= '.';
}
switch ($host->rr) {
case 'A':
case 'AAAA':
/* Handle wildcard entries which have "*" as a hostname. Since we added a . above, we match on "*.". */
if ($alias['hostname'] == '*.') {
$unbound_entries .= "local-zone: \"{$alias['domain']}\" redirect\n";
$unbound_entries .= "local-data: \"{$alias['domain']} IN {$host->rr} {$host->server}\"\n";
} else {
$unbound_entries .= "local-data-ptr: \"{$host->server} {$alias['hostname']}{$alias['domain']}\"\n";
$unbound_entries .= "local-data: \"{$alias['hostname']}{$alias['domain']} IN {$host->rr} {$host->server}\"\n";
}
break;
case 'MX':
$unbound_entries .= "local-data: \"{$alias['hostname']}{$alias['domain']} IN MX {$host->mxprio} {$host->mx}\"\n";
break;
}
if (!empty($alias['description']) && isset($config['unbound']['txtsupport'])) {
$unbound_entries .= "local-data: '{$alias['hostname']}{$alias['domain']} TXT \"" . addslashes($alias['description']) . "\"'\n";
}
}
}
}
}

View File

@ -65,4 +65,139 @@ class SettingsController extends ApiMutableModelControllerBase
{
return $this->toggleBase('dots.dot', $uuid, $enabled);
}
/* Host overrides */
public function searchHostOverrideAction()
{
return $this->searchBase(
'hosts.host',
['enabled', 'hostname', 'domain', 'rr', 'mxprio', 'mx', 'server', 'description'],
"sequence"
);
}
public function getHostOverrideAction($uuid = null)
{
return $this->getBase('host', 'hosts.host', $uuid);
}
public function addHostOverrideAction()
{
return $this->addBase('host', 'hosts.host');
}
public function delHostOverrideAction($uuid)
{
/* Make sure the linked aliases are deleted as well. */
$node = $this->getModel();
foreach ($node->aliases->alias->iterateItems() as $alias_uuid => $alias) {
if ($alias->host == $uuid) {
$this->delBase('aliases.alias', $alias_uuid);
}
}
return $this->delBase('hosts.host', $uuid);
}
public function setHostOverrideAction($uuid)
{
return $this->setBase('host', 'hosts.host', $uuid);
}
public function toggleHostOverrideAction($uuid, $enabled = null)
{
return $this->toggleBase('hosts.host', $uuid, $enabled);
}
/* Aliases for hosts */
public function searchHostAliasAction()
{
$host = $this->request->get('host');
$filter_func = null;
if (!empty($host)) {
$filter_func = function ($record) use ($host) {
return $record->host == $host;
};
}
return $this->searchBase(
'aliases.alias',
['enabled', 'host', 'hostname', 'domain'],
"sequence",
$filter_func
);
}
public function getHostAliasAction($uuid = null)
{
$host_uuid = $this->request->get('host');
$result = $this->getBase('alias', 'aliases.alias', $uuid);
if (empty($uuid) && !empty($host_uuid)) {
foreach($result['alias']['host'] as $key => &$value) {
if ($key == $host_uuid) {
$value['selected'] = 1;
} else {
$value['selected'] = 0;
}
}
}
return $result;
}
public function addHostAliasAction()
{
return $this->addBase('alias', 'aliases.alias');
}
public function delHostAliasAction($uuid)
{
return $this->delBase('aliases.alias', $uuid);
}
public function setHostAliasAction($uuid)
{
return $this->setBase('alias', 'aliases.alias');
}
public function toggleHostAliasAction($uuid, $enabled = null)
{
return $this->toggleBase('aliases.alias', $uuid, $enabled);
}
/* Domain overrides */
public function searchDomainOverrideAction()
{
return $this->searchBase(
'domains.domain',
['enabled', 'domain', 'server', 'description'],
"sequence"
);
}
public function getDomainOverrideAction($uuid = null)
{
return $this->getBase('domain', 'domains.domain', $uuid);
}
public function addDomainOverrideAction()
{
return $this->addBase('domain', 'domains.domain');
}
public function delDomainOverrideAction($uuid)
{
return $this->delBase('domains.domain', $uuid);
}
public function setDomainOverrideAction($uuid)
{
return $this->setBase('domain', 'domains.domain', $uuid);
}
public function toggleDomainOverrideAction($uuid, $enabled = null)
{
return $this->toggleBase('domains.domain', $uuid, $enabled);
}
}

View File

@ -0,0 +1,42 @@
<?php
/*
* Copyright (C) 2022 Stephan de Wit <stephan.de.wit@deciso.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\Unbound;
use OPNsense\Base\IndexController;
class OverridesController extends IndexController
{
public function indexAction()
{
$this->view->pick('OPNsense/Unbound/overrides');
$this->view->formDialogHostOverride = $this->getForm("dialogHostOverride");
$this->view->formDialogHostAlias = $this->getForm("dialogHostAlias");
$this->view->formDialogDomainOverride = $this->getForm("dialogDomainOverride");
}
}

View File

@ -0,0 +1,33 @@
<form>
<field>
<id>domain.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Enable this domain override</help>
</field>
<field>
<id>domain.domain</id>
<label>Domain</label>
<type>text</type>
<help>
Domain to override (NOTE: this does not have to be a valid TLD!),
e.g. 'test' or 'mycompany.localdomain' or '1.168.192.in-addr.arpa'
</help>
</field>
<field>
<id>domain.server</id>
<label>IP address</label>
<type>text</type>
<help>
IP address of the authoritative DNS server for this domain,
e.g. '192.168.100.100'. To use a nondefault port for communication,
append an '@' with the port number.
</help>
</field>
<field>
<id>domain.description</id>
<label>Description</label>
<type>text</type>
<help>You may enter a description here for your reference (not parsed).</help>
</field>
</form>

View File

@ -0,0 +1,26 @@
<form>
<field>
<id>alias.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Enable this alias for the selected host</help>
</field>
<field>
<id>alias.host</id>
<label>Host override</label>
<type>dropdown</type>
<help>Select the associated host override to apply this alias on</help>
</field>
<field>
<id>alias.hostname</id>
<label>Host</label>
<type>text</type>
<help>Name of the host, without the domain part. Use "*" to create a wildcard entry.</help>
</field>
<field>
<id>alias.domain</id>
<label>Domain</label>
<type>text</type>
<help>Domain of the host, e.g. example.com</help>
</field>
</form>

View File

@ -0,0 +1,50 @@
<form>
<field>
<id>host.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Enable the override for this host</help>
</field>
<field>
<id>host.hostname</id>
<label>Host</label>
<type>text</type>
<help>Name of the host, without the domain part. Use "*" to create a wildcard entry.</help>
</field>
<field>
<id>host.domain</id>
<label>Domain</label>
<type>text</type>
<help>Domain of the host, e.g. example.com</help>
</field>
<field>
<id>host.rr</id>
<label>Type</label>
<type>dropdown</type>
<help>Type of resource record, e.g. A or AAAA for IPv4 or IPv6 addresses</help>
</field>
<field>
<id>host.mxprio</id>
<label>MX Priority</label>
<type>text</type>
<help>Priority of MX record, e.g. 10</help>
</field>
<field>
<id>host.mx</id>
<label>MX Host</label>
<type>text</type>
<help>Host name of MX host, e.g. mail.example.com</help>
</field>
<field>
<id>host.server</id>
<label>IP address</label>
<type>text</type>
<help>IP address of the host, e.g. 192.168.100.100 or fd00:abcd::1</help>
</field>
<field>
<id>host.description</id>
<label>Description</label>
<type>text</type>
<help>You may enter a description here for your reference (not parsed)</help>
</field>
</form>

View File

@ -57,10 +57,15 @@ class HostnameField extends BaseField
private $internalAsList = false;
/**
* @var bool wildcard (any) enabled
* @var bool IP address allowed
*/
protected $internalIpAllowed = true;
/**
* @var bool wildcard (*) enabled
*/
protected $internalWildcardAllowed = false;
/**
* ip addresses allowed
* @param string $value Y/N
@ -74,6 +79,15 @@ class HostnameField extends BaseField
}
}
public function setWildcardAllowed($value)
{
if (trim($value) == "Y") {
$this->internalWildcardAllowed = true;
} else {
$this->internalWildcardAllowed = false;
}
}
/**
* always trim hostnames
* @param string $value
@ -132,11 +146,13 @@ class HostnameField extends BaseField
{
$validators = parent::getValidators();
if ($this->internalValue != null) {
$validators[] = new HostValidator(array(
'message' => $this->internalValidationMessage,
'split' => $this->internalFieldSeparator,
'allowip' => $this->internalIpAllowed
));
if ($this->internalValue != "*" || $this->internalWildcardAllowed == false) {
$validators[] = new HostValidator(array(
'message' => $this->internalValidationMessage,
'split' => $this->internalFieldSeparator,
'allowip' => $this->internalIpAllowed
));
}
}
return $validators;
}

View File

@ -17,20 +17,13 @@
<pattern>services_unbound_advanced.php*</pattern>
</patterns>
</page-services-dnsresolver-advanced>
<page-services-dnsresolver-editdomainoverride>
<name>Services: Unbound DNS: Edit Domain Override</name>
<page-services-dnsresolver-overrides>
<name>Services: Unbound DNS: Edit Host and Domain Override</name>
<patterns>
<pattern>services_unbound_overrides.php</pattern>
<pattern>services_unbound_domainoverride_edit.php*</pattern>
<pattern>ui/unbound/overrides/*</pattern>
<pattern>api/unbound/overrides/*</pattern>
</patterns>
</page-services-dnsresolver-editdomainoverride>
<page-services-dnsresolver-edithost>
<name>Services: Unbound DNS: Edit Host Override</name>
<patterns>
<pattern>services_unbound_overrides.php</pattern>
<pattern>services_unbound_host_edit.php*</pattern>
</patterns>
</page-services-dnsresolver-edithost>
</page-services-dnsresolver-overrides>
<page-diagnostics-logs-resolver>
<name>Services: Unbound DNS: Log File</name>
<patterns>

View File

@ -2,10 +2,7 @@
<Services>
<Unbound VisibleName="Unbound DNS" cssClass="fa fa-tags fa-fw">
<General order="10" url="/services_unbound.php"/>
<Overrides order="20" url="/services_unbound_overrides.php">
<Hosts url="/services_unbound_host_edit.php*" visibility="hidden"/>
<Domains url="/services_unbound_domainoverride_edit.php*" visibility="hidden"/>
</Overrides>
<Overrides order="20" url="/ui/unbound/overrides/"/>
<Advanced order="30" url="/services_unbound_advanced.php"/>
<ACL VisibleName="Access Lists" order="40" url="/services_unbound_acls.php">
<All url="/services_unbound_acls.php*" visibility="hidden"/>

View File

@ -0,0 +1,108 @@
<?php
/*
* Copyright (C) 2022 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\Unbound\Migrations;
use OPNsense\Base\BaseModelMigration;
use OPNsense\Base\FieldTypes\BooleanField;
use OPNsense\Base\FieldTypes\NetworkField;
use OPNsense\Base\FieldTypes\PortField;
use OPNsense\Core\Config;
class M1_0_1 extends BaseModelMigration
{
/**
* Migrate older models into shared model
* @param $model
*/
public function run($model)
{
$config = Config::getInstance()->object();
if (!empty($config->unbound->hosts)) {
foreach($config->unbound->hosts as $old_host) {
$new_host = $model->hosts->host->add();
/* Backwards compatibility for records created before introducing RR types. */
if (!isset($old_host->rr)) {
$old_host->rr = (strpos($old_host->ip, ':') !== false) ? 'AAAA' : 'A';
}
$host_data = [
'enabled' => 1,
'hostname' => !empty($old_host->host) ? $old_host->host : null,
'domain' => $old_host->domain,
'rr' => $old_host->rr,
'server' => $old_host->rr == 'A' ? $old_host->ip : null,
'mxprio' => $old_host->rr == 'MX' ? $old_host->mxprio : null,
'mx' => $old_host->rr == 'MX' ? $old_host->mx : null,
'description' => !empty($old_host->descr) ? $old_host->descr : null
];
$new_host->setNodes($host_data);
$uuid = $new_host->getAttribute('uuid');
foreach ($old_host->aliases->item as $old_alias) {
if (!empty($old_alias)) {
$new_alias = $model->aliases->alias->add();
$alias_data = [
'enabled' => 1,
'host' => $uuid,
'domain' => $old_alias->domain,
'hostname' => !empty($old_alias->host) ? $old_alias->host : null
];
$new_alias->setNodes($alias_data);
}
}
}
}
if (!empty($config->unbound->domainoverrides)) {
foreach($config->unbound->domainoverrides as $old_domain) {
$new_domain = $model->domains->domain->add();
$domain_data = [
'enabled' => 1,
'domain' => $old_domain->domain,
'server' => $old_domain->ip,
'description' => !empty($old_domain->descr) ? $old_domain->descr : null
];
$new_domain->setNodes($domain_data);
}
}
}
/**
* cleanup old config after config save
* @param $model
*/
public function post($model)
{
$cfgObj = Config::getInstance()->object();
unset($cfgObj->unbound->hosts, $cfgObj->unbound->domainoverrides);
}
}

View File

@ -1,7 +1,7 @@
<model>
<mount>//OPNsense/unboundplus</mount>
<description>Unbound configuration</description>
<version>1.0.0</version>
<version>1.0.1</version>
<items>
<service_enabled type="LegacyLinkField">
<Source>unbound.enable</Source>
@ -73,6 +73,128 @@
</verify>
</dot>
</dots>
<hosts>
<host type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<hostname type="HostnameField">
<Required>N</Required>
<WildcardAllowed>Y</WildcardAllowed>
</hostname>
<domain type="TextField">
<Required>Y</Required>
<mask>/^(?:(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])$/i</mask>
<ValidationMessage>A valid domain must be specified.</ValidationMessage>
</domain>
<rr type="OptionField">
<Required>Y</Required>
<default>A</default>
<OptionValues>
<A>A or AAAA (IPv4 or IPv6 address)</A>
<MX>MX (Mail server)</MX>
</OptionValues>
</rr>
<mxprio type="IntegerField">
<Constraints>
<check001>
<ValidationMessage>The field MX Priority is required.</ValidationMessage>
<type>SetIfConstraint</type>
<field>rr</field>
<check>MX</check>
</check001>
</Constraints>
</mxprio>
<mx type="HostnameField">
<Constraints>
<check001>
<ValidationMessage>The field MX Host is required.</ValidationMessage>
<type>SetIfConstraint</type>
<field>rr</field>
<check>MX</check>
</check001>
</Constraints>
</mx>
<server type="NetworkField">
<Constraints>
<check001>
<ValidationMessage>The field IP address is required.</ValidationMessage>
<type>SetIfConstraint</type>
<field>rr</field>
<check>A</check>
</check001>
</Constraints>
</server>
<description type="TextField">
<Required>N</Required>
<mask>/^([\t\n\v\f\r 0-9a-zA-Z.,_\x{00A0}-\x{FFFF}]){1,255}$/u</mask>
<ValidationMessage>Description should be a string between 1 and 255 characters</ValidationMessage>
</description>
</host>
</hosts>
<aliases>
<alias type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<host type="ModelRelationField">
<Model>
<host>
<source>OPNsense.Unbound.Unbound</source>
<items>hosts.host</items>
<display>domain</display>
</host>
</Model>
<Required>Y</Required>
</host>
<hostname type="HostnameField">
<Constraints>
<check001>
<ValidationMessage>The host field is required if a domain has not been specified.</ValidationMessage>
<type>SetIfConstraint</type>
<field>domain</field>
<check></check>
</check001>
</Constraints>
<WildcardAllowed>Y</WildcardAllowed>
</hostname>
<domain type="TextField">
<Constraints>
<check001>
<ValidationMessage>The domain field is required if a host has not been specified.</ValidationMessage>
<type>SetIfConstraint</type>
<field>hostname</field>
<check></check>
</check001>
</Constraints>
<mask>/^(?:(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])$/i</mask>
<ValidationMessage>A valid domain must be specified.</ValidationMessage>
</domain>
</alias>
</aliases>
<domains>
<domain type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<domain type="TextField">
<Required>Y</Required>
<mask>/^(?:(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*(?:[a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])$/i</mask>
<ValidationMessage>A valid domain must be specified.</ValidationMessage>
</domain>
<server type="NetworkField">
<Required>Y</Required>
</server>
<description type="TextField">
<Required>N</Required>
<mask>/^([\t\n\v\f\r 0-9a-zA-Z.,_\x{00A0}-\x{FFFF}]){1,255}$/u</mask>
<ValidationMessage>Description should be a string between 1 and 255 characters</ValidationMessage>
</description>
</domain>
</domains>
<miscellaneous>
<privatedomain type="CSVListField">
<Required>N</Required>

View File

@ -0,0 +1,289 @@
{#
# Copyright (c) 2014-2022 Deciso B.V.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#}
<script>
$( document ).ready(function() {
/**
* load content on tab changes
*/
let heading_appended = false;
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
if (e.target.id == 'host_overrides_tab') {
$("#grid-aliases-wrapper").show();
$("#grid-hosts").bootgrid('destroy');
let grid_hosts = $("#grid-hosts").UIBootgrid({
search:'/api/unbound/settings/searchHostOverride/',
get:'/api/unbound/settings/getHostOverride/',
set:'/api/unbound/settings/setHostOverride/',
add:'/api/unbound/settings/addHostOverride/',
del:'/api/unbound/settings/delHostOverride/',
toggle:'/api/unbound/settings/toggleHostOverride/',
options: {
selection: true,
multiSelect: false,
rowSelect: true,
formatters: {
"mxformatter": function (column, row) {
/* Format the "Value" column so it shows either an MX host ("MX" type) or a raw IP address ("A" type) */
if (row.mx.length > 0) {
row.server = row.mx + ' (prio ' + row.mxprio + ')';
}
return row.server;
},
/* commands and rowtoggles added here since adding a custom formatter removes these by default for some reason */
"commands": function (column, row) {
return '<button type="button" class="btn btn-xs btn-default command-edit bootgrid-tooltip" data-row-id="' + row.uuid + '"><span class="fa fa-fw fa-pencil"></span></button> ' +
'<button type="button" class="btn btn-xs btn-default command-copy bootgrid-tooltip" data-row-id="' + row.uuid + '"><span class="fa fa-fw fa-clone"></span></button>' +
'<button type="button" class="btn btn-xs btn-default command-delete bootgrid-tooltip" data-row-id="' + row.uuid + '"><span class="fa fa-fw fa-trash-o"></span></button>';
},
"rowtoggle": function (column, row) {
if (parseInt(row[column.id], 2) === 1) {
return '<span style="cursor: pointer;" class="fa fa-fw fa-check-square-o command-toggle bootgrid-tooltip" data-value="1" data-row-id="' + row.uuid + '"></span>';
} else {
return '<span style="cursor: pointer;" class="fa fa-fw fa-square-o command-toggle bootgrid-tooltip" data-value="0" data-row-id="' + row.uuid + '"></span>';
}
},
},
}
}).on("selected.rs.jquery.bootgrid", function (e, rows) {
$("#grid-aliases").bootgrid('reload');
}).on("deselected.rs.jquery.bootgrid", function (e, rows) {
$("#grid-aliases").bootgrid('reload');
}).on("loaded.rs.jquery.bootgrid", function (e) {
let ids = $("#grid-hosts").bootgrid("getCurrentRows");
if (ids.length > 0) {
$("#grid-hosts").bootgrid('select', [ids[0].uuid]);
}
/* Hide/unhide input fields based on selected RR (Type) value */
$('select[id="host.rr"]').on('change', function(e) {
if (this.value == "A") {
$('tr[id="row_host.mx"]').addClass('hidden');
$('tr[id="row_host.mxprio"]').addClass('hidden');
$('tr[id="row_host.server"]').removeClass('hidden');
} else if (this.value == "MX") {
$('tr[id="row_host.server"]').addClass('hidden');
$('tr[id="row_host.mx"]').removeClass('hidden');
$('tr[id="row_host.mxprio"]').removeClass('hidden');
}
});
});
let grid_aliases = $("#grid-aliases").UIBootgrid({
search:'/api/unbound/settings/searchHostAlias/',
get:'/api/unbound/settings/getHostAlias/',
set:'/api/unbound/settings/setHostAlias/',
add:'/api/unbound/settings/addHostAlias/',
del:'/api/unbound/settings/delHostAlias/',
toggle:'/api/unbound/settings/toggleHostAlias/',
options: {
selection: true,
multiSelect: true,
rowSelect: true,
useRequestHandlerOnGet: true,
requestHandler: function(request) {
let uuids = $("#grid-hosts").bootgrid("getSelectedRows");
request['host'] = uuids.length > 0 ? uuids[0] : "__not_found__";
let selected = $(".host_selected");
uuids.length > 0 ? selected.show() : selected.hide();
return request;
}
}
});
if (!heading_appended) {
$("div.actionBar").each(function(){
if ($(this).closest(".bootgrid-header").attr("id").includes("alias")) {
$(this).parent().prepend($('<td id="heading-wrapper" class="col-sm-2 theading-text">{{ lang._('Aliases') }}</div>'));
$(this).removeClass("col-sm-12");
$(this).addClass("col-sm-10");
heading_appended = true;
}
});
}
} else if (e.target.id == 'domain_overrides_tab') {
$("#grid-aliases-wrapper").hide();
$("#grid-domains").bootgrid('destroy');
let grid_domains = $("#grid-domains").UIBootgrid({
search:'/api/unbound/settings/searchDomainOverride/',
get:'/api/unbound/settings/getDomainOverride/',
set:'/api/unbound/settings/setDomainOverride/',
add:'/api/unbound/settings/addDomainOverride/',
del:'/api/unbound/settings/delDomainOverride/',
toggle:'/api/unbound/settings/toggleDomainOverride/',
options: {
selection: true,
multiSelect: true,
rowSelect: true,
}
});
}
});
if (window.location.hash != "") {
$('a[href="' + window.location.hash + '"]').click();
} else {
$('a[href="#host_overrides"]').click();
}
/**
* Reconfigure unbound - activate changes
*/
$("#reconfigureAct").SimpleActionButton();
updateServiceControlUI('unbound');
});
</script>
<style>
.theading-text {
font-weight: 800;
font-style: italic;
}
#infosection {
margin: 1em;
}
</style>
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li><a data-toggle="tab" href="#host_overrides" id="host_overrides_tab">{{ lang._('Host Overrides') }}</a></li>
<li><a data-toggle="tab" href="#domain_overrides" id="domain_overrides_tab">{{ lang._('Domain Overrides') }}</a></li>
</ul>
<div class="tab-content content-box col-xs-12 __mb">
<!-- host overrides -->
<div id="host_overrides" class="tab-pane fade in active">
<table id="grid-hosts" class="table table-condensed table-hover table-striped" data-editDialog="DialogHostOverride" data-editAlert="OverrideChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="hostname" data-type="string">{{ lang._('Host') }}</th>
<th data-column-id="domain" data-type="string">{{ lang._('Domain') }}</th>
<th data-column-id="rr" data-type="string">{{ lang._('Type') }}</th>
<th data-column-id="server" data-type="string" data-formatter="mxformatter">{{ lang._('Value') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Edit') }} | {{ lang._('Delete') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button id="test" data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-fw fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-fw fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div id="infosection" class="tab-content col-xs-12 __mb">
{{ lang._('Entries in this section override individual results from the forwarders.') }}
{{ lang._('Use these for changing DNS results or for adding custom DNS records.') }}
{{ lang._('Keep in mind that all resource record types (i.e. A, AAAA, MX, etc. records) of a specified host below are being overwritten.') }}
</div>
</div>
<!-- domain overrides -->
<div id="domain_overrides" class="tab-pane fade in">
<table id="grid-domains" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogDomainOverride" data-editAlert="OverrideChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="domain" data-type="string">{{ lang._('Domain') }}</th>
<th data-column-id="server" data-type="string">{{ lang._('IP') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Edit') }} | {{ lang._('Delete') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-fw fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-fw fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<div id="infosection" class="tab-content col-xs-12 __mb">
{{ lang._('Entries in this area override an entire domain by specifying an authoritative DNS server to be queried for that domain.') }}
</div>
</div>
</div>
<!-- aliases for host overrides -->
<div class="tab-content content-box col-xs-12 __mb" id ="grid-aliases-wrapper">
<table id="grid-aliases" class="table table-condensed table-hover table-striped" data-editDialog="DialogHostAlias" data-editAlert="OverrideChangeMessage">
<thead>
<tr>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="hostname" data-type="string">{{ lang._('Host') }}</th>
<th data-column-id="domain" data-type="string">{{ lang._('Domain') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot class="host_selected">
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-primary"><span class="fa fa-fw fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
</div>
<!-- reconfigure -->
<div class="tab-content content-box col-xs-12 __mb">
<div id="OverrideChangeMessage" class="alert alert-info" style="display: none" role="alert">
{{ lang._('After changing settings, please remember to apply them with the button below') }}
</div>
<table class="table table-condensed">
<tbody>
<tr>
<td>
<button class="btn btn-primary" id="reconfigureAct"
data-endpoint='/api/unbound/service/reconfigure'
data-label="{{ lang._('Apply') }}"
data-service-widget="unbound"
data-error-title="{{ lang._('Error reconfiguring unbound') }}"
type="button"
></button>
</td>
</tr>
</tbody>
</table>
</div>
{{ partial("layout_partials/base_dialog",['fields':formDialogHostOverride,'id':'DialogHostOverride','label':lang._('Edit Host Override')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogHostAlias,'id':'DialogHostAlias','label':lang._('Edit Host Override Alias')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogDomainOverride,'id':'DialogDomainOverride','label':lang._('Edit Domain Override')])}}

View File

@ -33,7 +33,6 @@ import glob
import collections
import netaddr
class SortKeyHelper:
""" generate item key for sort function
"""

View File

@ -69,6 +69,9 @@ class Template(object):
self._j2_env.filters['shlex_split'] = shlex.split
self._j2_env.filters['regex_replace'] = lambda value, pattern, replacement: re.sub(pattern, replacement, value)
# register additional tests
self._j2_env.tests['regex_match'] = lambda value, pattern: bool(re.match(pattern, value))
@staticmethod
def _encode_idna(x):
""" encode string to idna, preserve leading dots

View File

@ -1,5 +1,7 @@
blocklists.conf:/tmp/unbound-blocklists.conf
dot.conf:/usr/local/etc/unbound.opnsense.d/dot.conf
private_domains.conf:/var/unbound/private_domains.conf
domainoverrides.conf:/usr/local/etc/unbound.opnsense.d/domainoverrides.conf
miscellaneous.conf:/usr/local/etc/unbound.opnsense.d/miscellaneous.conf
root.min.hints:/var/unbound/root.hints
unbound_dhcpd.conf:/usr/local/etc/unbound_dhcpd.conf

View File

@ -0,0 +1,17 @@
{% if not helpers.empty('OPNsense.unboundplus.domains.domain') %}
{% set forwardlocal = namespace(found=false) %}
{% for domain in helpers.toList('OPNsense.unboundplus.domains.domain') %}
{% if domain.enabled == '1' %}
forward-zone:
name: "{{ domain.domain }}"
{% if domain.server.startswith('127.') or domain.server == '::1' %}
{% set forwardlocal.found = true %}
{% endif%}
forward-addr: {{ domain.server }}
{% endif %}
{% endfor %}
{% if forwardlocal.found %}
server:
do-not-query-localhost: no
{% endif %}
{% endif%}

View File

@ -0,0 +1,13 @@
{% if not helpers.empty('OPNsense.unboundplus.domains.domain') %}
# Set private domains in case authoritative name server returns a Private IP address
{% for domain in helpers.toList('OPNsense.unboundplus.domains.domain') %}
{% if domain.enabled == '1' %}
domain-insecure: "{{ domain.domain }}"
{% if domain.domain is regex_match('.+\.(in-addr|ip6)\.arpa\.?$') %}
local-zone: {{ domain.domain }} typetransparent
{% elif not helpers.exists('system.webgui.nodnsrebindcheck') %}
private-domain: "{{ domain.domain }}"
{% endif%}
{% endif %}
{% endfor %}
{% endif %}

View File

@ -1,166 +0,0 @@
<?php
/*
* Copyright (C) 2014-2016 Deciso B.V.
* Copyright (C) 2014 Warren Baker <warren@decoy.co.za>
* Copyright (C) 2003-2005 Bob Zoller <bob@kludgebox.com>
* Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
require_once("guiconfig.inc");
require_once("interfaces.inc");
$a_domainOverrides = &config_read_array('unbound', 'domainoverrides');
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if (isset($_GET['id']) && !empty($a_domainOverrides[$_GET['id']])) {
$id = $_GET['id'];
}
$pconfig = array();
$pconfig['domain'] = isset($id) && !empty($a_domainOverrides[$id]['domain']) ? $a_domainOverrides[$id]['domain'] : null;
$pconfig['ip'] = isset($id) && !empty($a_domainOverrides[$id]['ip']) ? $a_domainOverrides[$id]['ip'] : null;
$pconfig['descr'] = isset($id) && !empty($a_domainOverrides[$id]['descr']) ? $a_domainOverrides[$id]['descr'] : null;
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_GET['id']) && !empty($a_domainOverrides[$_POST['id']])) {
$id = $_POST['id'];
}
$input_errors= array();
$pconfig = $_POST;
/* input validation */
$reqdfields = explode(" ", "domain ip");
$reqdfieldsn = array(gettext("Domain"),gettext("IP address"));
do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors);
if (!empty($pconfig['domain']) && substr($pconfig['domain'], 0, 6) == '_msdcs') {
$subdomainstr = substr($pconfig['domain'], 7);
if ($subdomainstr && !is_domain($subdomainstr)) {
$input_errors[] = gettext("A valid domain must be specified after _msdcs.");
}
} elseif (!empty($pconfig['domain']) && !is_domain($_POST['domain'])) {
$input_errors[] = gettext("A valid domain must be specified.");
}
if (!empty($pconfig['ip'])) {
if (strpos($pconfig['ip'],'@') !== false) {
$ip_details = explode("@", $pconfig['ip']);
if (!is_ipaddr($ip_details[0]) && !is_port($ip_details[1])) {
$input_errors[] = gettext("A valid IP address and port must be specified, for example 192.168.100.10@5353.");
}
} elseif (!is_ipaddr($pconfig['ip'])) {
$input_errors[] = gettext("A valid IP address must be specified, for example 192.168.100.10.");
}
}
if (count($input_errors) == 0) {
$doment = array();
$doment['domain'] = $pconfig['domain'];
$doment['ip'] = $pconfig['ip'];
$doment['descr'] = $pconfig['descr'];
if (isset($id)) {
$a_domainOverrides[$id] = $doment;
} else {
$a_domainOverrides[] = $doment;
}
mark_subsystem_dirty('unbound');
write_config();
header(url_safe('Location: /services_unbound_overrides.php'));
exit;
}
}
$service_hook = 'unbound';
legacy_html_escape_form_data($pconfig);
include("head.inc");
?>
<body>
<?php include("fbegin.inc"); ?>
<section class="page-content-main">
<div class="container-fluid">
<div class="row">
<?php if (isset($input_errors) && count($input_errors) > 0) print_input_errors($input_errors); ?>
<section class="col-xs-12">
<div class="content-box">
<form method="post" name="iform" id="iform">
<div class="table-responsive">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td style="width:22%"><strong><?=gettext("Edit Domain Override entry");?></strong></td>
<td style="width:78%; text-align:right">
<small><?=gettext("full help"); ?> </small>
<i class="fa fa-toggle-off text-danger" style="cursor: pointer;" id="show_all_help_page"></i>
</td>
</tr>
<tr>
<td><a id="help_for_domain" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Domain");?></td>
<td>
<input name="domain" type="text" id="domain" size="40" value="<?=$pconfig['domain'];?>" />
<div class="hidden" data-for="help_for_domain">
<?=gettext("Domain to override (NOTE: this does not have to be a valid TLD!)"); ?><br />
<?=gettext("e.g."); ?> <em><?=gettext("test"); ?></em> <?=gettext("or"); ?> <em>mycompany.localdomain</em> <?=gettext("or"); ?> <em>1.168.192.in-addr.arpa</em>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_ip" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IP address");?></td>
<td>
<input name="ip" type="text" id="ip" size="40" value="<?=$pconfig['ip'];?>" />
<div class="hidden" data-for="help_for_ip">
<?=gettext("IP address of the authoritative DNS server for this domain"); ?><br />
<?=gettext("e.g."); ?> <em>192.168.100.100</em><br />
<?=gettext("To use a nondefault port for communication, append an '@' with the port number."); ?><br />
</div>
</td>
</tr>
<tr>
<td><a id="help_for_descr" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Description");?></td>
<td>
<input name="descr" type="text" id="descr" size="40" value="<?=$pconfig['descr'];?>" />
<div class="hidden" data-for="help_for_descr">
<?=gettext("You may enter a description here for your reference (not parsed).");?>
</div>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<input name="Submit" type="submit" class="btn btn-primary" value="<?= html_safe(gettext('Save')) ?>" />
<input type="button" class="btn btn-default" value="<?= html_safe(gettext('Cancel')) ?>" onclick="window.location.href='/services_unbound_overrides.php'" />
<?php if (isset($id)): ?>
<input name="id" type="hidden" value="<?=htmlspecialchars($id);?>" />
<?php endif; ?>
</td>
</tr>
</table>
</div>
</form>
</div>
</section>
</div>
</div>
</section>
<?php include("foot.inc"); ?>

View File

@ -1,377 +0,0 @@
<?php
/*
* Copyright (C) 2015 Manuel Faux <mfaux@conf.at>
* Copyright (C) 2014-2016 Deciso B.V.
* Copyright (C) 2014 Warren Baker <warren@decoy.co.za>
* Copyright (C) 2003-2004 Bob Zoller <bob@kludgebox.com>
* Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
require_once("guiconfig.inc");
require_once("interfaces.inc");
$a_hosts = &config_read_array('unbound', 'hosts');
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
if (isset($_GET['id']) && !empty($a_hosts[$_GET['id']])) {
$id = $_GET['id'];
}
$pconfig = array();
foreach (array('rr', 'host', 'domain', 'ip', 'mxprio', 'mx', 'descr', 'aliases') as $fieldname) {
if (isset($id) && !empty($a_hosts[$id][$fieldname])) {
$pconfig[$fieldname] = $a_hosts[$id][$fieldname];
} else {
$pconfig[$fieldname] = null;
}
}
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['id']) && !empty($a_hosts[$_POST['id']])) {
$id = $_POST['id'];
}
$input_errors = array();
$pconfig = $_POST;
$pconfig['aliases'] = array();
if (isset($pconfig['aliases_host'])) {
$pconfig['aliases']['item'] = array();
foreach ($pconfig['aliases_host'] as $opt_seq => $opt_host) {
if (empty($opt_host) && empty($pconfig['aliases_domain'][$opt_seq]) && empty($pconfig['aliases_descr'][$opt_seq])) {
continue;
}
$pconfig['aliases']['item'][] = array(
'domain' => $pconfig['aliases_domain'][$opt_seq],
'descr' => $pconfig['aliases_descr'][$opt_seq],
'host' => $opt_host,
);
}
}
$reqdfields = explode(" ", "domain rr");
$reqdfieldsn = array(gettext("Domain"),gettext("Type"));
do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors);
if (!empty($pconfig['host']) && !is_hostname($pconfig['host']) && $pconfig['host'] != '*') {
$input_errors[] = gettext("The hostname can only contain the characters A-Z, 0-9 and '-'.");
}
if (!empty($pconfig['domain']) && !is_domain($pconfig['domain'])) {
$input_errors[] = gettext("A valid domain must be specified.");
}
if (!empty($pconfig['domain']) && $pconfig['domain'] == $config['system']['domain'] && $pconfig['host'] == '*') {
$input_errors[] = sprintf(
gettext("A wildcard domain override is not supported for the local domain '%s'."),
$config['system']['domain']
);
}
switch ($pconfig['rr']) {
case 'A': /* also: AAAA */
$reqdfields = explode(" ", "ip");
$reqdfieldsn = array(gettext("IP address"));
do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors);
if (!empty($pconfig['ip']) && !is_ipaddr($pconfig['ip'])) {
$input_errors[] = gettext("A valid IP address must be specified.");
}
break;
case 'MX':
$reqdfields = explode(" ", "mxprio mx");
$reqdfieldsn = array(gettext("MX Priority"), gettext("MX Host"));
do_input_validation($pconfig, $reqdfields, $reqdfieldsn, $input_errors);
if (!empty($pconfig['mxprio']) && !is_numericint($pconfig['mxprio'])) {
$input_errors[] = gettext("A valid MX priority must be specified.");
}
if (!empty($pconfig['mx']) && !is_domain($pconfig['mx'])) {
$input_errors[] = gettext("A valid MX host must be specified.");
}
break;
default:
$input_errors[] = gettext("A valid resource record type must be specified.");
break;
}
foreach ($pconfig['aliases']['item'] as $idx => $alias) {
if (!empty($alias['host']) && !is_hostname($alias['host'])) {
$input_errors[] = gettext('Hostnames in alias list can only contain the characters A-Z, 0-9 and \'-\'.');
}
if (!empty($alias['domain']) && !is_domain($alias['domain'])) {
$input_errors[] = gettext('A valid domain must be specified in alias list.');
}
if (empty($alias['host']) && empty($alias['domain'])) {
$input_errors[] = gettext('A valid hostname or domain must be specified in alias list.');
}
}
if (count($input_errors) == 0) {
$hostent = array();
$hostent['host'] = $pconfig['host'];
$hostent['domain'] = $pconfig['domain'];
/* distinguish between A and AAAA by parsing the passed IP address */
$hostent['rr'] = ($pconfig['rr'] == 'A' && is_ipaddrv6($pconfig['ip'])) ? 'AAAA' : $pconfig['rr'];
$hostent['ip'] = $pconfig['ip'];
$hostent['mxprio'] = $pconfig['mxprio'];
$hostent['mx'] = $pconfig['mx'];
$hostent['descr'] = $pconfig['descr'];
$hostent['aliases'] = $pconfig['aliases'];
if (isset($id)) {
$a_hosts[$id] = $hostent;
} else {
$a_hosts[] = $hostent;
}
usort($a_hosts, function ($a, $b) {
return strcasecmp($a['host'], $b['host']);
});
mark_subsystem_dirty('unbound');
write_config();
header(url_safe('Location: /services_unbound_overrides.php'));
exit;
}
}
$service_hook = 'unbound';
legacy_html_escape_form_data($pconfig);
include("head.inc");
?>
<script>
$( document ).ready(function() {
$("#rr").change(function() {
$(".a_aaa_rec").hide();
$(".mx_rec").hide();
switch ($(this).val()) {
case 'A':
$('#ip').prop('disabled', false);
$('#mxprio').prop('disabled', true);
$('#mx').prop('disabled', true);
$(".a_aaa_rec").show();
break;
case 'MX':
$('#ip').prop('disabled', true);
$('#mxprio').prop('disabled', false);
$('#mx').prop('disabled', false);
$(".mx_rec").show();
break;
}
$( window ).resize(); // call window resize, which will re-apply zebra
});
// trigger initial change
$("#rr").change();
/**
* Aliases
*/
function removeRow() {
if ( $('#aliases_table > tbody > tr').length == 1 ) {
$('#aliases_table > tbody > tr:last > td > input').each(function(){
$(this).val("");
});
} else {
$(this).parent().parent().remove();
}
}
// add new detail record
$("#addNew").click(function(){
// copy last row and reset values
$('#aliases_table > tbody').append('<tr>'+$('#aliases_table > tbody > tr:last').html()+'</tr>');
$('#aliases_table > tbody > tr:last > td > input').each(function(){
$(this).val("");
});
$(".act-removerow").click(removeRow);
});
$(".act-removerow").click(removeRow);
});
</script>
<body>
<?php include("fbegin.inc"); ?>
<section class="page-content-main">
<div class="container-fluid">
<div class="row">
<?php if (isset($input_errors) && count($input_errors) > 0) print_input_errors($input_errors); ?>
<section class="col-xs-12">
<div class="content-box">
<form method="post" name="iform" id="iform">
<div class="table-responsive">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td style="width:22%"><strong><?= gettext('Edit entry') ?></strong></td>
<td style="width:78%; text-align:right">
<small><?=gettext("full help"); ?> </small>
<i class="fa fa-toggle-off text-danger" style="cursor: pointer;" id="show_all_help_page"></i>
</td>
</tr>
<tr>
<td><a id="help_for_host" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Host");?></td>
<td>
<input name="host" type="text" value="<?=$pconfig['host'];?>" />
<div class="hidden" data-for="help_for_host">
<?= gettext('Name of the host, without domain part. Use "*" to create a wildcard entry.') ?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_domain" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Domain");?></td>
<td>
<input name="domain" type="text" value="<?=$pconfig['domain'];?>" />
<div class="hidden" data-for="help_for_domain">
<?=gettext("Domain of the host"); ?><br />
<?=gettext("e.g."); ?> <em><?=gettext("example.com"); ?></em>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_rr" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Type");?></td>
<td>
<select name="rr" id="rr" class="selectpicker">
<?php
$rrs = array("A" => gettext("A or AAAA (IPv4 or IPv6 address)"), "MX" => gettext("MX (Mail server)"));
foreach ($rrs as $rr => $name) :?>
<option value="<?=$rr;?>" <?=($rr == $pconfig['rr'] || ($rr == 'A' && $pconfig['rr'] == 'AAAA')) ? 'selected="selected"' : '';?> >
<?=$name;?>
</option>
<?php
endforeach; ?>
</select>
<div class="hidden" data-for="help_for_rr">
<?=gettext("Type of resource record"); ?>
<br />
<?=gettext("e.g."); ?> <em>A</em> <?=gettext("or"); ?> <em>AAAA</em> <?=gettext("for IPv4 or IPv6 addresses"); ?>
</div>
</td>
</tr>
<tr class="a_aaa_rec">
<td><a id="help_for_ip" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("IP");?></td>
<td>
<input name="ip" type="text" id="ip" value="<?=$pconfig['ip'];?>" />
<div class="hidden" data-for="help_for_ip">
<?=gettext("IP address of the host"); ?><br />
<?=gettext("e.g."); ?> <em>192.168.100.100</em> <?=gettext("or"); ?> <em>fd00:abcd::1</em>
</div>
</td>
</tr>
<tr class="mx_rec">
<td><a id="help_for_mxprio" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("MX Priority");?></td>
<td>
<input name="mxprio" type="text" id="mxprio" value="<?=$pconfig['mxprio'];?>" />
<div class="hidden" data-for="help_for_mxprio">
<?=gettext("Priority of MX record"); ?><br />
<?=gettext("e.g."); ?> <em>10</em>
</div>
</td>
</tr>
<tr class="mx_rec">
<td><a id="help_for_mx" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("MX Host");?></td>
<td>
<input name="mx" type="text" id="mx" size="6" value="<?=$pconfig['mx'];?>" />
<div class="hidden" data-for="help_for_mx">
<?=gettext("Host name of MX host"); ?><br />
<?=gettext("e.g."); ?> <em>mail.example.com</em>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_descr" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Description");?></td>
<td>
<input name="descr" type="text" id="descr" value="<?=$pconfig['descr'];?>" />
<div class="hidden" data-for="help_for_descr">
<?=gettext("You may enter a description here for your reference (not parsed).");?>
</div>
</td>
</tr>
<tr>
<td><a id="help_for_alias" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("Aliases"); ?></td>
<td>
<table class="table table-striped table-condensed" id="aliases_table">
<thead>
<tr>
<th></th>
<th id="detailsHeading1"><?=gettext("Host"); ?></th>
<th id="detailsHeading3"><?=gettext("Domain"); ?></th>
<th id="updatefreqHeader" ><?=gettext("Description");?></th>
</tr>
</thead>
<tbody>
<?php
$aliases = !empty($pconfig['aliases']['item']) ? $pconfig['aliases']['item'] : array();
$aliases[] = array('number' => null, 'value' => null, 'type' => null);
foreach($aliases as $item): ?>
<tr>
<td>
<div style="cursor:pointer;" class="act-removerow btn btn-default btn-xs"><i class="fa fa-minus fa-fw"></i></div>
</td>
<td>
<input name="aliases_host[]" type="text" value="<?=$item['host'];?>" />
</td>
<td>
<input name="aliases_domain[]" type="text" value="<?=$item['domain'];?>" />
</td>
<td>
<input name="aliases_descr[]" type="text" value="<?=$item['descr'];?>" />
</td>
</tr>
<?php
endforeach;?>
</tbody>
<tfoot>
<tr>
<td colspan="4">
<div id="addNew" style="cursor:pointer;" class="btn btn-default btn-xs"><i class="fa fa-plus fa-fw"></i></div>
</td>
</tr>
</tfoot>
</table>
<div class="hidden" data-for="help_for_alias">
<?=gettext("Enter additional names for this host."); ?>
</div>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<input name="Submit" type="submit" class="btn btn-primary" value="<?= html_safe(gettext('Save')) ?>" />
<input type="button" class="btn btn-default" value="<?= html_safe(gettext('Cancel')) ?>" onclick="window.location.href='/services_unbound_overrides.php'" />
<?php if (isset($id)): ?>
<input name="id" type="hidden" value="<?=$id;?>" />
<?php endif; ?>
</td>
</tr>
</table>
</div>
</form>
</div>
</section>
</div>
</div>
</section>
<?php include("foot.inc"); ?>

View File

@ -1,252 +0,0 @@
<?php
/*
* Copyright (C) 2015 Manuel Faux <mfaux@conf.at>
* Copyright (C) 2014-2016 Deciso B.V.
* Copyright (C) 2014 Warren Baker <warren@decoy.co.za>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
require_once("guiconfig.inc");
require_once("system.inc");
require_once("interfaces.inc");
require_once("plugins.inc.d/unbound.inc");
$a_hosts = &config_read_array('unbound', 'hosts');
$a_domains = &config_read_array('unbound', 'domainoverrides');
/* Backwards compatibility for records created before introducing RR types. */
foreach ($a_hosts as $i => $hostent) {
if (!isset($hostent['rr'])) {
$a_hosts[$i]['rr'] = is_ipaddrv6($hostent['ip']) ? 'AAAA' : 'A';
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$pconfig = $_POST;
if (!empty($pconfig['apply'])) {
unbound_configure_do();
clear_subsystem_dirty('unbound');
header(url_safe('Location: /services_unbound_overrides.php'));
exit;
} elseif (!empty($pconfig['act']) && $pconfig['act'] == 'del') {
if (isset($pconfig['id']) && !empty($a_hosts[$pconfig['id']])) {
unset($a_hosts[$pconfig['id']]);
write_config();
mark_subsystem_dirty('unbound');
exit;
}
} elseif (!empty($pconfig['act']) && $pconfig['act'] == 'doverride') {
if (isset($pconfig['id']) && !empty($a_domains[$pconfig['id']])) {
unset($a_domains[$pconfig['id']]);
write_config();
mark_subsystem_dirty('unbound');
exit;
}
}
}
$service_hook = 'unbound';
legacy_html_escape_form_data($a_hosts);
legacy_html_escape_form_data($a_domains);
include_once("head.inc");
?>
<body>
<script>
$( document ).ready(function() {
// delete host action
$(".act_delete_host").click(function(event){
event.preventDefault();
var id = $(this).data("id");
// delete single
BootstrapDialog.show({
type:BootstrapDialog.TYPE_DANGER,
title: "<?= gettext('Unbound') ?>",
message: "<?=gettext("Do you really want to delete this host?");?>",
buttons: [{
label: "<?= gettext("No");?>",
action: function(dialogRef) {
dialogRef.close();
}}, {
label: "<?= gettext("Yes");?>",
action: function(dialogRef) {
$.post(window.location, {act: 'del', id:id}, function(data) {
location.reload();
});
}
}]
});
});
$(".act_delete_override").click(function(event){
event.preventDefault();
var id = $(this).data("id");
// delete single
BootstrapDialog.show({
type:BootstrapDialog.TYPE_DANGER,
title: "<?= gettext('Unbound') ?>",
message: "<?=gettext("Do you really want to delete this domain override?");?>",
buttons: [{
label: "<?= gettext("No");?>",
action: function(dialogRef) {
dialogRef.close();
}}, {
label: "<?= gettext("Yes");?>",
action: function(dialogRef) {
$.post(window.location, {act: 'doverride', id:id}, function(data) {
location.reload();
});
}
}]
});
});
});
</script>
<?php include("fbegin.inc"); ?>
<section class="page-content-main">
<div class="container-fluid">
<div class="row">
<?php if (is_subsystem_dirty('unbound')): ?>
<?php print_info_box_apply(gettext('The Unbound configuration has been changed.') . ' ' . gettext('You must apply the changes in order for them to take effect.')) ?>
<?php endif; ?>
<form method="post" name="iform" id="iform">
<section class="col-xs-12">
<div class="content-box">
<div class="table-responsive">
<table class="table table-striped">
<tbody>
<tr>
<td colspan="6"><strong><?= gettext('Host Overrides') ?></strong></td>
</tr>
<tr>
<td><strong><?= gettext('Host') ?></strong></td>
<td><strong><?= gettext('Domain') ?></strong></td>
<td><strong><?= gettext('Type') ?></strong></td>
<td><strong><?= gettext('Value') ?></strong></td>
<td><strong><?= gettext('Description') ?></strong></td>
<td class="text-nowrap">
<a href="services_unbound_host_edit.php" class="btn btn-primary btn-xs"><i class="fa fa-plus fa-fw"></i></a>
</td>
</tr>
<?php foreach ($a_hosts as $i => $hostent): ?>
<tr>
<td><?=strtolower($hostent['host']);?></td>
<td><?=strtolower($hostent['domain']);?></td>
<td><?=strtoupper($hostent['rr']);?></td>
<td>
<?php
/* Presentation of DNS value differs between chosen RR type. */
switch ($hostent['rr']) {
case 'A':
case 'AAAA':
print $hostent['ip'];
break;
case 'MX':
print $hostent['mxprio'] . " " . $hostent['mx'];
break;
default:
print '&nbsp;';
break;
}?>
</td>
<td><?=$hostent['descr'];?></td>
<td class="text-nowrap">
<a href="services_unbound_host_edit.php?id=<?=$i;?>" class="btn btn-default btn-xs"><i class="fa fa-pencil fa-fw"></i></a>
<a href="#" data-id="<?=$i;?>" class="act_delete_host btn btn-xs btn-default"><i class="fa fa-trash fa-fw"></i></a>
</td>
</tr>
<?php if (isset($hostent['aliases']['item'])): ?>
<?php foreach ($hostent['aliases']['item'] as $alias): ?>
<tr>
<td><?= strtolower(!empty($alias['host']) ? $alias['host'] : $hostent['host']) ?></td>
<td><?= strtolower(!empty($alias['domain']) ? $alias['domain'] : $hostent['domain']) ?></td>
<td><?=strtoupper($hostent['rr']);?></td>
<td><?= gettext('Alias for');?> <?=$hostent['host'] ? htmlspecialchars($hostent['host'] . '.' . $hostent['domain']) : htmlspecialchars($hostent['domain']);?></td>
<td><?= !empty($alias['descr']) ? $alias['descr'] : $hostent['descr'] ?></td>
<td class="text-nowrap">
<a href="services_unbound_host_edit.php?id=<?=$i;?>" class="btn btn-default btn-xs"><i class="fa fa-pencil fa-fw"></i></a>
</td>
</tr>
<?php endforeach ?>
<?php endif ?>
<?php endforeach ?>
<tr>
<td colspan="6">
<?=gettext("Entries in this section override individual results from the forwarders.");?>
<?=gettext("Use these for changing DNS results or for adding custom DNS records.");?>
<?=gettext("Keep in mind that all resource record types (i.e. A, AAAA, MX, etc. records) of a specified host below are being overwritten.");?>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<section class="col-xs-12">
<div class="content-box">
<div class="table-responsive">
<table class="table table-striped">
<tbody>
<tr>
<td colspan="4"><strong><?= gettext('Domain Overrides') ?></strong></td>
</tr>
<tr>
<td><strong><?= gettext('Domain') ?></strong></td>
<td><strong><?= gettext('IP') ?></strong></td>
<td><strong><?= gettext('Description') ?></strong></td>
<td class="text-nowrap">
<a href="services_unbound_domainoverride_edit.php" class="btn btn-primary btn-xs"><i class="fa fa-plus fa-fw"></i></a>
</td>
</tr>
<?php foreach ($a_domains as $i => $doment): ?>
<tr>
<td><?= strtolower($doment['domain']) ?></td>
<td><?= $doment['ip'] ?></td>
<td><?= $doment['descr'] ?></td>
<td class="text-nowrap">
<a href="services_unbound_domainoverride_edit.php?id=<?=$i;?>" class="btn btn-default btn-xs"><i class="fa fa-pencil fa-fw"></i></a>
<a href="#" data-id="<?=$i;?>" class="act_delete_override btn btn-xs btn-default"><i class="fa fa-trash fa-fw"></i></a>
</td>
</tr>
<?php endforeach ?>
<tr>
<td colspan="4">
<?= gettext('Entries in this area override an entire domain by specifying an authoritative DNS server to be queried for that domain.') ?>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</form>
</div>
</div>
</section>
<?php include("foot.inc"); ?>