Project

General

Profile

« Previous | Next » 

Revision d9d91d5d

Added by Nita Vesa almost 2 years ago

Add dynamic DNS support for Porkbun DNS, closes #14402

Signed-off-by: Nita Vesa <>

View differences:

src/etc/inc/dyndns.class
71 71
	 *    - Namecheap (namecheap.com)
72 72
	 *    - No-IP (no-ip.com)
73 73
	 *    - OpenDNS (opendns.com)
74
	 *    - Porkbun (porkbun.com)
74 75
	 *    - SelfHost (selfhost.de)
75 76
	 *    - SPDYN (spdyn.de)
76 77
	 *    - SPDYN IPv6 (spdyn.de)
......
141 142
	 *  ODS             - Last Tested: 02 August 2005
142 143
	 *  OpenDNS         - Last Tested: 4 August 2008
143 144
	 *  OVH DynHOST     - Last Tested: NEVER
145
	 *  Porkbun         - Last Tested: 26 June 2023
144 146
	 *  SelfHost        - Last Tested: 26 December 2011
145 147
	 *  SPDYN           - Last Tested: 02 July 2016
146 148
	 *  SPDYN IPv6      - Last Tested: 02 July 2016
......
309 311
				if (!$dnsZoneID) $this->_error(8);
310 312
				if (!$dnsTTL) $this->_error(9);
311 313
				break;
314
			case 'porkbun':
315
			case 'porkbun-v6':
316
				if (!$dnsUser) $this->_error(3);
317
				if (!$dnsPass) $this->_error(4);
318
				if (!$dnsHost) $this->_error(5);
319
				if (!$dnsDomain) $this->_error(5);
320
				break;
312 321
			default:
313 322
				if (!$dnsUser) $this->_error(3);
314 323
				if (!$dnsPass) $this->_error(4);
......
337 346
				case 'name.com-v6':
338 347
				case 'noip-free-v6':
339 348
				case 'noip-v6':
349
				case 'porkbun-v6':
340 350
				case 'route53-v6':
341 351
				case 'spdyn-v6':
342 352
				case 'yandex-v6':
......
464 474
					case 'ods':
465 475
					case 'opendns':
466 476
					case 'ovh-dynhost':
477
					case 'porkbun':
478
					case 'porkbun-v6':
467 479
					case 'route53':
468 480
					case 'route53-v6':
469 481
					case 'selfhost':
......
949 961
					$server = "https://api.nic.ru/dyndns/update?hostname={$this->_dnsHost}&{$iptype}={$this->_dnsIP}";
950 962
					curl_setopt($ch, CURLOPT_URL, $server);
951 963
					break;
964
				case 'porkbun':
965
				case 'porkbun-v6':
966
					// API documentation: https://porkbun.com/api/json/v3/documentation
967
					$porkbun_api = "https://porkbun.com/api/json/v3/dns/retrieve/{$this->_dnsDomain}";
968
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
969
					// Check if a record already exists for this host.
970
					$post_data['apikey'] = $this->_dnsUser;
971
					$post_data['secretapikey'] = $this->_dnsPass;
972
					curl_setopt($ch, CURLOPT_URL, "{$porkbun_api}");
973
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
974
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
975
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
976
					$response = json_decode(curl_exec($ch), true);
977
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
978
					if ($http_code != "200") {
979
						log_error(gettext("Error message: ") . $response);
980
						return false;
981
					}
982
					if (!is_array($response["records"])) {
983
						log_error(gettext("Unexpected response: ") . $response);
984
						return false;
985
					}
986
					foreach($response["records"] as $record) {
987
						if (($this->_dnsHost == "@" || $this->_dnsHost == "") &&
988
							($record["name"] == $this->_dnsDomain) &&
989
							($record["type"] == $record_type)) {
990
								$record_id = $record["id"];
991
								break;
992
						}
993
						else if (($record["name"] == "{$this->_dnsHost}.{$this->_dnsDomain}") &&
994
							($record["type"] == $record_type)) {
995
								$record_id = $record["id"];
996
								break;
997
						}
998
					}
999
					// No record exists for this host, add one.
1000
					if (!$record_id)
1001
					{
1002
						$porkbun_api = "https://porkbun.com/api/json/v3/dns/create/{$this->_dnsDomain}";
1003
						if ($this->_dnsHost == "@" || $this->_dnsHost == "")
1004
							$post_data['name'] = "";
1005
						else
1006
							$post_data['name'] = $this->_dnsHost;
1007
					} else {
1008
						$porkbun_api = "https://porkbun.com/api/json/v3/dns/edit/{$this->_dnsDomain}/{$record_id}";
1009
						$post_data['name'] = $this->_dnsHost;
1010
					}
1011
					$post_data['type'] = $record_type;
1012
					// Porkbun doesn't allow you to "update" an existing record with the same IP
1013
					if (($record_id) &&
1014
					    ($this->_forceUpdateNeeded == true) &&
1015
					    ($this->_dnsDummyUpdateDone == false)) {
1016
						$post_data['content'] = $this->_useIPv6 ? "fd00:d::1" : "127.0.0.1";
1017
						$this->_dnsDummyUpdateDone = true;
1018
						$log_message = 'Dynamic DNS %1$s (%2$s): ';
1019
						$log_message .= 'Performing forced update. ';
1020
						$log_message .= 'IP temporarily set to %3$s';
1021
						log_error(sprintf(gettext($log_message), $this->_dnsService, $this->_dnsHost, $post_data['content']));
1022
					} else {
1023
						$post_data['content'] = $this->_dnsIP;
1024
					}
1025
					if (intval($this->_dnsTTL)) $post_data['ttl'] = $this->_dnsTTL;
1026
					curl_setopt($ch, CURLOPT_URL, "{$porkbun_api}");
1027
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
1028
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
1029
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
1030
					break;
952 1031
				case 'yandex':
953 1032
				case 'yandex-v6':
954 1033
					// https://yandex.com/dev/connect/directory/api/concepts/domains/dns-records-via-pdd.html
......
2975 3054
							break;
2976 3055
					}
2977 3056
					break;
3057
				case 'porkbun':
3058
				case 'porkbun-v6':
3059
					$result = json_decode($data, true);
3060
					if ($result['status'] == 'SUCCESS') {
3061
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
3062
						$successful_update = true;
3063
					} else {
3064
						log_error($status_intro . " ( " . gettext("Error message: ") . $result['status'] . " )");
3065
					}
3066
					break;
2978 3067
				default:
2979 3068
					break;
2980 3069
			}
src/etc/inc/globals.inc
218 218
];
219 219

  
220 220
global $dyndns_split_domain_types;
221
$dyndns_split_domain_types = ['namecheap', 'cloudflare', 'cloudflare-v6', 'gratisdns', 'cloudns', 'godaddy', 'godaddy-v6', 'linode', 'linode-v6'];
221
$dyndns_split_domain_types = ['namecheap', 'cloudflare', 'cloudflare-v6', 'gratisdns', 'cloudns', 'godaddy', 'godaddy-v6', 'linode', 'linode-v6', 'porkbun', 'porkbun-v6'];
222 222

  
223 223
// pf tokens from FreeBSD source /sbin/pfctl/parse.y
224 224
global $pf_reserved_keywords;
src/etc/inc/services.inc
26 26
 */
27 27

  
28 28

  
29
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');
30
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');
29
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');
30
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)');
31 31

  
32 32
/* implement ipv6 route advertising daemon */
33 33
function services_radvd_configure($blacklist = array()) {
src/usr/local/www/services_dyndns_edit.php
155 155
		} elseif (($pconfig['type'] == "cloudflare") || ($pconfig['type'] == "cloudflare-v6")) {
156 156
			$host_to_check = $_POST['host'] == '@' ? $_POST['domainname'] : ( $_POST['host'] . '.' . $_POST['domainname'] );
157 157
			$allow_wildcard = true;
158
		} elseif (($pconfig['type'] == "linode") || ($pconfig['type'] == "linode-v6") || ($pconfig['type'] == "gandi-livedns") || ($pconfig['type'] == "gandi-livedns-v6") || ($pconfig['type'] == "yandex") || ($pconfig['type'] == "yandex-v6")) {
158
		} 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")) {
159 159
			$host_to_check = $_POST['host'] == '@' ? $_POST['domainname'] : ( $_POST['host'] . '.' . $_POST['domainname'] );
160 160
			$allow_wildcard = true;
161 161
		} elseif (($pconfig['type'] == "route53") || ($pconfig['type'] == "route53-v6")) {
......
364 364
));
365 365

  
366 366
$group->setHelp('Enter the complete fully qualified domain name. Example: myhost.dyndns.org%1$s' .
367
				'Cloudflare, Linode: Enter @ as the hostname to indicate an empty field.%1$s' .
367
				'Cloudflare, Linode, Porkbun: Enter @ as the hostname to indicate an empty field.%1$s' .
368 368
				'deSEC: Enter the FQDN.%1$s' .
369 369
				'DNSimple: Enter only the domain name.%1$s' .
370 370
				'DNS Made Easy: Dynamic DNS ID (NOT hostname)%1$s' .
371 371
				'GleSYS: Enter the record ID.%1$s' .
372 372
				'he.net tunnelbroker: Enter the tunnel ID.%1$s' .
373
				'Cloudflare, ClouDNS, DigitalOcean, GoDaddy, GratisDNS, Hover, Linode, Name.com, Namecheap: Enter the hostname and domain name separately.
373
				'Cloudflare, ClouDNS, DigitalOcean, GoDaddy, GratisDNS, Hover, Linode, Name.com, Namecheap, Porkbun: Enter the hostname and domain name separately.
374 374
					The domain name is the domain or subdomain zone being handled by the provider.', '<br />');
375 375

  
376 376
$section->add($group);
......
436 436
			'GleSYS: Enter the API user.%1$s' .
437 437
			'Godaddy: Enter the API key.%1$s' .
438 438
			'NoIP: For group authentication, replace semicolon (:) with pound-key (#).%1$s' .
439
			'Porkbun: Enter the API key.%1$s' .
439 440
			'Route 53: Enter the Access Key ID.', '<br />');
440 441

  
441 442
$section->addPassword(new Form_Input(
......
458 459
			'GoDaddy: Enter the API secret.%1$s' .
459 460
			'Linode: Enter the Personal Access Token.%1$s' .
460 461
			'Name.com: Enter the API token.%1$s' .
462
			'Porkbun: Enter the API secret.%1$s' .
461 463
			'Route 53: Enter the Secret Access Key.%1$s' .
462 464
			'Yandex: Yandex PDD Token.', '<br />');
463 465

  
......
636 638
			case "name.com-v6":
637 639
			case "onecom":
638 640
			case "onecom-v6":
641
			case "porkbun":
642
			case "porkbun-v6":
639 643
				hideGroupInput('domainname', false);
640 644
				hideInput('mx', true);
641 645
				hideCheckbox('wildcard', true);

Also available in: Unified diff