Skip to content

Commit e7b371c

Browse files
Fixed issue that prevent aliases from using same name for different IP types, updated unit test to check this case
1 parent 5849543 commit e7b371c

8 files changed

Lines changed: 182 additions & 71 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
2+
*/__pycache__/
23
*.DS_Store
34
.phplint-cache
45

pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ function get($id, $data=[], $all=false) {
272272
"status" => "bad request",
273273
"code" => 400,
274274
"return" => $id,
275-
"message" => "Unbound host override alias already exists"
275+
"message" => "Unbound host override alias already exists with this IP address type"
276276
],
277277
2010 => [
278278
"status" => "bad request",
@@ -490,6 +490,18 @@ function get($id, $data=[], $all=false) {
490490
"return" => $id,
491491
"message" => "DHCPd static mapping ID does not exist"
492492
],
493+
2046 => [
494+
"status" => "bad request",
495+
"code" => 400,
496+
"return" => $id,
497+
"message" => "Invalid unbound host value"
498+
],
499+
2047 => [
500+
"status" => "bad request",
501+
"code" => 400,
502+
"return" => $id,
503+
"message" => "Invalid unbound domain value"
504+
],
493505

494506
// 3000-3999 reserved for /interfaces API calls
495507
3000 => [

pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -690,38 +690,6 @@ function unbound_reload_config() {
690690
}
691691
}
692692

693-
// Check if a DNS Resolver (Unbound) host override already exists
694-
function is_unbound_fqdn($hostname, $domain, $ip=null, $instance_id=null) {
695-
# Local variables
696-
global $config;
697-
$curr_hosts = (array_key_exists("hosts", $config["unbound"])) ? $config["unbound"]["hosts"] : [];
698-
$index = 0;
699-
700-
# Loop through each host override and check if the FQDN already exists
701-
foreach ($curr_hosts as $ent) {
702-
# Check the FQDN matches this entry
703-
if ($ent["host"] === $hostname and $ent["domain"] === $domain) {
704-
# Host overrides are allowed an IPv4 and IPv6 address, check if it already has one.
705-
if (!$ip or (is_ipaddrv4($ent["ip"]) and is_ipaddrv4($ip)) or (is_ipaddrv6($ent["ip"]) and is_ipaddrv6($ip))){
706-
# If we are working with an existing instance, allow existing FQDN if ID matches
707-
if ($index !== $instance_id) {
708-
return true;
709-
}
710-
}
711-
}
712-
713-
# Check FQDN within host override aliases as well
714-
if (is_array($ent["aliases"])) {
715-
foreach ($ent["aliases"]["item"] as $alias_ent) {
716-
if ($alias_ent["host"] === $hostname and $alias_ent["domain"] === $domain) {
717-
return true;
718-
}
719-
}
720-
}
721-
$index++;
722-
}
723-
return false;
724-
}
725693

726694
// Get a complete config list of ALL interfaces. Based off interfaces_assign.php
727695
function get_all_avail_interfaces() {

pfSense-pkg-API/files/etc/inc/api/models/APIServicesUnboundHostOverrideAliasCreate.inc

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,14 @@ class APIServicesUnboundHostOverrideAliasCreate extends APIModel {
4242

4343
public function validate_payload() {
4444
$this->__validate_id();
45-
$this->__validate_host();
46-
$this->__validate_domain();
47-
$this->__validate_description();
45+
$this->validate_host();
46+
$this->validate_domain();
47+
$this->validate_description();
48+
49+
# Ensure alias host/domain combo isn't already used
50+
if (($this->alias_exists($this->validated_data))) {
51+
$this->errors[] = APIResponse\get(2009);
52+
}
4853
}
4954

5055
private function __validate_id() {
@@ -61,35 +66,69 @@ class APIServicesUnboundHostOverrideAliasCreate extends APIModel {
6166
}
6267
}
6368

64-
private function __validate_host() {
69+
public function validate_host() {
6570
# Check for our required 'host' payload value
6671
if (isset($this->initial_data["host"])) {
67-
$this->validated_data["host"] = trim($this->initial_data["host"]);
72+
# Ensure it is a valid hostname
73+
if (is_hostname($this->initial_data["host"])) {
74+
$this->validated_data["host"] = $this->initial_data['host'];
75+
} else {
76+
$this->errors[] = APIResponse\get(2046);
77+
}
6878
} else {
6979
$this->errors[] = APIResponse\get(2007);
7080
}
7181
}
7282

73-
private function __validate_domain() {
83+
public function validate_domain() {
7484
# Check for our required 'domain' payload value
7585
if (isset($this->initial_data["domain"])) {
76-
# Ensure this host/domain combo doesn't already exist
77-
if (APITools\is_unbound_fqdn($this->validated_data["host"], $this->validated_data["domain"])) {
78-
$this->errors[] = APIResponse\get(2009);
86+
# Ensure it is a validate hostname
87+
if (is_hostname($this->initial_data["domain"])) {
88+
$this->validated_data["domain"] = $this->initial_data['domain'];
7989
} else {
80-
$this->validated_data["domain"] = trim($this->initial_data["domain"]);
90+
$this->errors[] = APIResponse\get(2047);
8191
}
8292
} else {
8393
$this->errors[] = APIResponse\get(2008);
8494
}
8595
}
8696

87-
private function __validate_description() {
97+
public function validate_description() {
8898
# Check for our required 'domain' payload value
8999
if (isset($this->initial_data["domain"])) {
90100
$this->validated_data["domain"] = trim($this->initial_data["domain"]);
91101
} else {
92102
$this->errors[] = APIResponse\get(2008);
93103
}
94104
}
105+
106+
public function alias_exists($parent, $id=null) {
107+
# Local variables
108+
$host = $this->validated_data["host"];
109+
$domain = $this->validated_data["domain"];
110+
111+
# Loop through each host override and check if the FQDN already exists
112+
foreach ($this->config["unbound"]["hosts"] as $index=>$ent) {
113+
# Check the FQDN matches this entry
114+
if ($ent["host"] === $host and $ent["domain"] === $domain) {
115+
$this->errors[] = APIResponse\get(2009);
116+
}
117+
118+
# Check FQDN within host override aliases as well
119+
if (is_array($ent["aliases"])) {
120+
foreach ($ent["aliases"]["item"] as $alias_ent) {
121+
if ($alias_ent["host"] === $host and $alias_ent["domain"] === $domain) {
122+
$ip = $parent["ip"];
123+
# Aliases are allowed for an IPv4 and IPv6 alias, check if it already has one.
124+
if ((is_ipaddrv4($ent["ip"]) and is_ipaddrv4($ip)) or (is_ipaddrv6($ent["ip"]) and is_ipaddrv6($ip))){
125+
if ($index !== $id) {
126+
$this->errors[] = APIResponse\get(2009);
127+
}
128+
}
129+
}
130+
}
131+
}
132+
}
133+
}
95134
}

pfSense-pkg-API/files/etc/inc/api/models/APIServicesUnboundHostOverrideCreate.inc

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,31 +41,28 @@ class APIServicesUnboundHostOverrideCreate extends APIModel {
4141
}
4242

4343
public function validate_payload() {
44-
# Validate the IP first, it is needed to validate the host/domain
45-
$this->__validate_ip();
4644
$this->__validate_host();
4745
$this->__validate_domain();
48-
$this->__validate_descr();
49-
$this->__validate_aliases();
50-
}
46+
$this->__validate_ip();
5147

52-
private function __validate_ip() {
53-
# Check for our required 'ip' payload value
54-
if (isset($this->initial_data['ip'])) {
55-
if (!is_ipaddrv4($this->initial_data["ip"]) and !is_ipaddrv6($this->initial_data["ip"])) {
56-
$this->errors[] = APIResponse\get(2011);
57-
} else {
58-
$this->validated_data["ip"] = trim($this->initial_data['ip']);
59-
}
60-
} else {
61-
$this->errors[] = APIResponse\get(2006);
48+
# Before proceeding, check that a host override matching this host, domain and IP type doesn't already exist
49+
if ($this->host_override_exists()) {
50+
$this->errors[] = APIResponse\get(2010);
6251
}
52+
53+
$this->__validate_descr();
54+
$this->__validate_aliases();
6355
}
6456

6557
private function __validate_host() {
6658
# Check for our required 'host' payload value
6759
if (isset($this->initial_data['host'])) {
68-
$this->validated_data["host"] = trim($this->initial_data['host']);
60+
# Ensure it is a valid hostname
61+
if (is_hostname($this->initial_data["host"])) {
62+
$this->validated_data["host"] = $this->initial_data['host'];
63+
} else {
64+
$this->errors[] = APIResponse\get(2046);
65+
}
6966
} else {
7067
$this->errors[] = APIResponse\get(2004);
7168
}
@@ -74,16 +71,30 @@ class APIServicesUnboundHostOverrideCreate extends APIModel {
7471
private function __validate_domain() {
7572
# Check for our required 'domain' payload value
7673
if (isset($this->initial_data['domain'])) {
77-
if (APITools\is_unbound_fqdn($this->validated_data["host"], $this->initial_data["domain"], $this->validated_data["ip"])) {
78-
$this->errors[] = APIResponse\get(2010);
74+
# Ensure it is a validate hostname
75+
if (is_hostname($this->initial_data["domain"])) {
76+
$this->validated_data["domain"] = $this->initial_data['domain'];
7977
} else {
80-
$this->validated_data["domain"] = trim($this->initial_data['domain']);
78+
$this->errors[] = APIResponse\get(2047);
8179
}
8280
} else {
8381
$this->errors[] = APIResponse\get(2005);
8482
}
8583
}
8684

85+
private function __validate_ip() {
86+
# Check for our required 'ip' payload value
87+
if (isset($this->initial_data['ip'])) {
88+
if (!is_ipaddrv4($this->initial_data["ip"]) and !is_ipaddrv6($this->initial_data["ip"])) {
89+
$this->errors[] = APIResponse\get(2011);
90+
} else {
91+
$this->validated_data["ip"] = trim($this->initial_data['ip']);
92+
}
93+
} else {
94+
$this->errors[] = APIResponse\get(2006);
95+
}
96+
}
97+
8798
private function __validate_descr() {
8899
# Check for our optional 'descr' payload value
89100
if (isset($this->initial_data['descr'])) {
@@ -103,12 +114,42 @@ class APIServicesUnboundHostOverrideCreate extends APIModel {
103114
foreach ($this->initial_data['aliases'] as $alias) {
104115
$alias_create = new APIServicesUnboundHostOverrideAliasCreate();
105116
$alias_create->initial_data = $alias;
106-
$alias_create->validate_id = false;
107-
$alias_create->validate_payload();
117+
$alias_create->validate_host();
118+
$alias_create->validate_domain();
119+
$alias_create->validate_description();
120+
$alias_create->alias_exists($this->validated_data);
108121
$this->errors = array_merge($this->errors, $alias_create->errors);
109122
$this->validated_data["aliases"]["item"][] = $alias_create->validated_data;
110123
}
111124
}
112125
}
113126

127+
public function host_override_exists() {
128+
# Local variables
129+
$host = $this->validated_data["host"];
130+
$domain = $this->validated_data["domain"];
131+
$ip = $this->validated_data["ip"];
132+
133+
# Loop through each host override and check if the FQDN already exists
134+
foreach ($this->config["unbound"]["hosts"] as $ent) {
135+
# Check the FQDN matches this entry
136+
if ($ent["host"] === $host and $ent["domain"] === $domain) {
137+
# Host overrides are allowed an IPv4 and IPv6 address, check if it already has one.
138+
if ((is_ipaddrv4($ent["ip"]) and is_ipaddrv4($ip)) or (is_ipaddrv6($ent["ip"]) and is_ipaddrv6($ip))){
139+
return true;
140+
}
141+
}
142+
143+
# Check FQDN within host override aliases as well
144+
if (is_array($ent["aliases"])) {
145+
foreach ($ent["aliases"]["item"] as $alias_ent) {
146+
if ($alias_ent["host"] === $host and $alias_ent["domain"] === $domain) {
147+
return true;
148+
}
149+
}
150+
}
151+
}
152+
return false;
153+
}
154+
114155
}

pfSense-pkg-API/files/etc/inc/api/models/APIServicesUnboundHostOverrideUpdate.inc

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class APIServicesUnboundHostOverrideUpdate extends APIModel {
4646

4747
# If host or domain was updated, ensure this host override doesn't already exist
4848
if (isset($this->initial_data["host"]) or isset($this->initial_data["domain"])) {
49-
if (APITools\is_unbound_fqdn($this->validated_data["host"], $this->validated_data["domain"], $this->validated_data["ip"], $this->id)) {
49+
if ($this->host_override_exists()) {
5050
$this->errors[] = APIResponse\get(2010);
5151
}
5252
}
@@ -80,14 +80,23 @@ class APIServicesUnboundHostOverrideUpdate extends APIModel {
8080
private function __validate_host() {
8181
# Check for our optional 'host' payload value
8282
if (isset($this->initial_data['host'])) {
83-
$this->validated_data["host"] = trim($this->initial_data['host']);
84-
}
83+
# Ensure it is a valid hostname
84+
if (is_hostname($this->initial_data["host"])) {
85+
$this->validated_data["host"] = $this->initial_data['host'];
86+
} else {
87+
$this->errors[] = APIResponse\get(2046);
88+
} }
8589
}
8690

8791
private function __validate_domain() {
8892
# Check for our optional 'domain' payload value
8993
if (isset($this->initial_data['domain'])) {
90-
$this->validated_data["domain"] = trim($this->initial_data['domain']);
94+
# Ensure it is a validate hostname
95+
if (is_hostname($this->initial_data["domain"])) {
96+
$this->validated_data["domain"] = $this->initial_data['domain'];
97+
} else {
98+
$this->errors[] = APIResponse\get(2047);
99+
}
91100
}
92101
}
93102

@@ -110,11 +119,45 @@ class APIServicesUnboundHostOverrideUpdate extends APIModel {
110119
foreach ($this->initial_data['aliases'] as $alias) {
111120
$alias_create = new APIServicesUnboundHostOverrideAliasCreate();
112121
$alias_create->initial_data = $alias;
113-
$alias_create->validate_id = false;
114-
$alias_create->validate_payload();
122+
$alias_create->validate_host();
123+
$alias_create->validate_domain();
124+
$alias_create->validate_description();
125+
$alias_create->alias_exists($this->validated_data, $this->id);
115126
$this->errors = array_merge($this->errors, $alias_create->errors);
116127
$this->validated_data["aliases"]["item"][] = $alias_create->validated_data;
117128
}
118129
}
119130
}
131+
132+
public function host_override_exists() {
133+
# Local variables
134+
$host = $this->validated_data["host"];
135+
$domain = $this->validated_data["domain"];
136+
$ip = $this->validated_data["ip"];
137+
$id = $this->id;
138+
139+
# Loop through each host override and check if the FQDN already exists
140+
foreach ($this->config["unbound"]["hosts"] as $index=>$ent) {
141+
# Check the FQDN matches this entry
142+
if ($ent["host"] === $host and $ent["domain"] === $domain) {
143+
# Host overrides are allowed an IPv4 and IPv6 address, check if it already has one.
144+
if ((is_ipaddrv4($ent["ip"]) and is_ipaddrv4($ip)) or (is_ipaddrv6($ent["ip"]) and is_ipaddrv6($ip))){
145+
# If we are working with an existing instance, allow existing FQDN if ID matches
146+
if ($index !== $id) {
147+
return true;
148+
}
149+
}
150+
}
151+
152+
# Check FQDN within host override aliases as well
153+
if (is_array($ent["aliases"])) {
154+
foreach ($ent["aliases"]["item"] as $alias_ent) {
155+
if ($alias_ent["host"] === $host and $alias_ent["domain"] === $domain) {
156+
return true;
157+
}
158+
}
159+
}
160+
}
161+
return false;
162+
}
120163
}

tests/test_api_v1_services_unbound_host_override.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ class APIUnitTestServicesUnboundHostOverride(unit_test_framework.APIUnitTest):
3636
"domain": "unit.test",
3737
"ip": "fd00:abcd::",
3838
"descr": "Unit Test IPv6",
39+
"aliases": [
40+
{
41+
"host": "pfsense-api-alias",
42+
"domain": "unit.test",
43+
"description": "Unit Test"
44+
}
45+
]
3946
}
4047
]
4148
put_payloads = [
6.76 KB
Binary file not shown.

0 commit comments

Comments
 (0)