Skip to content

Commit f50083a

Browse files
author
Jared Hendrickson
committed
Added DDNS endpoint to read Dynamic DNS configuration and status, added unit test for DDNS endpoint, updated docs
1 parent 7927512 commit f50083a

9 files changed

Lines changed: 213 additions & 9 deletions

File tree

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,10 @@ There is no limit to API calls at this time but is important to note that pfSens
600600
* [Start All Services](#3-start-all-services)
601601
* [Stop All Services](#4-stop-all-services)
602602

603+
* [SERVICES/DDNS](#servicesddns)
604+
605+
* [Read Dynamic DNS](#1-read-dynamic-dns)
606+
603607
* [SERVICES/DHCPD](#servicesdhcpd)
604608

605609
* [Read DHCPd Service Configuration](#1-read-dhcpd-service-configuration)
@@ -3057,6 +3061,38 @@ URL: https://{{$hostname}}/api/v1/services/stop
30573061

30583062

30593063

3064+
## SERVICES/DDNS
3065+
3066+
3067+
3068+
### 1. Read Dynamic DNS
3069+
3070+
3071+
Read configured dynamic DNS settings and statuses.<br><br>
3072+
3073+
_Requires at least one of the following privileges:_ [`page-all`, `page-services-dynamicdnsclients`]
3074+
3075+
3076+
***Endpoint:***
3077+
3078+
```bash
3079+
Method: GET
3080+
Type: RAW
3081+
URL: https://{{$hostname}}/api/v1/services/ddns
3082+
```
3083+
3084+
3085+
3086+
***Body:***
3087+
3088+
```js
3089+
{
3090+
3091+
}
3092+
```
3093+
3094+
3095+
30603096
## SERVICES/DHCPD
30613097

30623098

docs/documentation.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7854,6 +7854,48 @@
78547854
"protocolProfileBehavior": {},
78557855
"_postman_isSubFolder": true
78567856
},
7857+
{
7858+
"name": "DDNS",
7859+
"item": [
7860+
{
7861+
"name": "Read Dynamic DNS",
7862+
"protocolProfileBehavior": {
7863+
"disableBodyPruning": true
7864+
},
7865+
"request": {
7866+
"method": "GET",
7867+
"header": [],
7868+
"body": {
7869+
"mode": "raw",
7870+
"raw": "{\n \n}",
7871+
"options": {
7872+
"raw": {
7873+
"language": "json"
7874+
}
7875+
}
7876+
},
7877+
"url": {
7878+
"raw": "https://{{$hostname}}/api/v1/services/ddns",
7879+
"protocol": "https",
7880+
"host": [
7881+
"{{$hostname}}"
7882+
],
7883+
"path": [
7884+
"api",
7885+
"v1",
7886+
"services",
7887+
"ddns"
7888+
]
7889+
},
7890+
"description": "Read configured dynamic DNS settings and statuses.<br><br>\n\n_Requires at least one of the following privileges:_ [`page-all`, `page-services-dynamicdnsclients`]"
7891+
},
7892+
"response": []
7893+
}
7894+
],
7895+
"description": "API endpoints that create, read, update, and delete Dynamic DNS configuration.",
7896+
"protocolProfileBehavior": {},
7897+
"_postman_isSubFolder": true
7898+
},
78577899
{
78587900
"name": "Read Services",
78597901
"protocolProfileBehavior": {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
// Copyright 2020 Jared Hendrickson
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
require_once("api/framework/APIEndpoint.inc");
17+
18+
class APIServicesDDNS extends APIEndpoint {
19+
public function __construct() {
20+
$this->url = "/api/v1/services/ddns";
21+
}
22+
23+
protected function get() {
24+
return (new APIServicesDDNSRead())->call();
25+
}
26+
}

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,5 @@ class APIEndpoint {
7171
http_response_code($resp["code"]);
7272
echo json_encode($resp) . PHP_EOL;
7373
exit();
74-
7574
}
76-
77-
7875
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,4 +1091,26 @@ function is_ip_subnet_or_alias($value) {
10911091
} else {
10921092
return false;
10931093
}
1094+
}
1095+
1096+
# Checks if an array is a associative array. Returns true if assoc, false if numeric, or null if not array
1097+
function is_assoc_array($array, $strict_seq=false) {
1098+
# Local variables
1099+
$i = 0;
1100+
1101+
# First check if our array is actually an array. Return null otherwise.
1102+
if (is_array($array)) {
1103+
# Loop through each array value and check it's key
1104+
foreach ($array as $key => $value) {
1105+
# Check if key is numeric
1106+
if (!is_numeric($key) or ($strict_seq and $i !== intval($key))) {
1107+
return true;
1108+
}
1109+
$i++;
1110+
}
1111+
} else {
1112+
return null;
1113+
}
1114+
1115+
return false;
10941116
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class APIFirewallAliasRead extends APIModel {
2525
}
2626

2727
public function action() {
28-
2928
// Check that we have a configuration
3029
if (!empty($this->config["aliases"]["alias"])) {
3130
$alias_array = $this->config["aliases"]["alias"];
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
// Copyright 2020 Jared Hendrickson
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
require_once("api/framework/APIModel.inc");
17+
require_once("api/framework/APIResponse.inc");
18+
19+
20+
class APIServicesDDNSRead extends APIModel {
21+
# Create our method constructor
22+
public function __construct() {
23+
parent::__construct();
24+
$this->privileges = ["page-all", "page-services-dynamicdnsclients"];
25+
}
26+
27+
public function action() {
28+
# Check that we have at least 1 DDNS configuration, otherwise return empty array
29+
if (!empty($this->config['dyndnses']['dyndns'])) {
30+
$this->validated_data = $this->config['dyndnses']['dyndns'];
31+
# Loop through each entry and check if it's address is current
32+
foreach ($this->validated_data as $id=>$dyndns_ent) {
33+
$this->validated_data[$id]["current"] = $this->__is_dyndns_current($dyndns_ent);
34+
}
35+
} else {
36+
$this->validated_data = [];
37+
}
38+
return APIResponse\get(0, $this->validated_data);
39+
}
40+
41+
private function __is_dyndns_current($ddns_ent) {
42+
global $dyndns_split_domain_types;
43+
# Determine our DDNS hostname
44+
if (in_array($ddns_ent['type'], $dyndns_split_domain_types)) {
45+
$host = $ddns_ent['host'] . "." . $ddns_ent['domainname'];
46+
} else {
47+
$host = $ddns_ent['host'];
48+
}
49+
# Set file paths
50+
$file = "/conf/dyndns_".$ddns_ent['interface'].$ddns_ent['type'].escapeshellarg($host).$ddns_ent['id'].".cache";
51+
$file_v6 = "/conf/dyndns_".$ddns_ent['interface'].$ddns_ent['type'].escapeshellarg($host).$ddns_ent['id']."_v6.cache";
52+
53+
# Check if either the IPv4 or IPv6 files exist, otherwise return false.
54+
if (file_exists($file)) {
55+
$ipaddr = dyndnsCheckIP($ddns_ent['interface']);
56+
$cached_ip_s = explode("|", file_get_contents($file));
57+
$cached_ip = $cached_ip_s[0];
58+
if ($ipaddr != $cached_ip) {
59+
return false;
60+
} else {
61+
return true;
62+
}
63+
} else if (file_exists($file_v6)) {
64+
$ipv6addr = get_interface_ipv6($ddns_ent['interface']);
65+
$cached_ipv6_s = explode("|", file_get_contents($file_v6));
66+
$cached_ipv6 = $cached_ipv6_s[0];
67+
if ($ipv6addr != $cached_ipv6) {
68+
return false;
69+
} else {
70+
return true;
71+
}
72+
} else {
73+
return false;
74+
}
75+
}
76+
}

pfSense-pkg-API/files/usr/local/www/api/documentation/index.php

Lines changed: 8 additions & 2 deletions
Large diffs are not rendered by default.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
import unit_test_framework
1616

17-
class APIUnitTestFirewallNAT(unit_test_framework.APIUnitTest):
18-
url = "/api/v1/firewall/nat"
17+
class APIUnitTestServicesDDNS(unit_test_framework.APIUnitTest):
18+
url = "/api/v1/services/ddns"
1919
get_payloads = [{}]
2020

21-
APIUnitTestFirewallNAT()
21+
APIUnitTestServicesDDNS()

0 commit comments

Comments
 (0)