What tool do I use to make a patch file?
-
I've manually patched files in 22.05 for a fix that was added to 23.xx to add Porkbun DDNS support to pfSense. I have the original files and the modified ones that are working, what tools should I use to generate a patch that I can import on other devices running the same version?
-
@bruor by no means a developer, but my understanding was diff was used to generate the differences in the file - and then just use patch to apply that diff.
diff and patch are both available on pfsense.
But wouldn't it be better to submit whatever it is you changed so it can get applied directly to pfsense, say in the next version. or they could add it directly to the patch manager, for easy deployment by anyone else that might be using the same DDNS service.
-
@johnpoz I'm backporting https://github.com/pfsense/pfsense/pull/4639 which works with 23.05+ to 22.05 for use on a few sg-1000 devices I still have running.
I've been able to make a unified diff of the changes, not sure how to get this into a PR, if I even can against an older version...
--- dyndns.class.bak 2023-10-13 16:00:47.738058000 -0400 +++ 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://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://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://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: --- globals.inc.bak 2023-10-13 16:01:01.023284000 -0400 +++ 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; --- services.inc.bak 2023-10-13 16:01:14.103456000 -0400 +++ 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()) { --- services_dyndns_edit.php.bak 2023-10-13 16:01:32.710407000 -0400 +++ 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);
-
Unlikely that would ever be pulled into 22.05 at this point.
But if you have created a diff you should be able to apply it using the System Patches package.
-
For anyone else who might have a use for it, this imports cleanly.
--- 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://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://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://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);
-
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);