Project

General

Profile

Download (107 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * dyndns.class
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2021 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 * http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23

    
24
	/*
25
	 * PHP.updateDNS (pfSense version)
26
	 *
27
	 * +====================================================+
28
	 *  Services Supported:
29
	 *    - DynDns (dyndns.org) [dynamic, static, custom]
30
	 *    - No-IP (no-ip.com)
31
	 *    - EasyDNS (easydns.com)
32
	 *    - EasyDNS IPv6 (easydns.com)
33
	 *    - DHS (www.dhs.org)
34
	 *    - HN (hn.org) -- incomplete checking!
35
	 *    - DynS (dyns.org)
36
	 *    - ZoneEdit (zoneedit.com)
37
	 *    - FreeDNS API v1 (freedns.afraid.org)
38
	 *    - FreeDNS IPv6 API v1 (freedns.afraid.org)
39
	 *    - FreeDNS API v2 (freedns.afraid.org)
40
	 *    - FreeDNS IPv6 API v2 (freedns.afraid.org)
41
	 *    - Loopia (loopia.se)
42
	 *    - StaticCling (staticcling.org)
43
	 *    - DNSexit (dnsexit.com)
44
	 *    - OpenDNS (opendns.com)
45
	 *    - Namecheap (namecheap.com)
46
	 *    - HE.net (dns.he.net)
47
	 *    - HE.net IPv6 (dns.he.net)
48
	 *    - HE.net Tunnelbroker IP update (ipv4.tunnelbroker.net)
49
	 *    - SelfHost (selfhost.de)
50
	 *    - Amazon Route 53 (aws.amazon.com)
51
	 *    - DNS-O-Matic (dnsomatic.com)
52
	 *    - Custom DDNS (any URL)
53
	 *    - Custom DDNS IPv6 (any URL)
54
	 *    - Cloudflare (www.cloudflare.com)
55
	 *    - Cloudflare IPv6 (www.cloudflare.com)
56
	 *    - Eurodns (eurodns.com)
57
	 *    - Gandi LiveDNS (www.gandi.net)
58
	 *    - GratisDNS (gratisdns.dk)
59
	 *    - City Network (citynetwork.se)
60
	 *    - GleSYS (glesys.com)
61
	 *    - DNSimple (dnsimple.com)
62
	 *    - Google Domains (domains.google.com)
63
	 *    - DNS Made Easy (www.dnsmadeeasy.com)
64
	 *    - SPDYN (spdyn.de)
65
	 *    - SPDYN IPv6 (spdyn.de)
66
	 *    - All-Inkl (all-inkl.com)
67
	 *    - DuiaDNS (www.duiadns.net)
68
	 *    - DuiaDNS IPv6 (www.duiadns.net)
69
	 *    - Hover (www.hover.com)
70
	 *    - DreamHost DNS (www.dreamhost.com)
71
	 *    - ClouDNS (www.cloudns.net)
72
	 *    - GoDaddy (www.godaddy.com)
73
	 *    - Azure DNS (azure.microsoft.com)
74
	 *    - Dynv6 (www.dynv6.com)
75
	 * +----------------------------------------------------+
76
	 *  Requirements:
77
	 *    - PHP version 4.0.2 or higher with the CURL Library and the PCRE Library
78
	 * +----------------------------------------------------+
79
	 *  Public Functions
80
	 *    - updatedns()
81
	 *
82
	 *  Private Functions
83
	 *    - _update()
84
	 *    - _checkStatus()
85
	 *    - _error()
86
	 *    - _detectChange()
87
	 *    - _debug()
88
	 *    - _checkIP()
89
	 * +----------------------------------------------------+
90
	 *  DynDNS Dynamic  - Last Tested: 12 July 2005
91
	 *  DynDNS Static   - Last Tested: NEVER
92
	 *  DynDNS Custom   - Last Tested: NEVER
93
	 *  No-IP           - Last Tested: 20 July 2008
94
	 *  HN.org          - Last Tested: 12 July 2005
95
	 *  EasyDNS         - Last Tested: 20 July 2008
96
	 *  DHS             - Last Tested: 12 July 2005
97
	 *  ZoneEdit        - Last Tested: NEVER
98
	 *  Dyns            - Last Tested: NEVER
99
	 *  ODS             - Last Tested: 02 August 2005
100
	 *  FreeDNS         - Last Tested: 01 May 2016
101
	 *  FreeDNS IPv6    - Last Tested: 01 May 2016
102
	 *  FreeDNS v2      - Last Tested: 01 June 2020
103
	 *  FreeDNS IPv6 v2 - Last Tested: 01 June 2020
104
	 *  Loopia          - Last Tested: 21 August 2019
105
	 *  StaticCling     - Last Tested: 27 April 2006
106
	 *  DNSexit         - Last Tested: 20 July 2008
107
	 *  OpenDNS         - Last Tested: 4 August 2008
108
	 *  Namecheap       - Last Tested: 31 August 2010
109
	 *  HE.net          - Last Tested: 7 July 2013
110
	 *  HE.net IPv6     - Last Tested: 7 July 2013
111
	 *  HE.net Tunnel   - Last Tested: 28 June 2011
112
	 *  SelfHost        - Last Tested: 26 December 2011
113
	 *  Amazon Route 53 - Last Tested: 04 February 2017
114
	 *  DNS-O-Matic     - Last Tested: 9 September 2010
115
	 *  Cloudflare      - Last Tested: 05 September 2016
116
	 *  Cloudflare IPv6 - Last Tested: 17 July 2016
117
	 *  Eurodns         - Last Tested: 27 June 2013
118
	 *  GratisDNS       - Last Tested: 15 August 2012
119
	 *  OVH DynHOST     - Last Tested: NEVER
120
	 *  City Network    - Last Tested: 13 November 2013
121
	 *  GleSYS          - Last Tested: 3 February 2015
122
	 *  DNSimple        - Last Tested: 09 February 2015
123
	 *  Google Domains  - Last Tested: 27 April 2015
124
	 *  DNS Made Easy   - Last Tested: 27 April 2015
125
	 *  SPDYN           - Last Tested: 02 July 2016
126
	 *  SPDYN IPv6      - Last Tested: 02 July 2016
127
	 *  All-Inkl        - Last Tested: 12 November 2016
128
	 *  DuiaDNS         - Last Tested: 25 November 2016
129
	 *  DuiaDNS IPv6    - Last Tested: 25 November 2016
130
	 *  Hover           - Last Tested: 15 February 2017
131
	 *  DreamHost       - Last Tested: 30 April 2017
132
	 *  DreamHost IPv6  - Not Yet Tested
133
	 *  ClouDNS         - Last Tested: 22 August 2017
134
	 *  GoDaddy         - Last Tested: 22 November 2017
135
	 *  GoDaddy IPv6    - Last Tested: 22 November 2017
136
	 *  DigitalOcean    - Not Yet Tested
137
	 *  Azure DNS       - Last Tested: 08 March 2018
138
	 *  Gandi LiveDNS   - Not Yet Tested
139
	 * +====================================================+
140
	 *
141
	 * @author 	E.Kristensen
142
	 * @link    	http://www.idylldesigns.com/projects/phpdns/
143
	 * @version 	0.8
144
	 * @updated	13 October 05 at 21:02:42 GMT
145
	 *
146
	 * DNSexit/OpenDNS support and multiwan extension for pfSense by Ermal Luçi
147
	 * Custom DNS support by Matt Corallo
148
	 *
149
	 */
150

    
151
	class updatedns {
152
		var $_cacheFile;
153
		var $_cacheFile_v6;
154
		var $_debugFile;
155
		var $_UserAgent = 'phpDynDNS/0.7';
156
		var $_errorVerbosity = 0;
157
		var $_dnsService;
158
		var $_dnsUser;
159
		var $_dnsPass;
160
		var $_dnsHost;
161
		var $_dnsDomain;
162
		var $_FQDN;
163
		var $_dnsIP;
164
		var $_dnsWildcard;
165
		var $_dnsProxied;
166
		var $_dnsMX;
167
		var $_dnsBackMX;
168
		var $_dnsServer;
169
		var $_dnsPort;
170
		var $_dnsUpdateURL;
171
		var $_dnsZoneID;
172
		var $_dnsTTL;
173
		var $status;
174
		var $_debugID;
175
		var $_if;
176
		var $_dnsResultMatch;
177
		var $_dnsRequestIf;
178
		var $_dnsRequestIfIP;
179
		var $_dnsVerboseLog;
180
		var $_curlIpresolveV4;
181
		var $_curlSslVerifypeer;
182
		var $_dnsMaxCacheAgeDays;
183
		var $_dnsDummyUpdateDone;
184
		var $_forceUpdateNeeded;
185
		var $_useIPv6;
186
		var $_existingRecords;
187

    
188
		/*
189
		 * Public Constructor Function (added 12 July 05) [beta]
190
		 *   - Gets the dice rolling for the update.
191
		 *   - $dnsResultMatch should only be used with $dnsService = 'custom'
192
		 *   -  $dnsResultMatch is parsed for '%IP%', which is the IP the provider was updated to,
193
		 *   -  it is otherwise expected to be exactly identical to what is returned by the Provider.
194
		 *   - $dnsUser, and $dnsPass indicate HTTP Auth for custom DNS, if they are needed in the URL (GET Variables), include them in $dnsUpdateURL.
195
		 *   - $For custom requests, $dnsUpdateURL is parsed for '%IP%', which is replaced with the new IP.
196
		 */
197
		function __construct($dnsService = '', $dnsHost = '', $dnsDomain = '', $dnsUser = '', $dnsPass = '',
198
					$dnsWildcard = 'OFF', $dnsProxied = false, $dnsMX = '', $dnsIf = '', $dnsBackMX = '',
199
					$dnsServer = '', $dnsPort = '', $dnsUpdateURL = '', $forceUpdate = false,
200
					$dnsZoneID ='', $dnsTTL='', $dnsResultMatch = '', $dnsRequestIf = '',
201
					$dnsID = '', $dnsVerboseLog = false, $curlIpresolveV4 = false, $curlSslVerifypeer = true) {
202

    
203
			global $config, $g, $dyndns_split_domain_types;
204
			if (in_array($dnsService, $dyndns_split_domain_types)) {
205
				$this->_FQDN = $dnsHost . "." . $dnsDomain;
206
			} else {
207
				$this->_FQDN = $dnsHost;
208
			}
209

    
210
			$this->_cacheFile = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.cache";
211
			$this->_cacheFile_v6 = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}_v6.cache";
212
			$this->_debugFile = "{$g['varetc_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.debug";
213

    
214
			$this->_curlIpresolveV4 = $curlIpresolveV4;
215
			$this->_curlSslVerifypeer = $curlSslVerifypeer;
216
			$this->_dnsVerboseLog = $dnsVerboseLog;
217
			if ($this->_dnsVerboseLog) {
218
				log_error(gettext("Dynamic DNS: updatedns() starting"));
219
			}
220

    
221
			$dyndnslck = lock("DDNS" . $dnsID, LOCK_EX);
222

    
223
			if (!$dnsService) $this->_error(2);
224
			switch ($dnsService) {
225
			case 'freedns':
226
			case 'freedns-v6':
227
			case 'freedns2':
228
			case 'freedns2-v6':
229
				if (!$dnsHost) $this->_error(5);
230
				break;
231
			case "namecheap":
232
				if (!$dnsPass) $this->_error(4);
233
				if (!$dnsHost) $this->_error(5);
234
				if (!$dnsDomain) $this->_error(5);
235
				break;
236
			case "cloudflare-v6":
237
			case "cloudflare":
238
				if (!$dnsPass) $this->_error(4);
239
				if (!$dnsHost) $this->_error(5);
240
				if (!$dnsDomain) $this->_error(5);
241
				break;
242
			case "gratisdns":
243
			case "hover":
244
				if (!$dnsUser) $this->_error(3);
245
				if (!$dnsPass) $this->_error(4);
246
				if (!$dnsHost) $this->_error(5);
247
				if (!$dnsDomain) $this->_error(5);
248
				break;
249
			case 'route53-v6':
250
			case 'route53':
251
				if (!$dnsZoneID) $this->_error(8);
252
				if (!$dnsTTL) $this->_error(9);
253
				break;
254
			case 'cloudns':
255
			case "godaddy":
256
			case "godaddy-v6":
257
				if (!$dnsUser) $this->_error(3);
258
				if (!$dnsPass) $this->_error(4);
259
				if (!$dnsHost) $this->_error(5);
260
				if (!$dnsDomain) $this->_error(5);
261
				if (!$dnsTTL) $this->_error(9);
262
				break;
263
			case 'digitalocean':
264
			case 'digitalocean-v6':
265
			case 'linode':
266
			case 'linode-v6':
267
				if (!$dnsPass) $this->_error(4);
268
				if (!$dnsHost) $this->_error(5);
269
				if (!$dnsDomain) $this->_error(5);
270
				if (!$dnsTTL) $this->_error(9);
271
				break;
272
			case 'azure':
273
			case 'azurev6':
274
				if (!$dnsUser) $this->_error(3);
275
				if (!$dnsPass) $this->_error(4);
276
				if (!$dnsHost) $this->_error(5);
277
				if (!$dnsZoneID) $this->_error(8);
278
				if (!$dnsTTL) $this->_error(9);
279
				break;
280
			case 'gandi-livedns': // NOTE: same as digitalocean
281
				if (!$dnsPass) $this->_error(4);//API key
282
				if (!$dnsHost) $this->_error(5); //DNS Record to update
283
				if (!$dnsDomain) $this->_error(5);//domain
284
				if (!$dnsTTL) $this->_error(9);//ttl
285
				break;
286
			case 'custom':
287
			case 'custom-v6':
288
				if (!$dnsUpdateURL) $this->_error(7);
289
				break;
290
			default:
291
				if (!$dnsUser) $this->_error(3);
292
				if (!$dnsPass) $this->_error(4);
293
				if (!$dnsHost) $this->_error(5);
294
			}
295

    
296
			switch ($dnsService) {
297
				case 'he-net-v6':
298
				case 'digitalocean-v6':
299
				case 'domeneshop-v6':
300
				case 'custom-v6':
301
				case 'spdyn-v6':
302
				case 'route53-v6':
303
				case 'duiadns-v6':
304
				case 'freedns-v6':
305
				case 'freedns2-v6':
306
				case 'cloudflare-v6':
307
				case 'dreamhost-v6':
308
				case 'godaddy-v6':
309
				case 'azurev6':
310
				case 'linode-v6':
311
				case 'noip-v6':
312
				case 'noip-free-v6':
313
				case 'dynv6-v6':
314
				case 'easydns-v6':
315
					$this->_useIPv6 = true;
316
					break;
317
				default:
318
					$this->_useIPv6 = false;
319
			}
320
			$this->_dnsService = strtolower($dnsService);
321
			$this->_dnsUser = $dnsUser;
322
			$this->_dnsPass = base64_decode($dnsPass);
323
			$this->_dnsHost = $dnsHost;
324
			$this->_dnsDomain = $dnsDomain;
325
			$this->_dnsServer = $dnsServer;
326
			$this->_dnsPort = $dnsPort;
327
			$this->_dnsWildcard = $dnsWildcard;
328
			$this->_dnsProxied = $dnsProxied;
329
			$this->_dnsMX = $dnsMX;
330
			$this->_dnsZoneID = $dnsZoneID;
331
			$this->_dnsTTL = $dnsTTL;
332
			$this->_if = get_failover_interface($dnsIf);
333
			$this->_checkIP();
334
			$this->_dnsUpdateURL = $dnsUpdateURL;
335
			$this->_dnsResultMatch = $dnsResultMatch;
336
			$this->_dnsRequestIf = get_failover_interface($dnsRequestIf);
337
			if ($this->_dnsVerboseLog) {
338
				log_error(sprintf(gettext('Dynamic DNS (%1$s): running get_failover_interface for %2$s. found %3$s'), $this->_FQDN, $dnsRequestIf, $this->_dnsRequestIf));
339
			}
340
			$this->_dnsRequestIfIP = get_interface_ip($dnsRequestIf);
341
			$this->_dnsMaxCacheAgeDays = 25;
342
			$this->_dnsDummyUpdateDone = false;
343
			$this->_forceUpdateNeeded = $forceUpdate;
344

    
345
			// Ensure that we were able to lookup the IP
346
			if (!is_ipaddr($this->_dnsIP)) {
347
				log_error(sprintf(gettext('Dynamic DNS (%1$s) There was an error trying to determine the public IP for interface - %2$s (%3$s %4$s).'), $this->_FQDN, $dnsIf, $this->_if, $this->_dnsIP));
348
				unlock($dyndnslck);
349
				return;
350
			}
351

    
352
			$this->_debugID = rand(1000000, 9999999);
353

    
354
			if ($forceUpdate == false && $this->_detectChange() == false) {
355
				$this->_error(10);
356
			} else {
357
				switch ($this->_dnsService) {
358
					case 'glesys':
359
					case 'dnsomatic':
360
					case 'domeneshop':
361
					case 'domeneshop-v6':
362
					case 'dyndns':
363
					case 'dyndns-static':
364
					case 'dyndns-custom':
365
					case 'dhs':
366
					case 'noip':
367
					case 'noip-v6':
368
					case 'noip-free':
369
					case 'noip-free-v6':
370
					case 'easydns':
371
					case 'easydns-v6':
372
					case 'hn':
373
					case 'zoneedit':
374
					case 'dyns':
375
					case 'ods':
376
					case 'freedns':
377
					case 'freedns-v6':
378
					case 'freedns2':
379
					case 'freedns2-v6':
380
					case 'loopia':
381
					case 'staticcling':
382
					case 'dnsexit':
383
					case 'custom':
384
					case 'custom-v6':
385
					case 'opendns':
386
					case 'namecheap':
387
					case 'he-net':
388
					case 'he-net-v6':
389
					case 'duiadns':
390
					case 'duiadns-v6':
391
					case 'selfhost':
392
					case 'he-net-tunnelbroker':
393
					case 'route53':
394
					case 'route53-v6':
395
					case 'cloudflare':
396
					case 'cloudflare-v6':
397
					case 'eurodns':
398
					case 'gratisdns':
399
					case 'ovh-dynhost':
400
					case 'citynetwork':
401
					case 'dnsimple':
402
					case 'googledomains':
403
					case 'dnsmadeeasy':
404
					case 'spdyn':
405
					case 'spdyn-v6':
406
					case 'all-inkl':
407
					case 'cloudns':
408
					case 'hover':
409
					case 'digitalocean':
410
					case 'digitalocean-v6':
411
					case 'godaddy':
412
					case 'godaddy-v6':
413
					case 'azure':
414
					case 'azurev6':
415
					case 'linode':
416
					case 'linode-v6':
417
					case 'gandi-livedns':
418
					case 'dynv6':
419
					case 'dynv6-v6':
420
						$this->_update();
421
						if ($this->_dnsDummyUpdateDone == true) {
422
							// If a dummy update was needed, then sleep a while and do the update again to put the proper address back.
423
							// Some providers (e.g. No-IP free accounts) need to have at least 1 address change every month.
424
							// If the address has not changed recently, or the user did "Force Update", then the code does
425
							// a dummy address change for providers like this.
426
							sleep(10);
427
							$this->_update();
428
						}
429
						break;
430
					case 'dreamhost':
431
					case 'dreamhost-v6':
432
						$this->_lookup_current();
433
						if (isset($this->status)) {
434
							return;
435
						}
436
						foreach ($this->_existingRecords as $record) {
437
							$this->_remove($record['existing_val']);
438
							$this->_update();
439
						}
440
						break;
441
					default:
442
						$this->_error(6);
443
						break;
444
				}
445
			}
446

    
447
			unlock($dyndnslck);
448
		}
449

    
450
		/*
451
		 * Private Function (added 12 July 05) [beta]
452
		 *   Send Update To Selected Service.
453
		 */
454
		function _update() {
455

    
456
			if ($this->_dnsVerboseLog) {
457
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
458
			}
459

    
460
			if (strstr($this->_dnsRequestIf, "_vip")) {
461
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
462
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
463
			} else {
464
				$realparentif = $this->_dnsRequestIf;
465
			}
466

    
467
			$ch = curl_init();
468

    
469
			if ($this->_useIPv6 == false) {
470
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
471
			}
472

    
473
			if ($this->_dnsService != 'ods') {
474
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
475
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
476
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
477
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
478
			}
479

    
480
			switch ($this->_dnsService) {
481
				case 'glesys':
482
					$needsIP = TRUE;
483
					$server = 'https://api.glesys.com/domain/updaterecord/format/json';
484
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
485
					$post_data['recordid'] = $this->_FQDN;
486
					$post_data['data'] = $this->_dnsIP;
487
					curl_setopt($ch, CURLOPT_URL, $server);
488
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
489
					break;
490
				case 'dyndns':
491
				case 'dyndns-static':
492
				case 'dyndns-custom':
493
					$needsIP = FALSE;
494
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
495
						$this->_dnsWildcard = "ON";
496
					}
497
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
498
					$server = "https://members.dyndns.org/nic/update";
499
					$port = "";
500
					if ($this->_dnsServer) {
501
						$server = $this->_dnsServer;
502
					}
503
					if ($this->_dnsPort) {
504
						$port = ":" . $this->_dnsPort;
505
					}
506
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
507
					break;
508
				case 'dhs':
509
					// DHS is disabled in the GUI because the following doesn't work.
510
					$needsIP = TRUE;
511
					$post_data['hostscmd'] = 'edit';
512
					$post_data['hostscmdstage'] = '2';
513
					$post_data['type'] = '4';
514
					$post_data['updatetype'] = 'Online';
515
					$post_data['mx'] = $this->_dnsMX;
516
					$post_data['mx2'] = '';
517
					$post_data['txt'] = '';
518
					$post_data['offline_url'] = '';
519
					$post_data['cloak'] = 'Y';
520
					$post_data['cloak_title'] = '';
521
					$post_data['ip'] = $this->_dnsIP;
522
					$post_data['domain'] = 'dyn.dhs.org';
523
					$post_data['hostname'] = $this->_dnsHost;
524
					$post_data['submit'] = 'Update';
525
					$server = "https://members.dhs.org/nic/hosts";
526
					$port = "";
527
					if ($this->_dnsServer) {
528
						$server = $this->_dnsServer;
529
					}
530
					if ($this->_dnsPort) {
531
						$port = ":" . $this->_dnsPort;
532
					}
533
					curl_setopt($ch, CURLOPT_URL, $server . $port);
534
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
535
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
536
					break;
537
				case 'noip-v6':
538
				case 'noip-free-v6':
539
					$needsIP = TRUE;
540
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
541
					$server = "https://dynupdate.no-ip.com/nic/update";
542
					$port = "";
543
					if ($this->_dnsServer) {
544
						$server = $this->_dnsServer;
545
					}
546
					if ($this->_dnsPort) {
547
						$port = ":" . $this->_dnsPort;
548
					}
549
					if (($this->_dnsService == "noip-free-v6") &&
550
					    ($this->_forceUpdateNeeded == true) &&
551
					    ($this->_dnsDummyUpdateDone == false)) {
552
						// Update the IP to a dummy value to force No-IP free accounts to see a change.
553
						$iptoset = "fd00:d::1";
554
						$this->_dnsDummyUpdateDone = true;
555
						$log_message = 'Dynamic DNS %1$s (%2$s): ';
556
						$log_message .= 'Processing dummy update on No-IP free account. ';
557
						$log_message .= 'IP temporarily set to %3$s';
558
						log_error(sprintf(gettext($log_message), $this->_dnsService, $this->_dnsHost, $iptoset));
559
					} else {
560
						$iptoset = $this->_dnsIP;
561
					}
562
					curl_setopt($ch, CURLOPT_URL, $url_data);
563
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myipv6=' . $iptoset);
564
					break;
565
				case 'noip':
566
				case 'noip-free':
567
					$needsIP = TRUE;
568
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
569
					$server = "https://dynupdate.no-ip.com/nic/update";
570
					$port = "";
571
					if ($this->_dnsServer) {
572
						$server = $this->_dnsServer;
573
					}
574
					if ($this->_dnsPort) {
575
						$port = ":" . $this->_dnsPort;
576
					}
577
					if (($this->_dnsService == "noip-free") &&
578
					    ($this->_forceUpdateNeeded == true) &&
579
					    ($this->_dnsDummyUpdateDone == false)) {
580
						// Update the IP to a dummy value to force No-IP free accounts to see a change.
581
						$iptoset = "192.168.1.1";
582
						$this->_dnsDummyUpdateDone = true;
583
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): Processing dummy update on No-IP free account. IP temporarily set to %3$s'), $this->_dnsService, $this->_dnsHost, $iptoset));
584
					} else {
585
						$iptoset = $this->_dnsIP;
586
					}
587
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $iptoset);
588
					break;
589
				case 'easydns':
590
					$needsIP = TRUE;
591
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
592
					$server = "https://members.easydns.com/dyn/dyndns.php";
593
					$port = "";
594
					if ($this->_dnsServer) {
595
						$server = $this->_dnsServer;
596
					}
597
					if ($this->_dnsPort) {
598
						$port = ":" . $this->_dnsPort;
599
					}
600
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=' . $this->_dnsBackMX);
601
					break;
602
				case 'easydns-v6':
603
					$needsIP = TRUE;
604
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
605
					$server = "https://members.easydns.com/dyn/dyndns.php";
606
					$port = "";
607
					if ($this->_dnsServer) {
608
						$server = $this->_dnsServer;
609
					}
610
					if ($this->_dnsPort) {
611
						$port = ":" . $this->_dnsPort;
612
					}
613
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=' . $this->_dnsBackMX);
614
					break;
615
				case 'hn':
616
					$needsIP = TRUE;
617
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
618
					$server = "http://dup.hn.org/vanity/update";
619
					$port = "";
620
					if ($this->_dnsServer) {
621
						$server = $this->_dnsServer;
622
					}
623
					if ($this->_dnsPort) {
624
						$port = ":" . $this->_dnsPort;
625
					}
626
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?ver=1&IP=' . $this->_dnsIP);
627
					break;
628
				case 'zoneedit':
629
					$needsIP = FALSE;
630
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
631

    
632
					$server = "https://dynamic.zoneedit.com/auth/dynamic.html";
633
					$port = "";
634
					if ($this->_dnsServer) {
635
						$server = $this->_dnsServer;
636
					}
637
					if ($this->_dnsPort) {
638
						$port = ":" . $this->_dnsPort;
639
					}
640
					curl_setopt($ch, CURLOPT_URL, "{$server}{$port}?host=" . $this->_dnsHost . '&dnsto=' . $this->_dnsIP);
641
					break;
642
				case 'dyns':
643
					$needsIP = FALSE;
644
					$server = "http://www.dyns.net/postscript011.php";
645
					$port = "";
646
					if ($this->_dnsServer) {
647
						$server = $this->_dnsServer;
648
					}
649
					if ($this->_dnsPort) {
650
						$port = ":" . $this->_dnsPort;
651
					}
652
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost);
653
					break;
654
				case 'ods':
655
					$needsIP = FALSE;
656
					$misc_errno = 0;
657
					$misc_error = "";
658
					$server = "ods.org";
659
					$port = "";
660
					if ($this->_dnsServer) {
661
						$server = $this->_dnsServer;
662
					}
663
					if ($this->_dnsPort) {
664
						$port = ":" . $this->_dnsPort;
665
					}
666
					$this->con['socket'] = fsockopen("{$server}{$port}", "7070", $misc_errno, $misc_error, 30);
667
					/* Check that we have connected */
668
					if (!$this->con['socket']) {
669
						print "error! could not connect.";
670
						break;
671
					}
672
					/* Here is the loop. Read the incoming data (from the socket connection) */
673
					while (!feof($this->con['socket'])) {
674
						$this->con['buffer']['all'] = trim(fgets($this->con['socket'], 4096));
675
						$code = substr($this->con['buffer']['all'], 0, 3);
676
						sleep(1);
677
						switch ($code) {
678
							case 100:
679
								fputs($this->con['socket'], "LOGIN " . $this->_dnsUser . " " . $this->_dnsPass . "\n");
680
								break;
681
							case 225:
682
								fputs($this->con['socket'], "DELRR " . $this->_dnsHost . " A\n");
683
								break;
684
							case 901:
685
								fputs($this->con['socket'], "ADDRR " . $this->_dnsHost . " A " . $this->_dnsIP . "\n");
686
								break;
687
							case 795:
688
								fputs($this->con['socket'], "QUIT\n");
689
								break;
690
						}
691
					}
692
					$this->_checkStatus(0, $code);
693
					break;
694
				case 'freedns':
695
				case 'freedns-v6':
696
					$needIP = TRUE;
697
					curl_setopt($ch, CURLOPT_URL, 'https://freedns.afraid.org/dynamic/update.php?' . $this->_dnsPass . '&address=' . $this->_dnsIP);
698
					break;
699
				case 'freedns2':
700
					$needIP = TRUE;
701
					curl_setopt($ch, CURLOPT_URL, 'https://sync.afraid.org/u/' . $this->_dnsPass . '/?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
702
					break;
703
				case 'freedns2-v6':
704
					$needIP = TRUE;
705
					curl_setopt($ch, CURLOPT_URL, 'https://v6.sync.afraid.org/u/' . $this->_dnsPass . '/?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
706
					break;
707
				case 'dnsexit':
708
					$needsIP = TRUE;
709
					curl_setopt($ch, CURLOPT_URL, 'https://update.dnsexit.com/RemoteUpdate.sv?login=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
710
					break;
711
				case 'loopia':
712
					$needsIP = TRUE;
713
					if(isset($this->_dnsWildcard) && $this->_dnsWildcard == TRUE) {
714
						$this->_dnsWildcard = "ON";
715
					} else {
716
						$this->_dnsWildcard = "OFF";
717
					}
718
					curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
719
					curl_setopt($ch, CURLOPT_URL, "https://dyndns.loopia.se/?system=custom&hostname={$this->_dnsHost}&myip={$this->_dnsIP}&wildcard={$this->_dnsWildcard}&mx={$this->_dnsMX}&backmx=NO");
720
					break;
721
				case 'opendns':
722
					$needsIP = FALSE;
723
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
724
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
725
					$server = "https://updates.opendns.com/nic/update?hostname=" . $this->_dnsHost;
726
					$port = "";
727
					if ($this->_dnsServer) {
728
						$server = $this->_dnsServer;
729
					}
730
					if ($this->_dnsPort) {
731
						$port = ":" . $this->_dnsPort;
732
					}
733
					curl_setopt($ch, CURLOPT_URL, $server . $port);
734
					break;
735

    
736
				case 'staticcling':
737
					$needsIP = FALSE;
738
					curl_setopt($ch, CURLOPT_URL, 'https://www.staticcling.org/update.html?login=' . $this->_dnsUser . '&pass=' . $this->_dnsPass);
739
					break;
740
				case 'dnsomatic':
741
					/* Example syntax
742
						https://username:password@updates.dnsomatic.com/nic/update?hostname=yourhostname&myip=ipaddress&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG
743
					*/
744
					$needsIP = FALSE;
745
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
746
						$this->_dnsWildcard = "ON";
747
					}
748
					/*
749
					Reference: https://www.dnsomatic.com/wiki/api
750
						DNS-O-Matic usernames are 3-25 characters.
751
						DNS-O-Matic passwords are 6-20 characters.
752
						All ASCII letters and numbers accepted.
753
						Dots, dashes, and underscores allowed, but not at the beginning or end of the string.
754
					Required: "rawurlencode" http://www.php.net/manual/en/function.rawurlencode.php
755
						Encodes the given string according to RFC 3986.
756
					*/
757
					$server = "https://" . rawurlencode($this->_dnsUser) . ":" . rawurlencode($this->_dnsPass) . "@updates.dnsomatic.com/nic/update?hostname=";
758
					if ($this->_dnsServer) {
759
						$server = $this->_dnsServer;
760
					}
761
					if ($this->_dnsPort) {
762
						$port = ":" . $this->_dnsPort;
763
					}
764
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NOCHG');
765
					break;
766
				case 'domeneshop':
767
				case 'domeneshop-v6':
768
					/* Example:
769
						https://{token}:{secret}@api.domeneshop.no/v0/dyndns/update?hostname=example.com&myip=127.0.0.1
770
					*/
771
					$needsIP = FALSE;
772
					$server = "https://{$this->_dnsUser}:{$this->_dnsPass}@api.domeneshop.no/v0/dyndns/update?hostname={$this->_dnsHost}&myip={$this->_dnsIP}";
773
					curl_setopt($ch, CURLOPT_URL, $server);
774
					break;
775
				case 'namecheap':
776
					/* Example:
777
						https://dynamicdns.park-your-domain.com/update?host=[host_name]&domain=[domain.com]&password=[domain_password]&ip=[your_ip]
778
					*/
779
					$needsIP = FALSE;
780
					$dnspass = trim($this->_dnsPass);
781
					$server = "https://dynamicdns.park-your-domain.com/update?host={$this->_dnsHost}&domain={$this->_dnsDomain}&password={$dnspass}&ip={$this->_dnsIP}";
782
					curl_setopt($ch, CURLOPT_URL, $server);
783
					break;
784
				case 'duiadns':
785
				case 'duiadns-v6':
786
					$needsIP = FALSE;
787
					$server = "https://ipv4.duiadns.net/dyndns.duia?";
788
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
789
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
790
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
791
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
792
					break;
793
				case 'he-net':
794
				case 'he-net-v6':
795
					$needsIP = FALSE;
796
					$server = "https://dyn.dns.he.net/nic/update?";
797
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
798
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
799
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&password=' . $this->_dnsPass . '&myip=' . $this->_dnsIP);
800
					break;
801
				case 'he-net-tunnelbroker':
802
					$needsIP = FALSE;
803
					$server = "https://ipv4.tunnelbroker.net/nic/update?";
804
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
805
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
806
					break;
807
				case 'selfhost':
808
					$needsIP = FALSE;
809
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
810
						$this->_dnsWildcard = "ON";
811
					}
812
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
813
					$server = "https://carol.selfhost.de/nic/update";
814
					$port = "";
815
					if ($this->_dnsServer) {
816
						$server = $this->_dnsServer;
817
					}
818
					if ($this->_dnsPort) {
819
						$port = ":" . $this->_dnsPort;
820
					}
821
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
822
					break;
823
				case 'route53':
824
					require_once("r53.class");
825
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
826
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
827
					$xmlreq = $r53->getRequestBody($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
828
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
829
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
830
					if($this->_dnsVerboseLog){
831
						log_error(sprintf("Sending request to: %s", $apiurl));
832
						foreach($httphead as $hv){
833
							log_error(sprintf("Header: %s", $hv));
834
						}
835
						log_error(sprintf("XMLPOST: %s", $xmlreq));
836
					}
837
					curl_setopt($ch, CURLOPT_URL, $apiurl);
838
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
839
					break;
840
				case 'route53-v6':
841
					require_once("r53.class");
842
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
843
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
844
					$xmlreq = $r53->getRequestBodyV6($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
845
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
846
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
847
					if($this->_dnsVerboseLog){
848
						log_error(sprintf("Sending request to: %s", $apiurl));
849
						foreach($httphead as $hv){
850
							log_error(sprintf("Header: %s", $hv));
851
						}
852
						log_error(sprintf("XMLPOST: %s", $xmlreq));
853
					}
854
					curl_setopt($ch, CURLOPT_URL, $apiurl);
855
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
856
					break;
857
				case 'custom':
858
				case 'custom-v6':
859
					if (strstr($this->dnsUpdateURL, "%IP%")) {$needsIP = TRUE;} else {$needsIP = FALSE;}
860
					if ($this->_dnsUser != '') {
861
						if ($this->_curlIpresolveV4) {
862
							curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
863
						}
864
						if ($this->_curlSslVerifypeer) {
865
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
866
						} else {
867
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
868
						}
869
						curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
870
					}
871
					$server = str_replace("%IP%", $this->_dnsIP, $this->_dnsUpdateURL);
872
					if ($this->_dnsVerboseLog) {
873
						log_error(sprintf(gettext("Sending request to: %s"), $server));
874
					}
875
					curl_setopt($ch, CURLOPT_URL, $server);
876
					break;
877
				case 'cloudflare-v6':
878
				case 'cloudflare':
879
					$this->_FQDN = ltrim($this->_FQDN, '@.');
880
					$isv6 = ($this->_dnsService === 'cloudflare-v6');
881
					$recordType = $isv6 ? "AAAA" : "A";
882
					$needsIP = TRUE;
883
					$dnsServer ='api.cloudflare.com';
884
					$dnsHost = str_replace(' ', '', $this->_dnsHost);
885

    
886
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
887

    
888
					if ((!$this->_dnsUser) || (strpos($this->_dnsUser, '@') !== false)) {
889
						if (strpos($this->_dnsUser, '@') !== false) {
890
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
891
										'X-Auth-Email: ' . $this->_dnsUser,
892
										'X-Auth-Key: ' . $this->_dnsPass,
893
										'Content-Type: application/json'
894
										));
895
						} else {
896
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
897
										'Authorization: Bearer ' . $this->_dnsPass,
898
										'Content-Type: application/json'
899
										));
900
						}
901

    
902
						// Get zone ID
903
						$getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$this->_dnsDomain}";
904
						curl_setopt($ch, CURLOPT_URL, $getZoneId);
905
						$output = json_decode(curl_exec($ch));
906
						$zone = $output->result[0]->id;
907
					} else {
908
						curl_setopt($ch, CURLOPT_HTTPHEADER, array(
909
							'Authorization: Bearer ' . $this->_dnsPass,
910
							'Content-Type: application/json'
911
						));
912

    
913
						$zone = $this->_dnsUser;
914
					}
915

    
916
					if ($zone) { // If zone ID was found get host ID
917
						$getHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records?name={$this->_FQDN}&type={$recordType}";
918
						curl_setopt($ch, CURLOPT_URL, $getHostId);
919
						$output = json_decode(curl_exec($ch));
920
						$host = $output->result[0]->id;
921
						if ($host) { // If host ID was found update host
922
							$hostData = array(
923
								"content" => "{$this->_dnsIP}",
924
								"type" => "{$recordType}",
925
								"proxied" => $this->_dnsProxied,
926
								"name" => "{$this->_dnsHost}",
927
								"ttl" => empty($this->_dnsTTL) ? 1 : (int) $this->_dnsTTL
928
							);
929
							$data_json = json_encode($hostData);
930
							$updateHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records/{$host}";
931
							curl_setopt($ch, CURLOPT_URL, $updateHostId);
932
							curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
933
							curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
934
						}
935
					}
936
					break;
937
				case 'eurodns':
938
					$needsIP = TRUE;
939
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
940
					$server = "https://update.eurodyndns.org/update/";
941
					$port = "";
942
					if ($this->_dnsPort) {
943
						$port = ":" . $this->_dnsPort;
944
					}
945
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
946
					break;
947
				case 'gratisdns':
948
					$needsIP = TRUE;
949
					$server = "https://ssl.gratisdns.dk/ddns.phtml";
950
					curl_setopt($ch, CURLOPT_URL, $server . '?u=' . $this->_dnsUser . '&p=' . $this->_dnsPass . '&h=' . $this->_dnsHost . '&d=' . $this->_dnsDomain . '&i=' . $this->_dnsIP);
951
					break;
952
				case 'ovh-dynhost':
953
					$needsIP = FALSE;
954
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
955
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
956
					$server = "https://www.ovh.com/nic/update";
957
					$port = "";
958
					if ($this->_dnsServer) {
959
						$server = $this->_dnsServer;
960
					}
961
					if ($this->_dnsPort) {
962
						$port = ":" . $this->_dnsPort;
963
					}
964
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
965
					break;
966
				case 'citynetwork':
967
					$needsIP = TRUE;
968
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
969
					$server = 'https://dyndns.citynetwork.se/nic/update';
970
					$port = "";
971
					if ($this->_dnsServer) {
972
						$server = $this->_dnsServer;
973
					}
974
					if ($this->_dnsPort) {
975
						$port = ":" . $this->_dnsPort;
976
					}
977
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
978
					break;
979
				case 'dnsimple':
980
					/* Uses DNSimple's v2 REST API
981
					   Requires the Account ID as the username (found in the URL when pull up the domain)
982
					   And an API Token for the password (generated in the User Settings -> API tokens area of the website)
983
					   Piggybacks on Route 53's ZoneID field for the DNSimple record ID to update
984
					   The DNS record MUST exist before it can update since it performs a PATCH operation
985
					   Data sent as JSON over HTTPS */
986
					$needsIP = TRUE;
987
					$server = 'https://api.dnsimple.com/v2/';
988
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
989
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-Type: application/json', 'Authorization: Bearer ' . $this->_dnsPass));
990
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsUser . '/zones/' . $this->_dnsHost . '/records/' . $this->_dnsZoneID);
991
					curl_setopt($ch, CURLOPT_POSTFIELDS, '{"content":"' . $this->_dnsIP . '","ttl":"' . $this->_dnsTTL . '"}');
992
					break;
993
				case 'godaddy':
994
				case 'godaddy-v6':
995
					/* Uses GoDaddy's REST API
996
					   Requires username and Account API sso-key passed in header
997
					   Data sent as JSON */
998
					$needsIP = TRUE;
999
					$server = 'https://api.godaddy.com/v1/domains/';
1000
					$recordType = $this->_useIPv6 ? "AAAA" : "A";
1001
					$url = $server . $this->_dnsDomain . '/records/' . $recordType . '/' . $this->_dnsHost;
1002
					$jsondata = '[{"data":"' . $this->_dnsIP . '","ttl":' . $this->_dnsTTL . '}]';
1003
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1004
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1005
						'Accept: application/json',
1006
						'Content-Type: application/json',
1007
						'Authorization: sso-key ' . $this->_dnsUser . ':' . $this->_dnsPass
1008
					));
1009
					curl_setopt($ch, CURLOPT_URL, $url);
1010
					curl_setopt($ch, CURLOPT_POSTFIELDS, $jsondata);
1011
					break;
1012
				case 'googledomains':
1013
					$needsIP = FALSE;
1014
					$post_data['username:password'] = $this->_dnsUser . ':' . $this->_dnsPass;
1015
					$post_data['hostname'] = $this->_dnsHost;
1016
					$post_data['myip'] = $this->_dnsIP;
1017
					$post_data['offline'] = 'no';
1018
					$server = "https://domains.google.com/nic/update";
1019
					$port = "";
1020
					curl_setopt($ch, CURLOPT_URL, 'https://domains.google.com/nic/update');
1021
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1022
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1023
					break;
1024
				case 'dnsmadeeasy':
1025
					$needsIP = TRUE;
1026
					$server = "https://cp.dnsmadeeasy.com/servlet/updateip";
1027
					curl_setopt($ch, CURLOPT_URL, $server . '?username=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&id=' . $this->_dnsHost . '&ip=' . $this->_dnsIP);
1028
					break;
1029
				case 'spdyn':
1030
				case 'spdyn-v6':
1031
					$needsIP = FALSE;
1032
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1033
					$server = "https://update.spdyn.de/nic/update";
1034
					$port = "";
1035
					if ($this->_dnsServer) {
1036
						$server = $this->_dnsServer;
1037
					}
1038
					if ($this->_dnsPort) {
1039
						$port = ":" . $this->_dnsPort;
1040
					}
1041
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1042
					break;
1043
				case 'all-inkl':
1044
					$needsIP = FALSE;
1045
					$server = 'https://dyndns.kasserver.com/';
1046
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1047
					curl_setopt($ch, CURLOPT_URL, $server . 'myip=' . $this->_dnsIP);
1048
					break;
1049
				case 'hover':
1050
					$needsIP = FALSE;
1051
					$port = "";
1052
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1053
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1054

    
1055
					//step 1: login to API
1056
					$post_data['username'] = $this->_dnsUser;
1057
					$post_data['password'] = $this->_dnsPass;
1058
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1059
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
1060
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1061
					$output = curl_exec($ch);
1062

    
1063
					//extract the cookies
1064
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1065
					if( count($cookies[1]) > 0 ){
1066
						$cookie_data = implode("; ",$cookies[1]);
1067
					}
1068

    
1069
					//step 2: find the id of the A record
1070
					$post_data = null;
1071
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1072
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1073
					curl_setopt($ch, CURLOPT_HEADER, 0);
1074
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
1075
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1076

    
1077
					$output = curl_exec($ch);
1078
					$pregHost = preg_quote($this->_dnsHost);
1079
					$pregDomain = preg_quote($this->_dnsDomain);
1080
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$pregHost}\",\"type\":\"A\".*?\$/", $output, $hostID);
1081
					$hostID = $hostID[1];
1082
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{[^\}]*?\"name\":\"{$pregHost}\",\"type\":\"A\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
1083
					$hostIP = $hostIP[1];
1084
					unset($pregHost);
1085
					unset($pregDomain);
1086

    
1087
					//step 3: update the IP
1088
					if ($hostID) {
1089
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1090
						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1091
						curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1092
						$post_data['content'] = $this->_dnsIP;
1093
						curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1094
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1095
						curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns/{$hostID}");
1096
						log_error("HostID:{$hostID}, OldIP:{$hostIP}");
1097
					}
1098
					break;
1099
				case 'dreamhost':
1100
				case 'dreamhost-v6':
1101
					$needsIP = TRUE;
1102
					$isv6 = ($this->_dnsService === 'dreamhost-v6');
1103
					$server = 'https://api.dreamhost.com/';
1104
					$post_data['key'] = $this->_dnsPass;
1105
					$post_data['unique_id'] = uniqid($this->_dnsHost);
1106
					$post_data['cmd'] = 'dns-add_record';
1107
					$post_data['format'] = 'json';
1108
					$post_data['value'] = $this->_dnsIP;
1109
					$post_data['record'] = $this->_dnsHost;
1110
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1111
					$post_data['comment'] = "Updated by pfSense:$this->_dnsUser on " . date('c');
1112
					$port = "";
1113
					if ($this->_dnsServer) {
1114
						$server = $this->_dnsServer;
1115
					}
1116
					if ($this->_dnsPort) {
1117
						$port = ":" . $this->_dnsPort;
1118
					}
1119
					curl_setopt($ch, CURLOPT_URL, $server . $port);
1120
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1121
					break;
1122
				case 'digitalocean':
1123
				case 'digitalocean-v6':
1124
					// Get record ID
1125
					$server = 'https://api.digitalocean.com/v2/domains/';
1126
					$isv6 = ($this->_dnsService === 'digitalocean-v6');
1127
					$url = $server . $this->_dnsDomain . '/records';
1128
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$this->_dnsPass}"));
1129
					curl_setopt($ch, CURLOPT_URL, $url);
1130
					$output = json_decode(curl_exec($ch));
1131
					if (!is_array($output->domain_records)) {
1132
						$output->domain_records = array();
1133
					}
1134

    
1135
					// DO's API lists 20 NS records per page, so additional pages needs to be downloaded
1136
					// https://redmine.pfsense.org/issues/10952
1137
					$_domain_records = $output->domain_records;
1138
					$_count = count($_domain_records);
1139
					$_total = 0;
1140
					if (property_exists($output, 'meta')) {
1141
						$meta = $output->meta;
1142
						if (property_exists($meta, 'total')) {
1143
							$_total = $meta->total;
1144
						}
1145
					}
1146
					$_next = '...';
1147
					$_last = '';
1148
					while ($_next != $_last) {
1149
						$_next = '';
1150
						if (property_exists($output, 'links')) {
1151
							$_links = $output->links;
1152
							if (property_exists($_links, 'pages')) {
1153
								$_pages = $_links->pages;
1154
								if (property_exists($_pages, 'next')) {
1155
									$_next = $_pages->next;
1156
								}
1157
								if (property_exists($_pages, 'last')) {
1158
									$_last = $_pages->last;
1159
								}
1160
								if ($_next != '') {
1161
									echo "getting $_next\n";
1162
									curl_setopt($ch, CURLOPT_URL, $_next);
1163
									$output = json_decode(curl_exec($ch));
1164
									if (!is_array($output->domain_records)) {
1165
										$output->domain_records = array();
1166
									}
1167
									$_domain_records = array_merge($_domain_records,$output->domain_records);
1168
								}
1169
							}
1170
						}
1171
					}
1172
					$_count = count($_domain_records);
1173

    
1174
					foreach($_domain_records as $dnsRecord) {
1175
						// NS records are named @ in DO's API, so check type as well 
1176
						// https://redmine.pfsense.org/issues/9171
1177
						if ($this->_dnsHost == $dnsRecord->name && $dnsRecord->type == ($isv6 ? 'AAAA' : 'A')) {
1178
							$recordID = $dnsRecord->id;
1179
							break;
1180
						}
1181
					}
1182

    
1183
					// Create/update record
1184
					if ($recordID == null) {
1185
						$url = $server . $this->_dnsDomain . '/records';
1186
					} else {
1187
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1188
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1189
					}
1190
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1191
					$post_data['ttl'] = $this->_dnsTTL;
1192
					$post_data['name'] = $this->_dnsHost;
1193
					$post_data['data'] = $this->_dnsIP;
1194
					curl_setopt($ch, CURLOPT_URL, $url);
1195
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1196
					break;
1197
				case 'cloudns':
1198
					/* Uses ClouDNS REST API
1199
					   Requires auth-id or sub-auth-id or sub-auth-user */
1200
					// Step 1: Find the Record ID
1201
					$url = 'https://api.cloudns.net/dns/records.json';
1202
					$post_data['auth-id'] = $this->_dnsUser;
1203
					$post_data['auth-password'] = $this->_dnsPass;
1204
					$post_data['domain-name'] = $this->_dnsDomain;
1205
					$post_data['host'] = $this->_dnsHost;
1206
					$post_data['type'] = 'a';
1207
					curl_setopt($ch, CURLOPT_URL, $url);
1208
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1209
					$output = json_decode(curl_exec($ch));
1210
					$recordID = key(get_object_vars($output));
1211

    
1212
					// Step 2: Set the record
1213
					$needsIP = TRUE;
1214
					$url = 'https://api.cloudns.net/dns/mod-record.json';
1215
					$post_data = array();
1216
					$post_data['auth-id'] = $this->_dnsUser;
1217
					$post_data['auth-password'] = $this->_dnsPass;
1218
					$post_data['domain-name'] = $this->_dnsDomain;
1219
					$post_data['record-id'] = $recordID;
1220
					$post_data['host'] = $this->_dnsHost;
1221
					$post_data['record'] = $this->_dnsIP;
1222
					$post_data['ttl'] = $this->_dnsTTL;
1223
					curl_setopt($ch, CURLOPT_URL, $url);
1224
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1225
					break;
1226
				case 'azurev6':
1227
				case 'azure':
1228
					$hostname = "{$this->_dnsHost}";
1229
					$resourceid = trim($this->_dnsZoneID);
1230
					$app_id = $this->_dnsUser;
1231
					$client_secret = $this->_dnsPass;
1232
					$newip = $this->_dnsIP;
1233
					$newttl = $this->_dnsTTL;
1234
						// ensure resourceid starts with / and has no trailing /
1235
					$resourceid = '/' . trim($resourceid, '/');
1236
						// extract subscription id from resource id
1237
					preg_match('/\\/subscriptions\\/(?<sid>[^\\/]*)/', $resourceid, $result);
1238
					$subscriptionid = isset($result['sid']) ? $result['sid'] : '';
1239
					if (isset($result['sid'])) {
1240
						$subscriptionid = $result['sid'];
1241
					} else {
1242
						log_error("Azure subscription id not found in resource id");
1243
						return false;
1244
					}
1245
						// find tenant id from subscription id
1246
					curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01");
1247
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1248
					curl_setopt($ch, CURLOPT_HEADER, 1);
1249
					curl_setopt($ch, CURLOPT_NOBODY, 1);
1250
					$output = curl_exec($ch);
1251
					$pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?<tid>[^"]*)/i';
1252
					preg_match($pattern, $output, $result);
1253
					if (isset($result['tid'])) {
1254
						$tenantid = $result['tid'];
1255
					} else {
1256
						log_error("Tenant ID not found");
1257
						return false;
1258
					}
1259
						// get an bearer token
1260
					curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token");
1261
					curl_setopt($ch, CURLOPT_POST, 1);
1262
					$body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret);
1263
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1264
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1265
					$server_output = curl_exec($ch);
1266
					$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1267
					preg_match("/\"access_token\":\"(?<tok>[^\"]*)\"/", $server_output, $result);
1268
					if (isset($result['tok'])) {
1269
						$bearertoken = $result['tok'];
1270
					} else {
1271
						log_error("no valid bearer token");
1272
						return false;
1273
					}
1274
						// Update the DNS record
1275
					if ($this->_useIPv6) {
1276
						$url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01";
1277
						$body = '{"properties":{"TTL":"' . $newttl . '", "AAAARecords":[{"ipv6Address":"' . $newip . '"}]}}';
1278
					} else {
1279
						$url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01";
1280
						$body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}';
1281
					}
1282
					$request_headers = array();
1283
					$request_headers[] = 'Accept: application/json';
1284
					$request_headers[] = 'Authorization: Bearer ' . $bearertoken;
1285
					$request_headers[] = 'Content-Type: application/json';
1286
					curl_setopt($ch, CURLOPT_URL, $url);
1287
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1288
					curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1289
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1290
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1291
					curl_setopt($ch, CURLOPT_HEADER, 1);
1292
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1293
					break;
1294
				case 'linode':
1295
				case 'linode-v6':
1296
					$linode_api = "https://api.linode.com/v4";
1297
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1298
					$record_name = $this->_dnsHost == "@" ? "" : $this->_dnsHost;
1299
					$err_prefix = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): (" . gettext("Error") . ")";
1300
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1301
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1302
						'Accept: application/json',
1303
						'Content-Type: application/json',
1304
						'Authorization: Bearer ' . $this->_dnsPass
1305
					));
1306

    
1307
					// get domain id
1308
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains");
1309
					$domains_output = curl_exec($ch);
1310
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1311
					if ( $http_code == 401 && preg_match('/invalid oauth token/i', $domains_output) ) {
1312
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authentication Failed"));
1313
						return false;
1314
					} else if ( $http_code == 401 ) {
1315
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authorization Failed"));
1316
						return false;
1317
					} else if ( $http_code != 200 ) {
1318
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1319
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1320
						return false;
1321
					}
1322

    
1323
					$domains_result = json_decode($domains_output, TRUE);
1324
					foreach($domains_result["data"] as $domains_entry) {
1325
						if ($domains_entry["domain"] == $this->_dnsDomain) {
1326
							$domain_id = $domains_entry["id"];
1327
						}
1328
					}
1329
					if ( ! $domain_id ) {
1330
						log_error("{$err_prefix} " . gettext("no domain ID for domain") . ": '{$this->_dnsDomain}'");
1331
						return false;
1332
					}
1333
					if ($this->_dnsVerboseLog) {
1334
						log_error("_update(): " . sprintf(gettext("found domain id: %s"), $domain_id));
1335
					}
1336

    
1337
					// get existing record if present
1338
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1339
					$records_output = curl_exec($ch);
1340
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1341
					if ( $http_code != 200 )
1342
					{
1343
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1344
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1345
						return false;
1346
					}
1347

    
1348
					$records_result = json_decode($records_output, TRUE);
1349
					foreach($records_result["data"] as $records_entry) {
1350
						if ( $records_entry["type"] == $record_type && $records_entry["name"] == $record_name ) {
1351
							// not adding support for pagination at this time, hope you have < 100 records!
1352
							$record = $records_entry;
1353
						}
1354
					}
1355
					if ($this->_dnsVerboseLog) {
1356
						log_error("_update(): " . ( $record ? sprintf(gettext("found existing record id: %s"), $record["id"]) : gettext("no matching record found") ));
1357
					}
1358

    
1359
					if (is_array($record)) {
1360
						// update existing record
1361
						$record["target"] = $this->_dnsIP;
1362
						$record["ttl_sec"] = (int) $this->_dnsTTL; // linode may round this up, 0 = zone default
1363
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1364
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1365
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records/" . $record["id"]);
1366
					} else {
1367
						// create a new record
1368
						$record = array(
1369
							"type"    => $record_type,
1370
							"name"    => $record_name,
1371
							"target"  => $this->_dnsIP,
1372
							"ttl_sec" => (int) $this->_dnsTTL, // linode may round this up, 0 = zone default
1373
						);
1374
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1375
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
1376
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1377
					}
1378
					break;
1379
				case 'dynv6':
1380
					$needIP = TRUE;
1381
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv4=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1382
					break;
1383
				case 'dynv6-v6':
1384
					$needIP = TRUE;
1385
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv6=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1386
					break;
1387
			case 'gandi-livedns':
1388
				// NOTE: quite similar to digitalocean case but with json content like azure case
1389
				// Get record href
1390
				$server = 'https://api.gandi.net/v5/livedns/domains/';
1391
				$url = $server . $this->_dnsDomain . '/records';
1392
				curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Apikey {$this->_dnsPass}"));
1393
				curl_setopt($ch, CURLOPT_URL, $url);
1394
				$output = json_decode(curl_exec($ch));
1395
				if (!is_array($output)) {
1396
					$output = array();
1397
				}
1398
				foreach($output as $dnsRecord) {
1399
					// NS records are named @ in DO's API, so check type as well
1400
					if ($this->_dnsHost == $dnsRecord->rrset_name && $dnsRecord->rrset_type == 'A') {
1401
						$recordHref = $dnsRecord->rrset_href;
1402
						break;
1403
					}
1404
				}
1405
				$request_headers = array();
1406
				//$request_headers[] = 'Accept: application/json';
1407
				$request_headers[] = 'Authorization: Apikey ' . $this->_dnsPass;
1408
				$request_headers[] = 'Content-Type: application/json';
1409
				//create or update record
1410
				if ($recordHref == null) {
1411
					//create record
1412
					$url = $server . $this->_dnsDomain . '/records';
1413
					$body = '{"rrset_type": "A", "rrset_ttl": ' . $this->_dnsTTL . ', "rrset_name": "' . $this->_dnsHost . '", "rrset_values": ["' . $this->_dnsIP . '"]}';
1414
				} else {
1415
					//update record
1416
					$url = $recordHref;
1417
					$body = '{"rrset_ttl": ' . $this->_dnsTTL . ', "rrset_values": ["' . $this->_dnsIP . '"]}';
1418
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1419
				}
1420
				curl_setopt($ch, CURLOPT_URL, $url);
1421
				curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1422
				curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1423
					break;
1424
				default:
1425
					break;
1426
			}
1427
			if ($this->_dnsService != 'ods') {
1428
				curl_setopt($ch, CURLOPT_HEADER, 1);
1429
				$response = curl_exec($ch);
1430
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1431
				$header = substr($response, 0, $header_size);
1432
				$data = substr($response, $header_size);
1433
				if ($this->_dnsVerboseLog) {
1434
					foreach (explode(PHP_EOL, $header) as $hv) {
1435
						log_error(sprintf("Response Header: %s", rtrim($hv)));
1436
				  	}
1437
					log_error(sprintf("Response Data: %s", $data));
1438
				}
1439
				$this->_checkStatus($ch, $data, $header);
1440
				@curl_close($ch);
1441
			}
1442
		}
1443

    
1444
		/**
1445
		 * Private Function (added 23 Feb 17)
1446
		 *   Send Removal To Selected Service.
1447
		 *
1448
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1449
		 *   is to remove the existing record and add a new record.
1450
		 *
1451
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1452
		 */
1453
		function _remove($existing_ip = NULL) {
1454
			$remove_allowed = false;
1455
			if ($this->_dnsVerboseLog) {
1456
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1457
			}
1458

    
1459
			if (strstr($this->_dnsRequestIf, "_vip")) {
1460
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1461
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1462
			} else {
1463
				$realparentif = $this->_dnsRequestIf;
1464
			}
1465

    
1466
			$ch = curl_init();
1467

    
1468
			if ($this->_useIPv6 == false) {
1469
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1470
			}
1471

    
1472
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1473
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1474
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1475
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1476

    
1477
			switch ($this->_dnsService) {
1478
			case 'dreamhost':
1479
			case 'dreamhost-v6':
1480
				$server = 'https://api.dreamhost.com/';
1481
				$post_data['key'] = $this->_dnsPass;
1482
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1483
				$post_data['cmd'] = 'dns-remove_record';
1484
				$post_data['format'] = 'json';
1485
				$post_data['value'] = $existing_ip;
1486
				$post_data['record'] = $this->_dnsHost;
1487
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1488
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1489
				$port = "";
1490
				if ($this->_dnsServer) {
1491
					$server = $this->_dnsServer;
1492
				}
1493
				if ($this->_dnsPort) {
1494
					$port = ":" . $this->_dnsPort;
1495
				}
1496
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1497
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1498
				$remove_allowed = true;
1499
				break;
1500
			default:
1501
				break;
1502
			}
1503
			if ($remove_allowed) {
1504
				curl_setopt($ch, CURLOPT_HEADER, 1);
1505
				$response = curl_exec($ch);
1506
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1507
				$header = substr($response, 0, $header_size);
1508
				$data = substr($response, $header_size);
1509
				$this->_checkStatus($ch, $data, $header);
1510
				@curl_close($ch);
1511
			}
1512
		}
1513

    
1514
		/**
1515
		 * Private Function (added 23 Feb 17)
1516
		 * Retrieves current DNS records from an external API source.
1517
		 *
1518
		 * Some services cannot perform new operations without the caller
1519
		 * providing existing record information.
1520
		 */
1521
		function _lookup_current() {
1522
			$lookup_allowed = false;
1523
			if ($this->_dnsVerboseLog) {
1524
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1525
			}
1526

    
1527
			if (strstr($this->_dnsRequestIf, "_vip")) {
1528
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1529
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1530
			} else {
1531
				$realparentif = $this->_dnsRequestIf;
1532
			}
1533

    
1534
			$ch = curl_init();
1535

    
1536
			if ($this->_useIPv6 == false) {
1537
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1538
			}
1539

    
1540
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1541
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1542
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1543
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1544

    
1545
			switch ($this->_dnsService) {
1546
			case 'dreamhost':
1547
			case 'dreamhost-v6':
1548
				$server = 'https://api.dreamhost.com/';
1549
				$post_data['key'] = $this->_dnsPass;
1550
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1551
				$post_data['cmd'] = 'dns-list_records';
1552
				$post_data['format'] = 'json';
1553
				$port = "";
1554
				if ($this->_dnsServer) {
1555
					$server = $this->_dnsServer;
1556
				}
1557
				if ($this->_dnsPort) {
1558
					$port = ":" . $this->_dnsPort;
1559
				}
1560
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1561
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1562
				$lookup_allowed = true;
1563
				break;
1564
			default:
1565
				break;
1566
			}
1567
			if ($lookup_allowed) {
1568
				curl_setopt($ch, CURLOPT_HEADER, 1);
1569
				$response = curl_exec($ch);
1570
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1571
				$header = substr($response, 0, $header_size);
1572
				$data = substr($response, $header_size);
1573
				$this->_checkLookupStatus($ch, $data, $header);
1574
				@curl_close($ch);
1575
			}
1576
		}
1577

    
1578
		/*
1579
		 * Private Function (added 23 Feb 17)
1580
		 *   Retrieve Lookup Status from the provided data and/or header
1581
		 */
1582
		function _checkLookupStatus($ch, $data, $header) {
1583
			if ($this->_dnsVerboseLog) {
1584
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1585
			}
1586
			$success_str = "(" . gettext("Success") . ") ";
1587
			$error_str = "(" . gettext("Error") . ") ";
1588
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1589

    
1590
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1591
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1592
				log_error($status);
1593
				$this->status = $status;
1594
				return;
1595
			}
1596
			switch ($this->_dnsService) {
1597
			case 'dreamhost':
1598
			case 'dreamhost-v6':
1599
				$result = json_decode($data,true);
1600
				if($result["result"] != "success") {
1601
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1602
					$this->_debug($data);
1603
					return;
1604
				} else {
1605
					foreach($result["data"] as $key => $row) {
1606
						if($row["record"] == $this->_dnsHost &&
1607
								(($row["type"] == "A" && !$this->_useIPv6)
1608
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1609
								)) {
1610
							if($row["editable"] == 0) {
1611
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1612
								continue;
1613
							}
1614
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1615
						}
1616
					}
1617
				}
1618
				if (!is_array($this->_existingRecords)){
1619
					if ($this->_dnsVerboseLog) {
1620
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1621
					}
1622
				}
1623
				break;
1624
			default:
1625
				break;
1626
			}
1627
		}
1628

    
1629
		/*
1630
		 * Private Function (added 12 July 2005) [beta]
1631
		 *   Retrieve Update Status
1632
		 */
1633
		function _checkStatus($ch, $data, $header) {
1634
			if ($this->_dnsVerboseLog) {
1635
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1636
			}
1637
			$successful_update = false;
1638
			$success_str = "(" . gettext("Success") . ") ";
1639
			$error_str = "(" . gettext("Error") . ") ";
1640
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1641

    
1642
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1643
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1644
				log_error($status);
1645
				$this->status = $status;
1646
				return;
1647
			}
1648
			switch ($this->_dnsService) {
1649
				case 'glesys':
1650
					$status_intro = "GleSYS ({$this->_dnsHost}): ";
1651
					if (preg_match('/Record updated/i', $data)) {
1652
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1653
						$successful_update = true;
1654
					} else {
1655
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1656
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1657
						$this->_debug($data);
1658
					}
1659
					break;
1660
				case 'dnsomatic':
1661
					$status_intro = "DNS-O-Matic ({$this->_dnsHost}): ";
1662
					if (preg_match('/badauth/i', $data)) {
1663
						$status = $status_intro . gettext("The DNS-O-Matic username or password specified are incorrect. No updates will be distributed to services until this is resolved.");
1664
					} else if (preg_match('/notfqdn /i', $data)) {
1665
						$status = $status_intro . gettext("The hostname specified is not a fully-qualified domain name. If no hostnames included, notfqdn will be returned once.");
1666
					} else if (preg_match('/nohost/i', $data)) {
1667
						$status = $status_intro . gettext("The hostname passed could not be matched to any services configured. The service field will be blank in the return code.");
1668
					} else if (preg_match('/numhost/i', $data)) {
1669
						$status = $status_intro . gettext("Up to 20 hosts my be updated. numhost is returned if attempting to update more than 20 or update a round-robin.");
1670
					} else if (preg_match('/abuse/i', $data)) {
1671
						$status = $status_intro . gettext("The hostname is blocked for update abuse.");
1672
					} else if (preg_match('/good/i', $data)) {
1673
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1674
						$successful_update = true;
1675
					} else if (preg_match('/dnserr/i', $data)) {
1676
						$status = $status_intro . gettext("DNS error encountered. Stop updating for 30 minutes.");
1677
					} else {
1678
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1679
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1680
						$this->_debug($data);
1681
					}
1682
					break;
1683
				case 'domeneshop':
1684
				case 'domeneshop-v6':
1685
					/* Responds with HTTP 204 on success.
1686
					 * see https://api.domeneshop.no/docs/index.html#tag/ddns/paths/~1dyndns~1update/get */
1687

    
1688
                                        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1689
					
1690
					if ($code == "204") {
1691
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1692
						$successful_update = true;
1693
					} else {
1694
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $code . ")";
1695
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1696
						$this->_debug($data);
1697
					}
1698
					break;
1699
				case 'citynetwork':
1700
					if (preg_match('/notfqdn/i', $data)) {
1701
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1702
					} else if (preg_match('/nohost/i', $data)) {
1703
						$status = $status_intro . $error_str . gettext("No such host");
1704
					} else if (preg_match('/nochg/i', $data)) {
1705
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1706
						$successful_update = true;
1707
					} else if (preg_match('/good/i', $data)) {
1708
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1709
						$successful_update = true;
1710
					} else if (preg_match('/badauth/i', $data)) {
1711
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1712
					} else {
1713
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1714
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1715
						$this->_debug($data);
1716
					}
1717
					break;
1718
				case 'ovh-dynhost':
1719
				case 'dyndns':
1720
					if (preg_match('/notfqdn/i', $data)) {
1721
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1722
					} else if (preg_match('/nochg/i', $data)) {
1723
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1724
						$successful_update = true;
1725
					} else if (preg_match('/good/i', $data)) {
1726
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1727
						$successful_update = true;
1728
					} else if (preg_match('/noauth/i', $data)) {
1729
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1730
					} else {
1731
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1732
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1733
						$this->_debug($data);
1734
					}
1735
					break;
1736
				case 'dyndns-static':
1737
					if (preg_match('/notfqdn/i', $data)) {
1738
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1739
					} else if (preg_match('/nochg/i', $data)) {
1740
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1741
						$successful_update = true;
1742
					} else if (preg_match('/good/i', $data)) {
1743
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1744
						$successful_update = true;
1745
					} else if (preg_match('/noauth/i', $data)) {
1746
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1747
					} else {
1748
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1749
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1750
						$this->_debug($data);
1751
					}
1752
					break;
1753
				case 'dyndns-custom':
1754
					if (preg_match('/notfqdn/i', $data)) {
1755
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1756
					} else if (preg_match('/nochg/i', $data)) {
1757
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1758
						$successful_update = true;
1759
					} else if (preg_match('/good/i', $data)) {
1760
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1761
						$successful_update = true;
1762
					} else if (preg_match('/noauth/i', $data)) {
1763
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1764
					} else {
1765
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1766
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1767
						$this->_debug($data);
1768
					}
1769
					break;
1770
				case 'dhs':
1771
					break;
1772
				case 'noip':
1773
				case 'noip-v6':
1774
				case 'noip-free':
1775
				case 'noip-free-v6':
1776
					list($ip, $code) = explode(":", $data);
1777
					switch ($code) {
1778
						case 0:
1779
							$status = $status_intro . $success_str . gettext("IP address is current, no update performed.");
1780
							$successful_update = true;
1781
							break;
1782
						case 1:
1783
							$status = $status_intro . $success_str . gettext("DNS hostname update successful.");
1784
							$successful_update = true;
1785
							break;
1786
						case 2:
1787
							$status = $status_intro . $error_str . gettext("Hostname supplied does not exist.");
1788
							break;
1789
						case 3:
1790
							$status = $status_intro . $error_str . gettext("Invalid Username.");
1791
							break;
1792
						case 4:
1793
							$status = $status_intro . $error_str . gettext("Invalid Password.");
1794
							break;
1795
						case 5:
1796
							$status = $status_intro . $error_str . gettext("Too many updates sent.");
1797
							break;
1798
						case 6:
1799
							$status = $status_intro . $error_str . gettext("Account disabled due to violation of No-IP terms of service.");
1800
							break;
1801
						case 7:
1802
							$status = $status_intro . $error_str . gettext("Invalid IP. IP Address submitted is improperly formatted or is a private IP address or is on a blacklist.");
1803
							break;
1804
						case 8:
1805
							$status = $status_intro . $error_str . gettext("Disabled / Locked Hostname.");
1806
							break;
1807
						case 9:
1808
							$status = $status_intro . $error_str . gettext("Host updated is configured as a web redirect and no update was performed.");
1809
							break;
1810
						case 10:
1811
							$status = $status_intro . $error_str . gettext("Group supplied does not exist.");
1812
							break;
1813
						case 11:
1814
							$status = $status_intro . $success_str . gettext("DNS group update is successful.");
1815
							$successful_update = true;
1816
							break;
1817
						case 12:
1818
							$status = $status_intro . $success_str . gettext("DNS group is current, no update performed.");
1819
							$successful_update = true;
1820
							break;
1821
						case 13:
1822
							$status = $status_intro . $error_str . gettext("Update client support not available for supplied hostname or group.");
1823
							break;
1824
						case 14:
1825
							$status = $status_intro . $error_str . gettext("Hostname supplied does not have offline settings configured.");
1826
							break;
1827
						case 99:
1828
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
1829
							break;
1830
						case 100:
1831
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
1832
							break;
1833
						default:
1834
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1835
							$this->_debug(gettext("Unknown Response:") . " " . $data);
1836
							break;
1837
					}
1838
					break;
1839
				case 'easydns':
1840
					if (preg_match('/NOACCESS/i', $data)) {
1841
						$status = $status_intro . $error_str . gettext("Authentication Failed: Username and/or Password was Incorrect.");
1842
					} else if (preg_match('/NOSERVICE/i', $data)) {
1843
						$status = $status_intro . $error_str . gettext("No Service: Dynamic DNS Service has been disabled for this domain.");
1844
					} else if (preg_match('/ILLEGAL INPUT/i', $data)) {
1845
						$status = $status_intro . $error_str . gettext("Illegal Input: Self-Explanatory");
1846
					} else if (preg_match('/TOOSOON/i', $data)) {
1847
						$status = $status_intro . $error_str . gettext("Too Soon: Not Enough Time Has Elapsed Since Last Update");
1848
					} else if (preg_match('/NOERROR/i', $data)) {
1849
						$status = $status_intro . $success_str . gettext("IP Updated Successfully!");
1850
						$successful_update = true;
1851
					} else {
1852
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1853
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1854
						$this->_debug($data);
1855
					}
1856
					break;
1857
				case 'easydns-v6':
1858
					if (preg_match('/NOACCESS/i', $data)) {
1859
						$status = $status_intro . $error_str . gettext("Authentication Failed: Username and/or Password was Incorrect.");
1860
					} else if (preg_match('/NOSERVICE/i', $data)) {
1861
						$status = $status_intro . $error_str . gettext("No Service: Dynamic DNS Service has been disabled for this domain.");
1862
					} else if (preg_match('/ILLEGAL INPUT/i', $data)) {
1863
						$status = $status_intro . $error_str . gettext("Illegal Input: Self-Explanatory");
1864
					} else if (preg_match('/TOOSOON/i', $data)) {
1865
						$status = $status_intro . $error_str . gettext("Too Soon: Not Enough Time Has Elapsed Since Last Update");
1866
					} else if (preg_match('/NOERROR/i', $data)) {
1867
						$status = $status_intro . $success_str . gettext("IP Updated Successfully!");
1868
						$successful_update = true;
1869
					} else {
1870
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1871
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1872
						$this->_debug($data);
1873
					}
1874
					break;
1875
				case 'hn':
1876
					/* FIXME: add checks */
1877
					break;
1878
				case 'zoneedit':
1879
					if (preg_match('/799/i', $data)) {
1880
						$status = $status_intro . "(" . gettext("Error 799") . ") " . gettext("Update Failed!");
1881
					} else if (preg_match('/700/i', $data)) {
1882
						$status = $status_intro . "(" . gettext("Error 700") . ") " . gettext("Update Failed!");
1883
					} else if (preg_match('/200/i', $data)) {
1884
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1885
						$successful_update = true;
1886
					} else if (preg_match('/201/i', $data)) {
1887
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1888
						$successful_update = true;
1889
					} else {
1890
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1891
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1892
						$this->_debug($data);
1893
					}
1894
					break;
1895
				case 'dyns':
1896
					if (preg_match("/400/i", $data)) {
1897
						$status = $status_intro . $error_str . gettext("Bad Request - The URL was malformed. Required parameters were not provided.");
1898
					} else if (preg_match('/402/i', $data)) {
1899
						$status = $status_intro . $error_str . gettext("Update Too Soon - Attempted to update too quickly since last change.");
1900
					} else if (preg_match('/403/i', $data)) {
1901
						$status = $status_intro . $error_str . gettext("Database Error - There was a server-sided database error.");
1902
					} else if (preg_match('/405/i', $data)) {
1903
						$status = $status_intro . $error_str . sprintf(gettext('Hostname Error - The hostname (%1$s) doesn\'t belong to user (%2$s).'), $this->_dnsHost, $this->_dnsUser);
1904
					} else if (preg_match('/200/i', $data)) {
1905
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1906
						$successful_update = true;
1907
					} else {
1908
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1909
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1910
						$this->_debug($data);
1911
					}
1912
					break;
1913
				case 'ods':
1914
					if (preg_match("/299/i", $data)) {
1915
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1916
						$successful_update = true;
1917
					} else {
1918
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1919
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1920
						$this->_debug($data);
1921
					}
1922
					break;
1923
				case 'freedns':
1924
				case 'freedns-v6':
1925
					if (preg_match("/has not changed./i", $data)) {
1926
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1927
						$successful_update = true;
1928
					} else if (preg_match("/Updated/i", $data)) {
1929
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1930
						$successful_update = true;
1931
					} else {
1932
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1933
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1934
						$this->_debug($data);
1935
					}
1936
					break;
1937
				case 'freedns2':
1938
				case 'freedns2-v6':
1939
					if (preg_match("/No IP change detected/i", $data)) {
1940
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1941
						$successful_update = true;
1942
					} else if (preg_match("/Updated/i", $data)) {
1943
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1944
						$successful_update = true;
1945
					} else {
1946
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1947
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1948
						$this->_debug($data);
1949
					}
1950
					break;
1951
				case 'dnsexit':
1952
					if (preg_match("/is the same/i", $data)) {
1953
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1954
						$successful_update = true;
1955
					} else if (preg_match("/Success/i", $data)) {
1956
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1957
						$successful_update = true;
1958
					} else {
1959
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1960
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1961
						$this->_debug($data);
1962
					}
1963
					break;
1964
				case 'loopia':
1965
					if (preg_match("/nochg/i", $data)) {
1966
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1967
						$successful_update = true;
1968
					} else if (preg_match("/good/i", $data)) {
1969
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1970
						$successful_update = true;
1971
					} else if (preg_match('/badauth/i', $data)) {
1972
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1973
					} else {
1974
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1975
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1976
						$this->_debug($data);
1977
					}
1978
					break;
1979
				case 'opendns':
1980
					if (preg_match('/badauth/i', $data)) {
1981
						$status = $status_intro . $error_str . gettext("Not a valid username or password!");
1982
					} else if (preg_match('/nohost/i', $data)) {
1983
						$status = $status_intro . $error_str . gettext("Hostname specified does not exist.");
1984
						$successful_update = true;
1985
					} else if (preg_match('/good/i', $data)) {
1986
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1987
						$successful_update = true;
1988
					} else if (preg_match('/yours/i', $data)) {
1989
						$status = $status_intro . $error_str . gettext("Hostname specified exists, but not under the username specified.");
1990
					} else if (preg_match('/abuse/i', $data)) {
1991
						$status = $status_intro . $error_str . gettext("Updating too frequently, considered abuse.");
1992
					} else {
1993
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1994
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1995
						$this->_debug($data);
1996
					}
1997
					break;
1998
				case 'staticcling':
1999
					if (preg_match("/invalid ip/i", $data)) {
2000
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
2001
					} else if (preg_match('/required info missing/i', $data)) {
2002
						$status = $status_intro . $error_str . gettext("Bad Request - Required parameters were not provided.");
2003
					} else if (preg_match('/invalid characters/i', $data)) {
2004
						$status = $status_intro . $error_str . gettext("Bad Request - Illegal characters in either the username or the password.");
2005
					} else if (preg_match('/bad password/i', $data)) {
2006
						$status = $status_intro . $error_str . gettext("Invalid password.");
2007
					} else if (preg_match('/account locked/i', $data)) {
2008
						$status = $status_intro . $error_str . gettext("This account has been administratively locked.");
2009
					} else if (preg_match('/update too frequent/i', $data)) {
2010
						$status = $status_intro . $error_str . gettext("Updating too frequently.");
2011
					} else if (preg_match('/DB error/i', $data)) {
2012
						$status = $status_intro . $error_str . gettext("Server side error.");
2013
					} else if (preg_match('/success/i', $data)) {
2014
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2015
						$successful_update = true;
2016
					} else {
2017
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2018
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2019
						$this->_debug($data);
2020
					}
2021
					break;
2022
				case 'namecheap':
2023
					$tmp = str_replace("^M", "", $data);
2024
					$ncresponse = @xml2array($tmp);
2025
					if (preg_match("/internal server error/i", $data)) {
2026
						$status = $status_intro . $error_str . gettext("Server side error.");
2027
					} else if (preg_match("/request is badly formed/i", $data)) {
2028
						$status = $status_intro . $error_str . gettext("Badly Formed Request (check the settings).");
2029
					} else if ($ncresponse['interface-response']['ErrCount'] === "0") {
2030
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2031
						$successful_update = true;
2032
					} else if (is_numeric($ncresponse['interface-response']['ErrCount']) && ($ncresponse['interface-response']['ErrCount'] > 0)) {
2033
						$status = $status_intro . $error_str . implode(", ", $ncresponse["interface-response"]["errors"]);
2034
					} else {
2035
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2036
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2037
						$this->_debug($data);
2038
					}
2039
					break;
2040
				case 'duiadns':
2041
				case 'duiadns-v6':
2042
					if (preg_match("/error/i", $data)) {
2043
						$status = $status_intro . $error_str . gettext("Server side error.");
2044
					} else if (preg_match('/nohost/i', $data)) {
2045
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
2046
					} else if (preg_match('/badauth/i', $data)) {
2047
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
2048
					} else if (preg_match('/good/i', $data)) {
2049
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2050
						$successful_update = true;
2051
					} else if (preg_match('/nochg/i', $data)) {
2052
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
2053
						$successful_update = true;
2054
					} else {
2055
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2056
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2057
						$this->_debug($data);
2058
					}
2059
					break;
2060
				case 'he-net':
2061
				case 'he-net-v6':
2062
				case 'he-net-tunnelbroker':
2063
					if (preg_match("/badip/i", $data)) {
2064
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
2065
					} else if (preg_match('/nohost/i', $data)) {
2066
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
2067
					} else if (preg_match('/badauth/i', $data)) {
2068
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
2069
					} else if (preg_match('/good/i', $data)) {
2070
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2071
						$successful_update = true;
2072
					} else if (preg_match('/nochg/i', $data)) {
2073
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
2074
						$successful_update = true;
2075
					} else {
2076
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2077
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2078
						$this->_debug($data);
2079
					}
2080
					break;
2081
				case 'selfhost':
2082
					if (preg_match('/notfqdn/i', $data)) {
2083
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2084
					} else if (preg_match('/nochg/i', $data)) {
2085
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
2086
						$successful_update = true;
2087
					} else if (preg_match('/good/i', $data)) {
2088
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2089
						$successful_update = true;
2090
					} else if (preg_match('/noauth/i', $data)) {
2091
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2092
					} else {
2093
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2094
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2095
						$this->_debug($data);
2096
					}
2097
					break;
2098
				case 'route53':
2099
				case 'route53-v6':
2100
					if(preg_match('/ErrorResponse/', $data)){
2101
						$status = $status_intro . $error_str . gettext("Route53 API call failed");
2102
						log_error(sprintf("error message: %s", $data));
2103
						$status_update = false;
2104
					} else {
2105
						$status = $status_intro . $success_str . gettext("IP address changed successfully");
2106
						$successful_update = true;
2107
					}
2108
					break;
2109
				case 'custom':
2110
				case 'custom-v6':
2111
					$successful_update = false;
2112
					if ($this->_dnsResultMatch == "") {
2113
						$successful_update = true;
2114
					} else {
2115
						$this->_dnsResultMatch = str_replace("%IP%", $this->_dnsIP, $this->_dnsResultMatch);
2116
						$matches = preg_split("/(?<!\\\\)\\|/", $this->_dnsResultMatch);
2117
						foreach ($matches as $match) {
2118
							$match= str_replace("\\|", "|", $match);
2119
							if (strcmp($match, trim($data, "\t\n\r")) == 0) {
2120
								$successful_update = true;
2121
							}
2122
						}
2123
						unset ($matches);
2124
					}
2125
					if ($successful_update == true) {
2126
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2127
					} else {
2128
						$status = $status_intro . $error_str . gettext("Result did not match.") . " [" . $data . "]";
2129
					}
2130
					break;
2131
				case 'cloudflare-v6':
2132
				case 'cloudflare':
2133
					$output = json_decode($data);
2134
					if ($output->result->content === $this->_dnsIP) {
2135
						$status = $status_intro . $success_str . sprintf(gettext('%1$s updated to %2$s'), $this->_dnsHost, $this->_dnsIP);
2136
						$successful_update = true;
2137
					} elseif ($output->errors[0]->code === 9103) {
2138
						$status = $status_intro . $error_str . gettext("Invalid Credentials! Don't forget to use API Key for password field with Cloudflare.");
2139
					} elseif (($output->success) && (!$output->result[0]->id)) {
2140
						$status = $status_intro . $error_str . gettext("Zone or Host ID was not found, check the hostname.");
2141
					} else {
2142
						$status = $status_intro . gettext("UNKNOWN ERROR") . " - " . $output->errors[0]->message;
2143
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2144
					}
2145
					break;
2146
				case 'eurodns':
2147
					if (preg_match('/notfqdn/i', $data)) {
2148
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2149
					} else if (preg_match('/nochg/i', $data)) {
2150
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2151
						$successful_update = true;
2152
					} else if (preg_match('/good/i', $data)) {
2153
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2154
						$successful_update = true;
2155
					} else if (preg_match('/badauth/i', $data)) {
2156
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2157
					} else {
2158
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2159
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2160
						$this->_debug($data);
2161
					}
2162
					break;
2163
				case 'gratisdns':
2164
					if (preg_match('/Forkerte værdier/i', $data)) {
2165
						$status = $status_intro . $error_str . gettext("Wrong values - Update could not be completed.");
2166
					} else if (preg_match('/Bruger login: Bruger eksistere ikke/i', $data)) {
2167
						$status = $status_intro . $error_str . gettext("Unknown username - User does not exist.");
2168
					} else if (preg_match('/Bruger login: 1Fejl i kodeord/i', $data)) {
2169
						$status = $status_intro . $error_str . gettext("Wrong password - Remember password is case sensitive.");
2170
					} else if (preg_match('/Domæne kan IKKE administreres af bruger/i', $data)) {
2171
						$status = $status_intro . $error_str . gettext("User unable to administer the selected domain.");
2172
					} else if (preg_match('/OK/i', $data)) {
2173
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2174
						$successful_update = true;
2175
					} else {
2176
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2177
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2178
						$this->_debug($data);
2179
					}
2180
					break;
2181
				case 'digitalocean':
2182
				case 'digitalocean-v6':
2183
					// Creating new records returns an HTTP 201, updating existing records get 200
2184
					// https://redmine.pfsense.org/issues/9171
2185
					if (preg_match("/HTTP\/2\s20[0,1]/i", $header)) {
2186
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2187
						$successful_update = true;
2188
					} else {
2189
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2190
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2191
						$this->_debug($data);
2192
					}
2193
					break;
2194
				case 'dnsimple':
2195
					/* Responds with HTTP 200 on success.
2196
					   Responds with HTTP 4xx on error.
2197
					   Returns JSON data as body */
2198
;
2199
                                        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2200
					
2201
					if ($code == "200") {
2202
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2203
						$successful_update = true;
2204
					} else if (preg_match("/4\d\d/i", $code)) {
2205
						$arrbody = json_decode($data, true);
2206
						$message = $arrbody['message'] . ".";
2207
						if (isset($arrbody['errors']['content'])) {
2208
							foreach ($arrbody['errors']['content'] as $key => $content) {
2209
								$message .= " " . $content . ".";
2210
							}
2211
						}
2212
						$status = $status_intro . $error_str . $message;
2213
					} else {
2214
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $code . ")";
2215
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2216
						$this->_debug($data);
2217
					}
2218
					break;
2219
				case 'godaddy':
2220
				case 'godaddy-v6':
2221
					/* Responds with HTTP 200 on success.
2222
					   Responds with HTTP 4xx or  on error.
2223
					   Returns JSON data as body */
2224
;
2225
					if (preg_match("/\s200\sOK/i", $header)) {
2226
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2227
						$successful_update = true;
2228
					} else if (preg_match("/\s4\d\d\s/i", $header)) {
2229
						$arrbody = json_decode($data, true);
2230
						$message = $arrbody['message'] . ".";
2231
						if (isset($arrbody['fields'])) {
2232
							foreach ($arrbody['fields'] as $error) {
2233
								$message .= " " . $error['path'] . ": " . $error['message'] . ".";
2234
							}
2235
						}
2236
						$status = $status_intro . $error_str . $message;
2237
					} else {
2238
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2239
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2240
						$this->_debug($data);
2241
					}
2242
					break;
2243
				case 'googledomains':
2244
					if (preg_match('/notfqdn/i', $data)) {
2245
						$status = $status_intro . $error_str . gettext("Not A FQDN");
2246
					} else if (preg_match('/nochg/i', $data)) {
2247
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2248
						$successful_update = true;
2249
					} else if (preg_match('/good/i', $data)) {
2250
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2251
						$successful_update = true;
2252
					} else if (preg_match('/badauth/i', $data)) {
2253
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2254
					} else if (preg_match('/nohost/i', $data)) {
2255
						$status = $status_intro . $error_str . gettext("Hostname does not exist or DynDNS not enabled");
2256
					} else if (preg_match('/badagent/i', $data)) {
2257
						$status = $status_intro . $error_str . gettext("Bad request");
2258
					} else if (preg_match('/abuse/i', $data)) {
2259
						$status = $status_intro . $error_str . gettext("Dynamic DNS access has been blocked!");
2260
					} else if (preg_match('/911/i', $data)) {
2261
						$status = $status_intro . $error_str . gettext("Error on Google's end, retry in 5 minutes");
2262
					} else {
2263
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2264
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2265
						$this->_debug($data);
2266
					}
2267
					break;
2268
				case 'dnsmadeeasy':
2269
					switch ($data) {
2270
						case 'success':
2271
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2272
							$successful_update = true;
2273
							break;
2274
						case 'error-auth':
2275
							$status = $status_intro . $error_str . gettext("Invalid username or password");
2276
							break;
2277
						case 'error-auth-suspend':
2278
							$status = $status_intro . $error_str . gettext("Account suspended");
2279
							break;
2280
						case 'error-auth-voided':
2281
							$status = $status_intro . $error_str . gettext("Account revoked");
2282
							break;
2283
						case 'error-record-invalid':
2284
							$status = $status_intro . $error_str . gettext("Record does not exist in the system. Unable to update record");
2285
							break;
2286
						case 'error-record-auth':
2287
							$status = $status_intro . $error_str . gettext("User does not have access to this record");
2288
							break;
2289
						case 'error-record-ip-same':
2290
							$status = $status_intro . $success_str . gettext("No Change In IP Address");
2291
							$successful_update = true;
2292
							break;
2293
						case 'error-system':
2294
							$status = $status_intro . $error_str . gettext("General system error recognized by the system");
2295
							break;
2296
						case 'error':
2297
							$status = $status_intro . $error_str . gettext("General system error unrecognized by the system");
2298
							break;
2299
						default:
2300
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2301
							log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2302
							$this->_debug($data);
2303
							break;
2304
					}
2305
					break;
2306
				case 'spdyn':
2307
				case 'spdyn-v6':
2308
					if (preg_match('/notfqdn/i', $data)) {
2309
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2310
					} else if (preg_match('/nohost/i', $data)) {
2311
						$status = $status_intro . $error_str . gettext("No such host");
2312
					} else if (preg_match('/nochg/i', $data)) {
2313
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2314
						$successful_update = true;
2315
					} else if (preg_match('/good/i', $data)) {
2316
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2317
						$successful_update = true;
2318
					} else if (preg_match('/badauth/i', $data)) {
2319
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2320
					} else {
2321
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2322
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2323
						$this->_debug($data);
2324
					}
2325
					break;
2326
				case 'all-inkl':
2327
 					if (preg_match('/good\s' . $this->_dnsIP . '/i', $data)) {
2328
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2329
 							$successful_update = true;
2330
 					} else if (preg_match('/good/i', $data)) {
2331
						$status = $status_intro . $error_str . gettext("Result did not match.");
2332
					} else if (preg_match("/\s401\sUnauthorized/i", $header)) {
2333
						$status = $status_intro . $error_str . gettext("Invalid username or password");
2334
					}
2335
					else {
2336
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2337
							log_error($status_intro . gettext("PAYLOAD:") . " " . $header . $data);
2338
 							$this->_debug($data);
2339
 							$this->_debug($header);
2340
 					}
2341
 					break;
2342
				case 'hover':
2343
					if (preg_match('/succeeded":true/i', $data)) {
2344
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2345
						$successful_update = true;
2346
					} else {
2347
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2348
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2349
						$this->_debug($data);
2350
					}
2351
					break;
2352
				case 'cloudns':
2353
					$result = json_decode($data, true);
2354
					if ($result['status'] == 'Success') {
2355
						$successful_update = true;
2356
					} else {
2357
						log_error($result['status'] . "(" . $result['statusDescription'] . ")");
2358
					}
2359
					break;
2360
				case 'dreamhost':
2361
				case 'dreamhost-v6':
2362
					$result = json_decode($data,true);
2363
					if ($this->_dnsVerboseLog) {
2364
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2365
					}
2366
					switch ($result['data']) {
2367
					case 'success':
2368
					case 'record_added':
2369
					case 'record_removed':
2370
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2371
						$successful_update = true;
2372
						break;
2373
					case 'no_record':
2374
					case 'no_such_record ':
2375
						$status = $status_intro . $error_str . gettext("No record exists.");
2376
						break;
2377
					case 'no_type':
2378
					case 'no_such_type ':
2379
						$status = $status_intro . $error_str . gettext("No type exists.");
2380
						break;
2381
					case 'no_value':
2382
					case 'no_such_value ':
2383
						$status = $status_intro . $error_str . gettext("No value exists.");
2384
						break;
2385
					case 'no_such_zone':
2386
						$status = $status_intro . $error_str . gettext("No such zone exists.");
2387
						break;
2388
					case 'invalid_record':
2389
						$status = $status_intro . $error_str . gettext("The specified record is invalid.");
2390
						break;
2391
					case 'invalid_type':
2392
						$status = $status_intro . $error_str . gettext("The specified type is invalid.");
2393
						break;
2394
					case 'invalid_value':
2395
						$status = $status_intro . $error_str . gettext("The specified value is invalid.");
2396
						break;
2397
					case 'not_editable ':
2398
						$status = $status_intro . $error_str . gettext("Record is not editable.");
2399
						break;
2400
					case 'record_already_exists_not_editable':
2401
						$status = $status_intro . $error_str . gettext("Record exists but is not editable.");
2402
						break;
2403
					case 'record_already_exists_remove_first':
2404
						$status = $status_intro . $error_str . gettext("Record exists and must be removed before adding.");
2405
						break;
2406
					case 'internal_error_updating_zone':
2407
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2408
						break;
2409
					case 'internal_error_could_not_load_zone':
2410
						$status = $status_intro . $error_str . gettext("A remote server error occurred loading the zone.");
2411
						break;
2412
					case 'internal_error_could_not_update_zone':
2413
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2414
						break;
2415
					case 'internal_error_could_not_add_record':
2416
						$status = $status_intro . $error_str . gettext("A remote server error occurred adding a new record.");
2417
						break;
2418
					case 'internal_error_could_not_destroy_record ':
2419
						$status = $status_intro . $error_str . gettext("A remote server error occurred removing an existing record.");
2420
						break;
2421
					default:
2422
						break;
2423
					}
2424
				case 'azure':
2425
				case 'azurev6':
2426
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2427
					if ($http_code == 401) {
2428
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2429
					} else if ($http_code == 201) {
2430
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2431
						$successful_update = true;
2432
					} else if ($http_code == 200) {
2433
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2434
						$successful_update = true;
2435
					} else {
2436
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2437
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2438
						$this->_debug($data);
2439
					}
2440
					break;
2441
				case 'linode':
2442
				case 'linode-v6':
2443
					$status_intro = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): ";
2444
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2445
					$result = json_decode($data,true);
2446
					if ($this->_dnsVerboseLog) {
2447
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2448
					}
2449
					if ($http_code == 200 && isset($result["id"]) && ! isset($result["errors"])) {
2450
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2451
						$successful_update = true;
2452
					} else if ( $http_code == 401 && preg_match('/not authorized to use/i', $data) ) {
2453
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2454
					} else {
2455
						$status = $status_intro . $error_str .
2456
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" );
2457
					}
2458
					break;
2459
				case 'gandi-livedns':
2460
					// NOTE: same as azure
2461
					// Creating new records returns an HTTP 201, updating existing records get 200
2462
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2463
					if ($http_code == 401) {
2464
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2465
					} else if ($http_code == 201) {
2466
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2467
						$successful_update = true;
2468
					} else if ($http_code == 200) {
2469
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2470
						$successful_update = true;
2471
					} else {
2472
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2473
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2474
						$this->_debug($data);
2475
					}
2476
					break;
2477
				default:
2478
					break;
2479
			}
2480

    
2481
			if ($successful_update == true) {
2482
				/* Write WAN IP to cache file */
2483
				$wan_ip = $this->_checkIP();
2484
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2485
					$currentTime = time();
2486
					notify_all_remote(sprintf(gettext('DynDNS updated IP Address on %1$s (%2$s) to %3$s'), convert_real_interface_to_friendly_descr($this->_if), $this->_if, $wan_ip));
2487
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2488
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2489
				} else {
2490
					@unlink($this->_cacheFile);
2491
				}
2492
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2493
					$currentTime = time();
2494
					notify_all_remote(sprintf(gettext('DynDNS updated IPv6 Address on %1$s (%2$s) to %3$s'), convert_real_interface_to_friendly_descr($this->_if), $this->_if, $wan_ip));
2495
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2496
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2497
				} else {
2498
					@unlink($this->_cacheFile_v6);
2499
				}
2500
			}
2501
			$this->status = $status;
2502
			log_error($status);
2503
		}
2504

    
2505
		/*
2506
		 * Private Function (added 12 July 05) [beta]
2507
		 *   Return Error, Set Last Error, and Die.
2508
		 */
2509
		function _error($errorNumber = '1') {
2510
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2511
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2512
			switch ($errorNumber) {
2513
				case 0:
2514
					break;
2515
				case 2:
2516
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2517
					break;
2518
				case 3:
2519
					$error = $err_str . gettext('No Username Provided.');
2520
					break;
2521
				case 4:
2522
					$error = $err_str . gettext('No Password Provided.');
2523
					break;
2524
				case 5:
2525
					$error = $err_str . gettext('No Hostname Provided.');
2526
					break;
2527
				case 6:
2528
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2529
					break;
2530
				case 7:
2531
					$error = $err_str . gettext('No Update URL Provided.');
2532
					break;
2533
				case 8:
2534
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2535
					break;
2536
				case 9:
2537
					$status = $err_str_r53 . gettext("Invalid TTL");
2538
					break;
2539
				case 10:
2540
					$error = "phpDynDNS ({$this->_FQDN}): " . sprintf(gettext("No change in my IP address and/or %s days has not passed. Not updating dynamic DNS entry."), $this->_dnsMaxCacheAgeDays);
2541
					break;
2542
				default:
2543
					$error = $err_str . gettext('Unknown Response.');
2544
					/* FIXME: $data isn't in scope here */
2545
					/* $this->_debug($data); */
2546
					break;
2547
			}
2548
			$this->lastError = $error;
2549
			log_error($error);
2550
		}
2551

    
2552
		/*
2553
		 * Private Function (added 12 July 05) [beta]
2554
		 *   - Detect whether or not IP needs to be updated.
2555
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2556
		 *      | work with other systems. pfSense base is FreeBSD.
2557
		 */
2558
		function _detectChange() {
2559
			global $debug;
2560

    
2561
			if ($debug) {
2562
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2563
			}
2564

    
2565
			$currentTime = time();
2566

    
2567
			$wan_ip = $this->_checkIP();
2568
			if ($wan_ip == 0) {
2569
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2570
				return false;
2571
			}
2572
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
2573

    
2574
			if ($this->_useIPv6 == true) {
2575
				if (file_exists($this->_cacheFile_v6)) {
2576
					$contents = file_get_contents($this->_cacheFile_v6);
2577
					list($cacheIP, $cacheTime) = explode('|', $contents);
2578
					$this->_debug($cacheIP . '/' . $cacheTime);
2579
					$initial = false;
2580
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
2581
				} else {
2582
					$cacheIP = '::';
2583
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
2584
					$cacheTime = $currentTime;
2585
					$initial = true;
2586
					$log_error .= gettext("No Cached IPv6 found.");
2587
				}
2588
			} else {
2589
				if (file_exists($this->_cacheFile)) {
2590
					$contents = file_get_contents($this->_cacheFile);
2591
					list($cacheIP, $cacheTime) = explode('|', $contents);
2592
					$this->_debug($cacheIP . '/' . $cacheTime);
2593
					$initial = false;
2594
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
2595
				} else {
2596
					$cacheIP = '0.0.0.0';
2597
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
2598
					$cacheTime = $currentTime;
2599
					$initial = true;
2600
					$log_error .= gettext("No Cached IP found.");
2601
				}
2602
			}
2603
			if ($this->_dnsVerboseLog) {
2604
				log_error($log_error);
2605
			}
2606

    
2607
			// Convert seconds = days * hr/day * min/hr * sec/min
2608
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60;
2609

    
2610
			$needs_updating = FALSE;
2611
			/* lets determine if the item needs updating */
2612
			if ($cacheIP != $wan_ip) {
2613
				$needs_updating = true;
2614
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
2615
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
2616
			}
2617
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
2618
				$needs_updating = true;
2619
				$this->_forceUpdateNeeded = true;
2620
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
2621
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
2622
			}
2623
			if ($initial == true) {
2624
				$needs_updating = true;
2625
				$update_reason .= gettext("Initial update.");
2626
			}
2627

    
2628
			/*   finally if we need updating then store the
2629
			 *   new cache value and return true
2630
			 */
2631
			if ($needs_updating == true) {
2632
				if ($this->_dnsVerboseLog) {
2633
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
2634
				}
2635
				return true;
2636
			}
2637

    
2638
			return false;
2639
		}
2640

    
2641
		/*
2642
		 * Private Function (added 16 July 05) [beta]
2643
		 *   - Writes debug information to a file.
2644
		 *   - This function is only called when a unknown response
2645
		 *   - status is returned from a DynDNS service provider.
2646
		 */
2647
		function _debug($data) {
2648
			global $g;
2649

    
2650
			if (!$g['debug']) {
2651
				return;
2652
			}
2653
			$string = date('m-d-y h:i:s') . ' - (' . $this->_debugID . ') - [' . $this->_dnsService . '] - ' . $data . "\n";
2654
			$file = fopen($this->_debugFile, 'a');
2655
			fwrite($file, $string);
2656
			fclose($file);
2657
		}
2658
		function _checkIP() {
2659
			global $debug;
2660

    
2661
			if ($debug) {
2662
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
2663
			}
2664

    
2665
			if ($this->_useIPv6 == true) {
2666
				$ip_address = get_interface_ipv6($this->_if);
2667
				if (!is_ipaddrv6($ip_address)) {
2668
					return 0;
2669
				}
2670
			} else {
2671
				$ip_address = dyndnsCheckIP($this->_if);
2672
				if (!is_ipaddr($ip_address)) {
2673
					return 0;
2674
				}
2675
			}
2676
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
2677
				if (is_ipaddr($ip_address)) {
2678
					if ($this->_dnsVerboseLog) {
2679
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
2680
					}
2681
				} else {
2682
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
2683
					return 0;
2684
				}
2685
			} else {
2686
				if ($this->_dnsVerboseLog) {
2687
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
2688
				}
2689
			}
2690
			$this->_dnsIP = $ip_address;
2691

    
2692
			return $ip_address;
2693
		}
2694

    
2695
	}
2696

    
2697
?>
(15-15/61)