diff --git a/src/etc/inc/dyndns.class b/src/etc/inc/dyndns.class index c9caac84e3..24cda33d7f 100644 --- a/src/etc/inc/dyndns.class +++ b/src/etc/inc/dyndns.class @@ -64,6 +64,7 @@ * - DreamHost DNS (www.dreamhost.com) * - ClouDNS (www.cloudns.net) * - GoDaddy (www.godaddy.com) + * - Azure DNS (azure.microsoft.com) * +----------------------------------------------------+ * Requirements: * - PHP version 4.0.2 or higher with the CURL Library and the PCRE Library @@ -123,6 +124,7 @@ * ClouDNS - Last Tested: 22 August 2017 * GoDaddy - Last Tested: 22 November 2017 * GoDaddy IPv6 - Last Tested: 22 November 2017 + * Azure DNS - Last Tested: 08 March 2018 * +====================================================+ * * @author E.Kristensen @@ -241,6 +243,14 @@ if (!$dnsDomain) $this->_error(5); if (!$dnsTTL) $this->_error(9); break; + case azure: + case azurev6: + if (!$dnsUser) $this->_error(3); + if (!$dnsPass) $this->_error(4); + if (!$dnsHost) $this->_error(5); + if (!$dnsZoneID) $this->_error(8); + if (!$dnsTTL) $this->_error(9); + break; case 'custom': if (!$dnsUpdateURL) $this->_error(7); break; @@ -260,6 +270,7 @@ case 'cloudflare-v6': case 'dreamhost-v6': case 'godaddy-v6': + case 'azurev6': $this->_useIPv6 = true; break; default: @@ -349,6 +360,8 @@ case 'hover': case 'godaddy': case 'godaddy-v6': + case 'azure': + case 'azurev6': $this->_update(); if ($this->_dnsDummyUpdateDone == true) { // If a dummy update was needed, then sleep a while and do the update again to put the proper address back. @@ -995,9 +1008,77 @@ curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); break; + case 'azurev6': + case 'azure': + $hostname = "{$this->_dnsHost}"; + $resourceid = trim($this->_dnsZoneID); + $app_id = $this->_dnsUser; + $client_secret = $this->_dnsPass; + $newip = $this->_dnsIP; + $newttl = $this->_dnsTTL; + // ensure resourceid starts with / and has no trailing / + $resourceid = '/' . trim($resourceid, '/'); + // extract subscription id from resource id + preg_match('/\\/subscriptions\\/(?[^\\/]*)/', $resourceid, $result); + $subscriptionid = isset($result['sid']) ? $result['sid'] : ''; + if (isset($result['sid'])) { + $subscriptionid = $result['sid']; + } else { + log_error("Azure subscription id not found in resource id"); + return false; + } + // find tenant id from subscription id + curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_NOBODY, 1); + $output = curl_exec($ch); + $pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?[^"]*)/i'; + preg_match($pattern, $output, $result); + if (isset($result['tid'])) { + $tenantid = $result['tid']; + } else { + log_error("Tenant ID not found"); + return false; + } + // get an bearer token + curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token"); + curl_setopt($ch, CURLOPT_POST, 1); + $body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $server_output = curl_exec($ch); + $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + preg_match("/\"access_token\":\"(?[^\"]*)\"/", $server_output, $result); + if (isset($result['tok'])) { + $bearertoken = $result['tok']; + } else { + log_error("no valid bearer token"); + return false; + } + // Update the DNS record + if ($this->_useIPv6) { + $url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01"; + $body = '{"properties":{"TTL":"' . $newttl . '", "AaaaRecords":[{"ipv6Address":"' . $newip . '"}]}}'; + } else { + $url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01"; + $body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}'; + } + $request_headers = array(); + $request_headers[] = 'Accept: application/json'; + $request_headers[] = 'Authorization: Bearer ' . $bearertoken; + $request_headers[] = 'Content-Type: application/json'; + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent); + curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers); + curl_setopt($ch, CURLOPT_POSTFIELDS, $body); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); + break; - default: - break; + default: + break; } if ($this->_dnsService != 'ods') { curl_setopt($ch, CURLOPT_HEADER, 1); @@ -1952,6 +2033,23 @@ default: break; } + case 'azure': + case 'azurev6': + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($http_code == 401) { + $status = $status_intro . $error_str . gettext("User Authorization Failed"); + } else if ($http_code == 201) { + $status = $status_intro . $success_str . gettext("IP Address Changed Successfully!"); + $successful_update = true; + } else if ($http_code == 200) { + $status = $status_intro . $success_str . gettext("IP Address Changed Successfully!"); + $successful_update = true; + } else { + $status = $status_intro . "(" . gettext("Unknown Response") . ")"; + log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code); + $this->_debug($data); + } + break; default: break; } diff --git a/src/etc/inc/services.inc b/src/etc/inc/services.inc index 60f3b827ad..0b50a08f01 100644 --- a/src/etc/inc/services.inc +++ b/src/etc/inc/services.inc @@ -24,8 +24,8 @@ */ -define('DYNDNS_PROVIDER_VALUES', 'all-inkl citynetwork cloudflare cloudflare-v6 cloudns custom custom-v6 dnsexit dnsimple dnsmadeeasy dnsomatic dreamhost dreamhost-v6 duiadns duiadns-v6 dyndns dyndns-custom dyndns-static dyns easydns eurodns freedns freedns-v6 glesys godaddy godaddy-v6 googledomains gratisdns he-net he-net-v6 he-net-tunnelbroker hover loopia namecheap noip noip-free ods opendns ovh-dynhost route53 route53-v6 selfhost spdyn spdyn-v6 zoneedit'); -define('DYNDNS_PROVIDER_DESCRIPTIONS', 'All-Inkl.com,City Network,Cloudflare,Cloudflare (v6),ClouDNS,Custom,Custom (v6),DNSexit,DNSimple,DNS Made Easy,DNS-O-Matic,DreamHost,Dreamhost (v6),DuiaDns.net,DuiaDns.net (v6),DynDNS (dynamic),DynDNS (custom),DynDNS (static),DyNS,easyDNS,Euro Dns,freeDNS,freeDNS (v6),GleSYS,GoDaddy,GoDaddy (v6),Google Domains,GratisDNS,HE.net,HE.net (v6),HE.net Tunnelbroker,Hover,Loopia,Namecheap,No-IP,No-IP (free),ODS.org,OpenDNS,OVH DynHOST,Route 53,Route 53 (v6),SelfHost,SPDYN,SPDYN (v6),ZoneEdit'); +define('DYNDNS_PROVIDER_VALUES', 'all-inkl azure azurev6 citynetwork cloudflare cloudflare-v6 cloudns custom custom-v6 dnsexit dnsimple dnsmadeeasy dnsomatic dreamhost dreamhost-v6 duiadns duiadns-v6 dyndns dyndns-custom dyndns-static dyns easydns eurodns freedns freedns-v6 glesys godaddy godaddy-v6 googledomains gratisdns he-net he-net-v6 he-net-tunnelbroker hover loopia namecheap noip noip-free ods opendns ovh-dynhost route53 route53-v6 selfhost spdyn spdyn-v6 zoneedit'); +define('DYNDNS_PROVIDER_DESCRIPTIONS', 'All-Inkl.com,Azure DNS,Azure DNS (v6),City Network,Cloudflare,Cloudflare (v6),ClouDNS,Custom,Custom (v6),DNSexit,DNSimple,DNS Made Easy,DNS-O-Matic,DreamHost,Dreamhost (v6),DuiaDns.net,DuiaDns.net (v6),DynDNS (dynamic),DynDNS (custom),DynDNS (static),DyNS,easyDNS,Euro Dns,freeDNS,freeDNS (v6),GleSYS,GoDaddy,GoDaddy (v6),Google Domains,GratisDNS,HE.net,HE.net (v6),HE.net Tunnelbroker,Hover,Loopia,Namecheap,No-IP,No-IP (free),ODS.org,OpenDNS,OVH DynHOST,Route 53,Route 53 (v6),SelfHost,SPDYN,SPDYN (v6),ZoneEdit'); /* implement ipv6 route advertising daemon */ function services_radvd_configure($blacklist = array()) { diff --git a/src/usr/local/www/services_dyndns_edit.php b/src/usr/local/www/services_dyndns_edit.php index 6fc98228b7..e9c3a651a5 100644 --- a/src/usr/local/www/services_dyndns_edit.php +++ b/src/usr/local/www/services_dyndns_edit.php @@ -357,6 +357,7 @@ $section->addInput(new Form_Input( 'text', $pconfig['username'] ))->setHelp('Username is required for all types except Namecheap, FreeDNS and Custom Entries.%1$s' . + 'Azure: Enter your Azure AD application ID%1$s' . 'DNS Made Easy: Dynamic DNS ID%1$s' . 'Route 53: Enter the Access Key ID.%1$s' . 'GleSYS: Enter the API user.%1$s' . @@ -370,6 +371,7 @@ $section->addPassword(new Form_Input( 'password', $pconfig['password'] ))->setHelp('FreeDNS (freedns.afraid.org): Enter the "Authentication Token" provided by FreeDNS.%1$s' . + 'Azure: client secret of the AD application%1$s' . 'DNS Made Easy: Dynamic DNS Password%1$s' . 'Route 53: Enter the Secret Access Key.%1$s' . 'GleSYS: Enter the API key.%1$s' . @@ -384,6 +386,7 @@ $section->addInput(new Form_Input( 'text', $pconfig['zoneid'] ))->setHelp('Route53: Enter AWS Zone ID.%1$s' . + 'Azure: Enter the resource id of the of the DNS Zone%1$s' . 'DNSimple: Enter the Record ID of record to update.', '
'); $section->addInput(new Form_Input( @@ -553,6 +556,21 @@ events.push(function() { hideInput('zoneid', true); hideInput('ttl', false); break; + case "azurev6": + case "azure": + hideGroupInput('domainname', true); + hideInput('resultmatch', true); + hideInput('updateurl', true); + hideInput('requestif', true); + hideCheckbox('curl_ipresolve_v4', true); + hideCheckbox('curl_ssl_verifypeer', true); + hideInput('host', false); + hideInput('mx', true); + hideCheckbox('wildcard', true); + hideCheckbox('proxied', true); + hideInput('zoneid', false); + hideInput('ttl', false); + break; default: hideGroupInput('domainname', true); hideInput('resultmatch', true);