Updated due to porkbun change, moving apis from porkbun.com to api.porkbun.com
--- a/src/etc/inc/dyndns.class 2023-10-13 16:00:47.738058000 -0400
+++ b/src/etc/inc/dyndns.class 2023-10-13 16:39:40.614111000 -0400
@@ -71,6 +71,7 @@
* - Namecheap (namecheap.com)
* - No-IP (no-ip.com)
* - OpenDNS (opendns.com)
+ * - Porkbun (porkbun.com)
* - SelfHost (selfhost.de)
* - SPDYN (spdyn.de)
* - SPDYN IPv6 (spdyn.de)
@@ -141,6 +142,7 @@
* ODS - Last Tested: 02 August 2005
* OpenDNS - Last Tested: 4 August 2008
* OVH DynHOST - Last Tested: NEVER
+ * Porkbun - Last Tested: 13 October 2023
* SelfHost - Last Tested: 26 December 2011
* SPDYN - Last Tested: 02 July 2016
* SPDYN IPv6 - Last Tested: 02 July 2016
@@ -309,6 +311,13 @@
if (!$dnsZoneID) $this->_error(8);
if (!$dnsTTL) $this->_error(9);
break;
+ case 'porkbun':
+ case 'porkbun-v6':
+ if (!$dnsUser) $this->_error(3);
+ if (!$dnsPass) $this->_error(4);
+ if (!$dnsHost) $this->_error(5);
+ if (!$dnsDomain) $this->_error(5);
+ break;
default:
if (!$dnsUser) $this->_error(3);
if (!$dnsPass) $this->_error(4);
@@ -337,6 +346,7 @@
case 'name.com-v6':
case 'noip-free-v6':
case 'noip-v6':
+ case 'porkbun-v6':
case 'route53-v6':
case 'spdyn-v6':
case 'yandex-v6':
@@ -464,6 +474,8 @@
case 'ods':
case 'opendns':
case 'ovh-dynhost':
+ case 'porkbun':
+ case 'porkbun-v6':
case 'route53':
case 'route53-v6':
case 'selfhost':
@@ -948,6 +960,73 @@
$server = "https://api.nic.ru/dyndns/update?hostname={$this->_dnsHost}&{$iptype}={$this->_dnsIP}";
curl_setopt($ch, CURLOPT_URL, $server);
break;
+ case 'porkbun':
+ case 'porkbun-v6':
+ // API documentation: https://porkbun.com/api/json/v3/documentation
+ $porkbun_api = "https://api.porkbun.com/api/json/v3/dns/retrieve/{$this->_dnsDomain}";
+ $record_type = $this->_useIPv6 ? "AAAA" : "A";
+ // Check if a record already exists for this host.
+ $post_data['apikey'] = $this->_dnsUser;
+ $post_data['secretapikey'] = $this->_dnsPass;
+ curl_setopt($ch, CURLOPT_URL, "{$porkbun_api}");
+ curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
+ curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
+ $response = json_decode(curl_exec($ch), true);
+ $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ if ($http_code != "200") {
+ log_error(gettext("Error message: ") . $response);
+ return false;
+ }
+ if (!is_array($response["records"])) {
+ log_error(gettext("Unexpected response: ") . $response);
+ return false;
+ }
+ foreach($response["records"] as $record) {
+ if (($this->_dnsHost == "@" || $this->_dnsHost == "") &&
+ ($record["name"] == $this->_dnsDomain) &&
+ ($record["type"] == $record_type)) {
+ $record_id = $record["id"];
+ break;
+ }
+ else if (($record["name"] == "{$this->_dnsHost}.{$this->_dnsDomain}") &&
+ ($record["type"] == $record_type)) {
+ $record_id = $record["id"];
+ break;
+ }
+ }
+ // No record exists for this host, add one.
+ if (!$record_id)
+ {
+ $porkbun_api = "https://api.porkbun.com/api/json/v3/dns/create/{$this->_dnsDomain}";
+ if ($this->_dnsHost == "@" || $this->_dnsHost == "")
+ $post_data['name'] = "";
+ else
+ $post_data['name'] = $this->_dnsHost;
+ } else {
+ $porkbun_api = "https://api.porkbun.com/api/json/v3/dns/edit/{$this->_dnsDomain}/{$record_id}";
+ $post_data['name'] = $this->_dnsHost;
+ }
+ $post_data['type'] = $record_type;
+ // Porkbun doesn't allow you to "update" an existing record with the same IP
+ if (($record_id) &&
+ ($this->_forceUpdateNeeded == true) &&
+ ($this->_dnsDummyUpdateDone == false)) {
+ $post_data['content'] = $this->_useIPv6 ? "fd00:d::1" : "127.0.0.1";
+ $this->_dnsDummyUpdateDone = true;
+ $log_message = 'Dynamic DNS %1$s (%2$s): ';
+ $log_message .= 'Performing forced update. ';
+ $log_message .= 'IP temporarily set to %3$s';
+ log_error(sprintf(gettext($log_message), $this->_dnsService, $this->_dnsHost, $post_data['content']));
+ } else {
+ $post_data['content'] = $this->_dnsIP;
+ }
+ if (intval($this->_dnsTTL)) $post_data['ttl'] = $this->_dnsTTL;
+ curl_setopt($ch, CURLOPT_URL, "{$porkbun_api}");
+ curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
+ curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
+ break;
case 'yandex':
case 'yandex-v6':
// https://yandex.com/dev/connect/directory/api/concepts/domains/dns-records-via-pdd.html
@@ -2927,6 +3006,16 @@
log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
$this->_debug($data);
break;
+ }
+ break;
+ case 'porkbun':
+ case 'porkbun-v6':
+ $result = json_decode($data, true);
+ if ($result['status'] == 'SUCCESS') {
+ $status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
+ $successful_update = true;
+ } else {
+ log_error($status_intro . " ( " . gettext("Error message: ") . $result['status'] . " )");
}
break;
default:
--- a/src/etc/inc/globals.inc 2023-10-13 16:01:01.023284000 -0400
+++ b/src/etc/inc/globals.inc 2023-10-13 16:12:09.816744000 -0400
@@ -222,7 +222,7 @@
"descr" => 'Default Check IP Service'
);
-$dyndns_split_domain_types = array("namecheap", "cloudflare", "cloudflare-v6", "gratisdns", "cloudns", "godaddy", "godaddy-v6", "linode", "linode-v6");
+$dyndns_split_domain_types = array("namecheap", "cloudflare", "cloudflare-v6", "gratisdns", "cloudns", "godaddy", "godaddy-v6", "linode", "linode-v6", "porkbun", "porkbun-v6");
/* pf tokens from FreeBSD source sbin/pfctl/parse.y (plus our custom entries at the end)*/
global $pf_reserved_keywords;
--- a/src/etc/inc/services.inc 2023-10-13 16:01:14.103456000 -0400
+++ b/src/etc/inc/services.inc 2023-10-13 16:13:41.295138000 -0400
@@ -26,8 +26,8 @@
*/
-define('DYNDNS_PROVIDER_VALUES', 'all-inkl azure azurev6 citynetwork cloudflare cloudflare-v6 cloudns custom custom-v6 desec desec-v6 digitalocean digitalocean-v6 dnsexit dnsimple dnsimple-v6 dnsmadeeasy dnsomatic domeneshop domeneshop-v6 dreamhost dreamhost-v6 duiadns duiadns-v6 dyfi dyndns dyndns-custom dyndns-static dyns dynv6 dynv6-v6 easydns easydns-v6 eurodns freedns freedns-v6 freedns2 freedns2-v6 glesys gandi-livedns gandi-livedns-v6 godaddy godaddy-v6 googledomains gratisdns he-net he-net-v6 he-net-tunnelbroker hover linode linode-v6 loopia mythicbeasts mythicbeasts-v6 name.com name.com-v6 namecheap nicru nicru-v6 noip noip-v6 noip-free noip-free-v6 onecom onecom-v6 ods opendns ovh-dynhost route53 route53-v6 selfhost spdyn spdyn-v6 strato yandex yandex-v6 zoneedit');
-define('DYNDNS_PROVIDER_DESCRIPTIONS', 'All-Inkl.com,Azure DNS,Azure DNS (v6),City Network,Cloudflare,Cloudflare (v6),ClouDNS,Custom,Custom (v6),deSEC,deSEC (v6),DigitalOcean,DigitalOcean (v6),DNSexit,DNSimple,DNSimple (v6),DNS Made Easy,DNS-O-Matic,Domeneshop,Domeneshop (v6),DreamHost,Dreamhost (v6),DuiaDns.net,DuiaDns.net (v6),DY.fi,DynDNS (dynamic),DynDNS (custom),DynDNS (static),DyNS,Dynv6,Dynv6 (v6),easyDNS,easyDNS (v6),Euro Dns,freeDNS,freeDNS (v6),freeDNS API Version 2, freeDNS API Version 2 (v6),GleSYS,Gandi Live DNS,Gandi Live DNS (v6),GoDaddy,GoDaddy (v6),Google Domains,GratisDNS,HE.net,HE.net (v6),HE.net Tunnelbroker,Hover,Linode,Linode (v6),Loopia,Mythic Beasts,Mythic Beasts (v6),Name.com,Name.com (v6),Namecheap,NIC.RU,NIC.RU (v6),No-IP,No-IP (v6),No-IP (free),No-IP (free-v6),One.com,One.com (v6),ODS.org,OpenDNS,OVH DynHOST,Route 53,Route 53 (v6),SelfHost,SPDYN,SPDYN (v6),Strato,Yandex,Yandex (v6),ZoneEdit');
+define('DYNDNS_PROVIDER_VALUES', 'all-inkl azure azurev6 citynetwork cloudflare cloudflare-v6 cloudns custom custom-v6 desec desec-v6 digitalocean digitalocean-v6 dnsexit dnsimple dnsimple-v6 dnsmadeeasy dnsomatic domeneshop domeneshop-v6 dreamhost dreamhost-v6 duiadns duiadns-v6 dyfi dyndns dyndns-custom dyndns-static dyns dynv6 dynv6-v6 easydns easydns-v6 eurodns freedns freedns-v6 freedns2 freedns2-v6 glesys gandi-livedns gandi-livedns-v6 godaddy godaddy-v6 googledomains gratisdns he-net he-net-v6 he-net-tunnelbroker hover linode linode-v6 loopia mythicbeasts mythicbeasts-v6 name.com name.com-v6 namecheap nicru nicru-v6 noip noip-v6 noip-free noip-free-v6 onecom onecom-v6 ods opendns ovh-dynhost route53 route53-v6 selfhost spdyn spdyn-v6 strato yandex yandex-v6 zoneedit porkbun porkbun-v6');
+define('DYNDNS_PROVIDER_DESCRIPTIONS', 'All-Inkl.com,Azure DNS,Azure DNS (v6),City Network,Cloudflare,Cloudflare (v6),ClouDNS,Custom,Custom (v6),deSEC,deSEC (v6),DigitalOcean,DigitalOcean (v6),DNSexit,DNSimple,DNSimple (v6),DNS Made Easy,DNS-O-Matic,Domeneshop,Domeneshop (v6),DreamHost,Dreamhost (v6),DuiaDns.net,DuiaDns.net (v6),DY.fi,DynDNS (dynamic),DynDNS (custom),DynDNS (static),DyNS,Dynv6,Dynv6 (v6),easyDNS,easyDNS (v6),Euro Dns,freeDNS,freeDNS (v6),freeDNS API Version 2, freeDNS API Version 2 (v6),GleSYS,Gandi Live DNS,Gandi Live DNS (v6),GoDaddy,GoDaddy (v6),Google Domains,GratisDNS,HE.net,HE.net (v6),HE.net Tunnelbroker,Hover,Linode,Linode (v6),Loopia,Mythic Beasts,Mythic Beasts (v6),Name.com,Name.com (v6),Namecheap,NIC.RU,NIC.RU (v6),No-IP,No-IP (v6),No-IP (free),No-IP (free-v6),One.com,One.com (v6),ODS.org,OpenDNS,OVH DynHOST,Route 53,Route 53 (v6),SelfHost,SPDYN,SPDYN (v6),Strato,Yandex,Yandex (v6),ZoneEdit,Porkbun,Porkbun (v6)');
/* implement ipv6 route advertising daemon */
function services_radvd_configure($blacklist = array()) {
--- a/src/usr/local/www/services_dyndns_edit.php 2023-10-13 16:01:32.710407000 -0400
+++ b/src/usr/local/www/services_dyndns_edit.php 2023-10-13 16:20:58.885037000 -0400
@@ -154,7 +154,7 @@
} elseif (($pconfig['type'] == "cloudflare") || ($pconfig['type'] == "cloudflare-v6")) {
$host_to_check = $_POST['host'] == '@' ? $_POST['domainname'] : ( $_POST['host'] . '.' . $_POST['domainname'] );
$allow_wildcard = true;
- } elseif (($pconfig['type'] == "linode") || ($pconfig['type'] == "linode-v6") || ($pconfig['type'] == "gandi-livedns") || ($pconfig['type'] == "gandi-livedns-v6") || ($pconfig['type'] == "yandex") || ($pconfig['type'] == "yandex-v6")) {
+ } elseif (($pconfig['type'] == "linode") || ($pconfig['type'] == "linode-v6") || ($pconfig['type'] == "gandi-livedns") || ($pconfig['type'] == "gandi-livedns-v6") || ($pconfig['type'] == "yandex") || ($pconfig['type'] == "yandex-v6") || ($pconfig['type'] == "porkbun") || ($pconfig['type'] == "porkbun-v6")) {
$host_to_check = $_POST['host'] == '@' ? $_POST['domainname'] : ( $_POST['host'] . '.' . $_POST['domainname'] );
$allow_wildcard = true;
} elseif (($pconfig['type'] == "route53") || ($pconfig['type'] == "route53-v6")) {
@@ -367,8 +367,8 @@
'he.net tunnelbroker: Enter the tunnel ID.%1$s' .
'GleSYS: Enter the record ID.%1$s' .
'DNSimple: Enter only the domain name.%1$s' .
- 'Name.com, Namecheap, Cloudflare, GratisDNS, Hover, ClouDNS, GoDaddy, Linode, DigitalOcean: Enter the hostname and the domain separately, with the domain being the domain or subdomain zone being handled by the provider.%1$s' .
- 'Cloudflare, Linode: Enter @ as the hostname to indicate an empty field.%1$s' .
+ 'Name.com, Namecheap, Cloudflare, GratisDNS, Hover, ClouDNS, GoDaddy, Linode, DigitalOcean, Porkbun: Enter the hostname and the domain separately, with the domain being the domain or subdomain zone being handled by the provider.%1$s' .
+ 'Cloudflare, Linode, Porkbun: Enter @ as the hostname to indicate an empty field.%1$s' .
'deSEC: Enter the FQDN.', '<br />');
$section->add($group);
@@ -434,6 +434,7 @@
'Godaddy: Enter the API key.%1$s' .
'Cloudflare: Enter email for Global API Key or (optionally) Zone ID for API token.%1$s' .
'NoIP: For group authentication, replace semicolon (:) with pound-key (#).%1$s' .
+ 'Porkbun: Enter the API key.%1$s' .
'For Custom Entries, Username and Password represent HTTP Authentication username and passwords.', '<br />');
$section->addPassword(new Form_Input(
@@ -454,6 +455,7 @@
'DNSimple: Enter the API token.%1$s' .
'Linode: Enter the Personal Access Token.%1$s' .
'Name.com: Enter the API token.%1$s' .
+ 'Porkbun: Enter the API secret.%1$s' .
'Yandex: Yandex PDD Token.%1$s' .
'Cloudflare: Enter the Global API Key or API token with DNS edit permisson on the provided zone.%1$s' .
'deSEC: Enter the API token.', '<br />');
@@ -633,6 +635,8 @@
case "name.com-v6":
case "onecom":
case "onecom-v6":
+ case "porkbun":
+ case "porkbun-v6":
hideGroupInput('domainname', false);
hideInput('mx', true);
hideCheckbox('wildcard', true);