Project

General

Profile

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

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

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

    
198
			global $config, $g, $dyndns_split_domain_types;
199
			if (in_array($dnsService, $dyndns_split_domain_types)) {
200
				$this->_FQDN = $dnsHost . "." . $dnsDomain;
201
			} else {
202
				$this->_FQDN = $dnsHost;
203
			}
204

    
205
			$this->_cacheFile = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.cache";
206
			$this->_cacheFile_v6 = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}_v6.cache";
207
			$this->_debugFile = "{$g['varetc_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.debug";
208

    
209
			$this->_curlIpresolveV4 = $curlIpresolveV4;
210
			$this->_curlSslVerifypeer = $curlSslVerifypeer;
211
			$this->_dnsVerboseLog = $dnsVerboseLog;
212
			if ($this->_dnsVerboseLog) {
213
				log_error(gettext("Dynamic DNS: updatedns() starting"));
214
			}
215

    
216
			$dyndnslck = lock("DDNS" . $dnsID, LOCK_EX);
217

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

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

    
331
			// Ensure that we were able to lookup the IP
332
			if (!is_ipaddr($this->_dnsIP)) {
333
				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));
334
				unlock($dyndnslck);
335
				return;
336
			}
337

    
338
			$this->_debugID = rand(1000000, 9999999);
339

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

    
428
			unlock($dyndnslck);
429
		}
430

    
431
		/*
432
		 * Private Function (added 12 July 05) [beta]
433
		 *   Send Update To Selected Service.
434
		 */
435
		function _update() {
436

    
437
			if ($this->_dnsVerboseLog) {
438
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
439
			}
440

    
441
			if (strstr($this->_dnsRequestIf, "_vip")) {
442
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
443
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
444
			} else {
445
				$realparentif = $this->_dnsRequestIf;
446
			}
447

    
448
			$ch = curl_init();
449

    
450
			if ($this->_useIPv6 == false) {
451
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
452
			}
453

    
454
			if ($this->_dnsService != 'ods') {
455
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
456
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
457
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
458
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
459
			}
460

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

    
602
					$server = "https://dynamic.zoneedit.com/auth/dynamic.html";
603
					$port = "";
604
					if ($this->_dnsServer) {
605
						$server = $this->_dnsServer;
606
					}
607
					if ($this->_dnsPort) {
608
						$port = ":" . $this->_dnsPort;
609
					}
610
					curl_setopt($ch, CURLOPT_URL, "{$server}{$port}?host=" . $this->_dnsHost . '&dnsto=' . $this->_dnsIP);
611
					break;
612
				case 'dyns':
613
					$needsIP = FALSE;
614
					$server = "http://www.dyns.net/postscript011.php";
615
					$port = "";
616
					if ($this->_dnsServer) {
617
						$server = $this->_dnsServer;
618
					}
619
					if ($this->_dnsPort) {
620
						$port = ":" . $this->_dnsPort;
621
					}
622
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost);
623
					break;
624
				case 'ods':
625
					$needsIP = FALSE;
626
					$misc_errno = 0;
627
					$misc_error = "";
628
					$server = "ods.org";
629
					$port = "";
630
					if ($this->_dnsServer) {
631
						$server = $this->_dnsServer;
632
					}
633
					if ($this->_dnsPort) {
634
						$port = ":" . $this->_dnsPort;
635
					}
636
					$this->con['socket'] = fsockopen("{$server}{$port}", "7070", $misc_errno, $misc_error, 30);
637
					/* Check that we have connected */
638
					if (!$this->con['socket']) {
639
						print "error! could not connect.";
640
						break;
641
					}
642
					/* Here is the loop. Read the incoming data (from the socket connection) */
643
					while (!feof($this->con['socket'])) {
644
						$this->con['buffer']['all'] = trim(fgets($this->con['socket'], 4096));
645
						$code = substr($this->con['buffer']['all'], 0, 3);
646
						sleep(1);
647
						switch ($code) {
648
							case 100:
649
								fputs($this->con['socket'], "LOGIN " . $this->_dnsUser . " " . $this->_dnsPass . "\n");
650
								break;
651
							case 225:
652
								fputs($this->con['socket'], "DELRR " . $this->_dnsHost . " A\n");
653
								break;
654
							case 901:
655
								fputs($this->con['socket'], "ADDRR " . $this->_dnsHost . " A " . $this->_dnsIP . "\n");
656
								break;
657
							case 795:
658
								fputs($this->con['socket'], "QUIT\n");
659
								break;
660
						}
661
					}
662
					$this->_checkStatus(0, $code);
663
					break;
664
				case 'freedns':
665
				case 'freedns-v6':
666
					$needIP = TRUE;
667
					curl_setopt($ch, CURLOPT_URL, 'https://freedns.afraid.org/dynamic/update.php?' . $this->_dnsPass . '&address=' . $this->_dnsIP);
668
					break;
669
				case 'dnsexit':
670
					$needsIP = TRUE;
671
					curl_setopt($ch, CURLOPT_URL, 'https://update.dnsexit.com/RemoteUpdate.sv?login=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
672
					break;
673
				case 'loopia':
674
					$needsIP = TRUE;
675
					if(isset($this->_dnsWildcard) && $this->_dnsWildcard == TRUE) {
676
						$this->_dnsWildcard = "ON";
677
					} else {
678
						$this->_dnsWildcard = "OFF";
679
					}
680
					curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
681
					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");
682
					break;
683
				case 'opendns':
684
					$needsIP = FALSE;
685
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
686
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
687
					$server = "https://updates.opendns.com/nic/update?hostname=" . $this->_dnsHost;
688
					$port = "";
689
					if ($this->_dnsServer) {
690
						$server = $this->_dnsServer;
691
					}
692
					if ($this->_dnsPort) {
693
						$port = ":" . $this->_dnsPort;
694
					}
695
					curl_setopt($ch, CURLOPT_URL, $server . $port);
696
					break;
697

    
698
				case 'staticcling':
699
					$needsIP = FALSE;
700
					curl_setopt($ch, CURLOPT_URL, 'https://www.staticcling.org/update.html?login=' . $this->_dnsUser . '&pass=' . $this->_dnsPass);
701
					break;
702
				case 'dnsomatic':
703
					/* Example syntax
704
						https://username:password@updates.dnsomatic.com/nic/update?hostname=yourhostname&myip=ipaddress&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG
705
					*/
706
					$needsIP = FALSE;
707
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
708
						$this->_dnsWildcard = "ON";
709
					}
710
					/*
711
					Reference: https://www.dnsomatic.com/wiki/api
712
						DNS-O-Matic usernames are 3-25 characters.
713
						DNS-O-Matic passwords are 6-20 characters.
714
						All ASCII letters and numbers accepted.
715
						Dots, dashes, and underscores allowed, but not at the beginning or end of the string.
716
					Required: "rawurlencode" http://www.php.net/manual/en/function.rawurlencode.php
717
						Encodes the given string according to RFC 3986.
718
					*/
719
					$server = "https://" . rawurlencode($this->_dnsUser) . ":" . rawurlencode($this->_dnsPass) . "@updates.dnsomatic.com/nic/update?hostname=";
720
					if ($this->_dnsServer) {
721
						$server = $this->_dnsServer;
722
					}
723
					if ($this->_dnsPort) {
724
						$port = ":" . $this->_dnsPort;
725
					}
726
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NOCHG');
727
					break;
728
				case 'namecheap':
729
					/* Example:
730
						https://dynamicdns.park-your-domain.com/update?host=[host_name]&domain=[domain.com]&password=[domain_password]&ip=[your_ip]
731
					*/
732
					$needsIP = FALSE;
733
					$dnspass = trim($this->_dnsPass);
734
					$server = "https://dynamicdns.park-your-domain.com/update?host={$this->_dnsHost}&domain={$this->_dnsDomain}&password={$dnspass}&ip={$this->_dnsIP}";
735
					curl_setopt($ch, CURLOPT_URL, $server);
736
					break;
737
				case 'duiadns':
738
				case 'duiadns-v6':
739
					$needsIP = FALSE;
740
					$server = "https://ipv4.duiadns.net/dyndns.duia?";
741
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
742
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
743
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
744
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
745
					break;
746
				case 'he-net':
747
				case 'he-net-v6':
748
					$needsIP = FALSE;
749
					$server = "https://dyn.dns.he.net/nic/update?";
750
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
751
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
752
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&password=' . $this->_dnsPass . '&myip=' . $this->_dnsIP);
753
					break;
754
				case 'he-net-tunnelbroker':
755
					$needsIP = FALSE;
756
					$server = "https://ipv4.tunnelbroker.net/ipv4_end.php?";
757
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
758
					curl_setopt($ch, CURLOPT_URL, $server . 'tid=' . $this->_dnsHost);
759
					break;
760
				case 'selfhost':
761
					$needsIP = FALSE;
762
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
763
						$this->_dnsWildcard = "ON";
764
					}
765
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
766
					$server = "https://carol.selfhost.de/nic/update";
767
					$port = "";
768
					if ($this->_dnsServer) {
769
						$server = $this->_dnsServer;
770
					}
771
					if ($this->_dnsPort) {
772
						$port = ":" . $this->_dnsPort;
773
					}
774
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
775
					break;
776
				case 'route53':
777
					require_once("r53.class");
778
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
779
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
780
					$xmlreq = $r53->getRequestBody($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
781
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
782
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
783
					if($this->_dnsVerboseLog){
784
						log_error(sprintf("Sending request to: %s", $apiurl));
785
						foreach($httphead as $hv){
786
							log_error(sprintf("Header: %s", $hv));
787
						}
788
						log_error(sprintf("XMLPOST: %s", $xmlreq));
789
					}
790
					curl_setopt($ch, CURLOPT_URL, $apiurl);
791
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
792
					break;
793
				case 'route53-v6':
794
					require_once("r53.class");
795
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
796
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
797
					$xmlreq = $r53->getRequestBodyV6($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
798
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
799
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
800
					if($this->_dnsVerboseLog){
801
						log_error(sprintf("Sending request to: %s", $apiurl));
802
						foreach($httphead as $hv){
803
							log_error(sprintf("Header: %s", $hv));
804
						}
805
						log_error(sprintf("XMLPOST: %s", $xmlreq));
806
					}
807
					curl_setopt($ch, CURLOPT_URL, $apiurl);
808
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
809
					break;
810
				case 'custom':
811
				case 'custom-v6':
812
					if (strstr($this->dnsUpdateURL, "%IP%")) {$needsIP = TRUE;} else {$needsIP = FALSE;}
813
					if ($this->_dnsUser != '') {
814
						if ($this->_curlIpresolveV4) {
815
							curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
816
						}
817
						if ($this->_curlSslVerifypeer) {
818
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
819
						} else {
820
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
821
						}
822
						curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
823
					}
824
					$server = str_replace("%IP%", $this->_dnsIP, $this->_dnsUpdateURL);
825
					if ($this->_dnsVerboseLog) {
826
						log_error(sprintf(gettext("Sending request to: %s"), $server));
827
					}
828
					curl_setopt($ch, CURLOPT_URL, $server);
829
					break;
830
				case 'cloudflare-v6':
831
				case 'cloudflare':
832
					$this->_FQDN = ltrim($this->_FQDN, '@.');
833
					$isv6 = ($this->_dnsService === 'cloudflare-v6');
834
					$recordType = $isv6 ? "AAAA" : "A";
835
					$needsIP = TRUE;
836
					$dnsServer ='api.cloudflare.com';
837
					$dnsHost = str_replace(' ', '', $this->_dnsHost);
838

    
839
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
840

    
841
					if (strpos($this->_dnsUser, '@') !== false) {
842
						curl_setopt($ch, CURLOPT_HTTPHEADER, array(
843
							'X-Auth-Email: ' . $this->_dnsUser,
844
							'X-Auth-Key: ' . $this->_dnsPass,
845
							'Content-Type: application/json'
846
						));
847

    
848
						// Get zone ID
849
						$getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$this->_dnsDomain}";
850
						curl_setopt($ch, CURLOPT_URL, $getZoneId);
851
						$output = json_decode(curl_exec($ch));
852
						$zone = $output->result[0]->id;
853
					} else {
854
						curl_setopt($ch, CURLOPT_HTTPHEADER, array(
855
							'Authorization: Bearer ' . $this->_dnsPass,
856
							'Content-Type: application/json'
857
						));
858

    
859
						$zone = $this->_dnsUser;
860
					}
861

    
862
					if ($zone) { // If zone ID was found get host ID
863
						$getHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records?name={$this->_FQDN}&type={$recordType}";
864
						curl_setopt($ch, CURLOPT_URL, $getHostId);
865
						$output = json_decode(curl_exec($ch));
866
						$host = $output->result[0]->id;
867
						if ($host) { // If host ID was found update host
868
							$hostData = array(
869
								"content" => "{$this->_dnsIP}",
870
								"type" => "{$recordType}",
871
								"proxied" => $this->_dnsProxied,
872
								"name" => "{$this->_dnsHost}",
873
								"ttl" => empty($this->_dnsTTL) ? 1 : (int) $this->_dnsTTL
874
							);
875
							$data_json = json_encode($hostData);
876
							$updateHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records/{$host}";
877
							curl_setopt($ch, CURLOPT_URL, $updateHostId);
878
							curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
879
							curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
880
						}
881
					}
882
					break;
883
				case 'eurodns':
884
					$needsIP = TRUE;
885
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
886
					$server = "https://update.eurodyndns.org/update/";
887
					$port = "";
888
					if ($this->_dnsPort) {
889
						$port = ":" . $this->_dnsPort;
890
					}
891
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
892
					break;
893
				case 'gratisdns':
894
					$needsIP = TRUE;
895
					$server = "https://ssl.gratisdns.dk/ddns.phtml";
896
					curl_setopt($ch, CURLOPT_URL, $server . '?u=' . $this->_dnsUser . '&p=' . $this->_dnsPass . '&h=' . $this->_dnsHost . '&d=' . $this->_dnsDomain . '&i=' . $this->_dnsIP);
897
					break;
898
				case 'ovh-dynhost':
899
					$needsIP = FALSE;
900
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
901
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
902
					$server = "https://www.ovh.com/nic/update";
903
					$port = "";
904
					if ($this->_dnsServer) {
905
						$server = $this->_dnsServer;
906
					}
907
					if ($this->_dnsPort) {
908
						$port = ":" . $this->_dnsPort;
909
					}
910
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
911
					break;
912
				case 'citynetwork':
913
					$needsIP = TRUE;
914
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
915
					$server = 'https://dyndns.citynetwork.se/nic/update';
916
					$port = "";
917
					if ($this->_dnsServer) {
918
						$server = $this->_dnsServer;
919
					}
920
					if ($this->_dnsPort) {
921
						$port = ":" . $this->_dnsPort;
922
					}
923
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
924
					break;
925
				case 'dnsimple':
926
					/* Uses DNSimple's v2 REST API
927
					   Requires the Account ID as the username (found in the URL when pull up the domain)
928
					   And an API Token for the password (generated in the User Settings -> API tokens area of the website)
929
					   Piggybacks on Route 53's ZoneID field for the DNSimple record ID to update
930
					   The DNS record MUST exist before it can update since it performs a PATCH operation
931
					   Data sent as JSON over HTTPS */
932
					$needsIP = TRUE;
933
					$server = 'https://api.dnsimple.com/v2/';
934
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
935
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-Type: application/json', 'Authorization: Bearer ' . $this->_dnsPass));
936
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsUser . '/zones/' . $this->_dnsHost . '/records/' . $this->_dnsZoneID);
937
					curl_setopt($ch, CURLOPT_POSTFIELDS, '{"content":"' . $this->_dnsIP . '","ttl":"' . $this->_dnsTTL . '"}');
938
					break;
939
				case 'godaddy':
940
				case 'godaddy-v6':
941
					/* Uses GoDaddy's REST API
942
					   Requires username and Account API sso-key passed in header
943
					   Data sent as JSON */
944
					$needsIP = TRUE;
945
					$server = 'https://api.godaddy.com/v1/domains/';
946
					$recordType = $this->_useIPv6 ? "AAAA" : "A";
947
					$url = $server . $this->_dnsDomain . '/records/' . $recordType . '/' . $this->_dnsHost;
948
					$jsondata = '[{"data":"' . $this->_dnsIP . '","ttl":' . $this->_dnsTTL . '}]';
949
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
950
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
951
						'Accept: application/json',
952
						'Content-Type: application/json',
953
						'Authorization: sso-key ' . $this->_dnsUser . ':' . $this->_dnsPass
954
					));
955
					curl_setopt($ch, CURLOPT_URL, $url);
956
					curl_setopt($ch, CURLOPT_POSTFIELDS, $jsondata);
957
					break;
958
				case 'googledomains':
959
					$needsIP = FALSE;
960
					$post_data['username:password'] = $this->_dnsUser . ':' . $this->_dnsPass;
961
					$post_data['hostname'] = $this->_dnsHost;
962
					$post_data['myip'] = $this->_dnsIP;
963
					$post_data['offline'] = 'no';
964
					$server = "https://domains.google.com/nic/update";
965
					$port = "";
966
					curl_setopt($ch, CURLOPT_URL, 'https://domains.google.com/nic/update');
967
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
968
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
969
					break;
970
				case 'dnsmadeeasy':
971
					$needsIP = TRUE;
972
					$server = "https://cp.dnsmadeeasy.com/servlet/updateip";
973
					curl_setopt($ch, CURLOPT_URL, $server . '?username=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&id=' . $this->_dnsHost . '&ip=' . $this->_dnsIP);
974
					break;
975
				case 'spdyn':
976
				case 'spdyn-v6':
977
					$needsIP = FALSE;
978
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
979
					$server = "https://update.spdyn.de/nic/update";
980
					$port = "";
981
					if ($this->_dnsServer) {
982
						$server = $this->_dnsServer;
983
					}
984
					if ($this->_dnsPort) {
985
						$port = ":" . $this->_dnsPort;
986
					}
987
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
988
					break;
989
				case 'all-inkl':
990
					$needsIP = FALSE;
991
					$server = 'https://dyndns.kasserver.com/';
992
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
993
					curl_setopt($ch, CURLOPT_URL, $server . 'myip=' . $this->_dnsIP);
994
					break;
995
				case 'hover':
996
					$needsIP = FALSE;
997
					$port = "";
998
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
999
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1000

    
1001
					//step 1: login to API
1002
					$post_data['username'] = $this->_dnsUser;
1003
					$post_data['password'] = $this->_dnsPass;
1004
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1005
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
1006
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1007
					$output = curl_exec($ch);
1008

    
1009
					//extract the cookies
1010
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1011
					if( count($cookies[1]) > 0 ){
1012
						$cookie_data = implode("; ",$cookies[1]);
1013
					}
1014

    
1015
					//step 2: find the id of the A record
1016
					$post_data = null;
1017
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1018
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1019
					curl_setopt($ch, CURLOPT_HEADER, 0);
1020
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
1021
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1022

    
1023
					$output = curl_exec($ch);
1024
					$pregHost = preg_quote($this->_dnsHost);
1025
					$pregDomain = preg_quote($this->_dnsDomain);
1026
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$pregHost}\",\"type\":\"A\".*?\$/", $output, $hostID);
1027
					$hostID = $hostID[1];
1028
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{[^\}]*?\"name\":\"{$pregHost}\",\"type\":\"A\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
1029
					$hostIP = $hostIP[1];
1030
					unset($pregHost);
1031
					unset($pregDomain);
1032

    
1033
					//step 3: update the IP
1034
					if ($hostID) {
1035
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1036
						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1037
						curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1038
						$post_data['content'] = $this->_dnsIP;
1039
						curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1040
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1041
						curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns/{$hostID}");
1042
						log_error("HostID:{$hostID}, OldIP:{$hostIP}");
1043
					}
1044
					break;
1045
				case 'dreamhost':
1046
				case 'dreamhost-v6':
1047
					$needsIP = TRUE;
1048
					$isv6 = ($this->_dnsService === 'dreamhost-v6');
1049
					$server = 'https://api.dreamhost.com/';
1050
					$post_data['key'] = $this->_dnsPass;
1051
					$post_data['unique_id'] = uniqid($this->_dnsHost);
1052
					$post_data['cmd'] = 'dns-add_record';
1053
					$post_data['format'] = 'json';
1054
					$post_data['value'] = $this->_dnsIP;
1055
					$post_data['record'] = $this->_dnsHost;
1056
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1057
					$post_data['comment'] = "Updated by pfSense:$this->_dnsUser on " . date('c');
1058
					$port = "";
1059
					if ($this->_dnsServer) {
1060
						$server = $this->_dnsServer;
1061
					}
1062
					if ($this->_dnsPort) {
1063
						$port = ":" . $this->_dnsPort;
1064
					}
1065
					curl_setopt($ch, CURLOPT_URL, $server . $port);
1066
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1067
					break;
1068
				case 'digitalocean':
1069
				case 'digitalocean-v6':
1070
					// Get record ID
1071
					$server = 'https://api.digitalocean.com/v2/domains/';
1072
					$isv6 = ($this->_dnsService === 'digitalocean-v6');
1073
					$url = $server . $this->_dnsDomain . '/records';
1074
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$this->_dnsPass}"));
1075
					curl_setopt($ch, CURLOPT_URL, $url);
1076
					$output = json_decode(curl_exec($ch));
1077
					if (!is_array($output->domain_records)) {
1078
						$output->domain_records = array();
1079
					}
1080

    
1081
					// DO's API lists 20 NS records per page, so additional pages needs to be downloaded
1082
					// https://redmine.pfsense.org/issues/10952
1083
					$_domain_records = $output->domain_records;
1084
					$_count = count($_domain_records);
1085
					$_total = 0;
1086
					if (property_exists($output, 'meta')) {
1087
						$meta = $output->meta;
1088
						if (property_exists($meta, 'total')) {
1089
							$_total = $meta->total;
1090
						}
1091
					}
1092
					$_next = '...';
1093
					$_last = '';
1094
					while ($_next != $_last) {
1095
						$_next = '';
1096
						if (property_exists($output, 'links')) {
1097
							$_links = $output->links;
1098
							if (property_exists($_links, 'pages')) {
1099
								$_pages = $_links->pages;
1100
								if (property_exists($_pages, 'next')) {
1101
									$_next = $_pages->next;
1102
								}
1103
								if (property_exists($_pages, 'last')) {
1104
									$_last = $_pages->last;
1105
								}
1106
								if ($_next != '') {
1107
									echo "getting $_next\n";
1108
									curl_setopt($ch, CURLOPT_URL, $_next);
1109
									$output = json_decode(curl_exec($ch));
1110
									if (!is_array($output->domain_records)) {
1111
										$output->domain_records = array();
1112
									}
1113
									$_domain_records = array_merge($_domain_records,$output->domain_records);
1114
								}
1115
							}
1116
						}
1117
					}
1118
					$_count = count($_domain_records);
1119

    
1120
					foreach($_domain_records as $dnsRecord) {
1121
						// NS records are named @ in DO's API, so check type as well 
1122
						// https://redmine.pfsense.org/issues/9171
1123
						if ($this->_dnsHost == $dnsRecord->name && $dnsRecord->type == ($isv6 ? 'AAAA' : 'A')) {
1124
							$recordID = $dnsRecord->id;
1125
							break;
1126
						}
1127
					}
1128

    
1129
					// Create/update record
1130
					if ($recordID == null) {
1131
						$url = $server . $this->_dnsDomain . '/records';
1132
					} else {
1133
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1134
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1135
					}
1136
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1137
					$post_data['ttl'] = $this->_dnsTTL;
1138
					$post_data['name'] = $this->_dnsHost;
1139
					$post_data['data'] = $this->_dnsIP;
1140
					curl_setopt($ch, CURLOPT_URL, $url);
1141
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1142
					break;
1143
				case 'cloudns':
1144
					/* Uses ClouDNS REST API
1145
					   Requires auth-id or sub-auth-id or sub-auth-user */
1146
					// Step 1: Find the Record ID
1147
					$url = 'https://api.cloudns.net/dns/records.json';
1148
					$post_data['auth-id'] = $this->_dnsUser;
1149
					$post_data['auth-password'] = $this->_dnsPass;
1150
					$post_data['domain-name'] = $this->_dnsDomain;
1151
					$post_data['host'] = $this->_dnsHost;
1152
					$post_data['type'] = 'a';
1153
					curl_setopt($ch, CURLOPT_URL, $url);
1154
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1155
					$output = json_decode(curl_exec($ch));
1156
					$recordID = key(get_object_vars($output));
1157

    
1158
					// Step 2: Set the record
1159
					$needsIP = TRUE;
1160
					$url = 'https://api.cloudns.net/dns/mod-record.json';
1161
					$post_data = array();
1162
					$post_data['auth-id'] = $this->_dnsUser;
1163
					$post_data['auth-password'] = $this->_dnsPass;
1164
					$post_data['domain-name'] = $this->_dnsDomain;
1165
					$post_data['record-id'] = $recordID;
1166
					$post_data['host'] = $this->_dnsHost;
1167
					$post_data['record'] = $this->_dnsIP;
1168
					$post_data['ttl'] = $this->_dnsTTL;
1169
					curl_setopt($ch, CURLOPT_URL, $url);
1170
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1171
					break;
1172
				case 'azurev6':
1173
				case 'azure':
1174
					$hostname = "{$this->_dnsHost}";
1175
					$resourceid = trim($this->_dnsZoneID);
1176
					$app_id = $this->_dnsUser;
1177
					$client_secret = $this->_dnsPass;
1178
					$newip = $this->_dnsIP;
1179
					$newttl = $this->_dnsTTL;
1180
						// ensure resourceid starts with / and has no trailing /
1181
					$resourceid = '/' . trim($resourceid, '/');
1182
						// extract subscription id from resource id
1183
					preg_match('/\\/subscriptions\\/(?<sid>[^\\/]*)/', $resourceid, $result);
1184
					$subscriptionid = isset($result['sid']) ? $result['sid'] : '';
1185
					if (isset($result['sid'])) {
1186
						$subscriptionid = $result['sid'];
1187
					} else {
1188
						log_error("Azure subscription id not found in resource id");
1189
						return false;
1190
					}
1191
						// find tenant id from subscription id
1192
					curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01");
1193
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1194
					curl_setopt($ch, CURLOPT_HEADER, 1);
1195
					curl_setopt($ch, CURLOPT_NOBODY, 1);
1196
					$output = curl_exec($ch);
1197
					$pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?<tid>[^"]*)/i';
1198
					preg_match($pattern, $output, $result);
1199
					if (isset($result['tid'])) {
1200
						$tenantid = $result['tid'];
1201
					} else {
1202
						log_error("Tenant ID not found");
1203
						return false;
1204
					}
1205
						// get an bearer token
1206
					curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token");
1207
					curl_setopt($ch, CURLOPT_POST, 1);
1208
					$body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret);
1209
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1210
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1211
					$server_output = curl_exec($ch);
1212
					$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1213
					preg_match("/\"access_token\":\"(?<tok>[^\"]*)\"/", $server_output, $result);
1214
					if (isset($result['tok'])) {
1215
						$bearertoken = $result['tok'];
1216
					} else {
1217
						log_error("no valid bearer token");
1218
						return false;
1219
					}
1220
						// Update the DNS record
1221
					if ($this->_useIPv6) {
1222
						$url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01";
1223
						$body = '{"properties":{"TTL":"' . $newttl . '", "AAAARecords":[{"ipv6Address":"' . $newip . '"}]}}';
1224
					} else {
1225
						$url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01";
1226
						$body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}';
1227
					}
1228
					$request_headers = array();
1229
					$request_headers[] = 'Accept: application/json';
1230
					$request_headers[] = 'Authorization: Bearer ' . $bearertoken;
1231
					$request_headers[] = 'Content-Type: application/json';
1232
					curl_setopt($ch, CURLOPT_URL, $url);
1233
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1234
					curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1235
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1236
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1237
					curl_setopt($ch, CURLOPT_HEADER, 1);
1238
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1239
					break;
1240
				case 'linode':
1241
				case 'linode-v6':
1242
					$linode_api = "https://api.linode.com/v4";
1243
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1244
					$record_name = $this->_dnsHost == "@" ? "" : $this->_dnsHost;
1245
					$err_prefix = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): (" . gettext("Error") . ")";
1246
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1247
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1248
						'Accept: application/json',
1249
						'Content-Type: application/json',
1250
						'Authorization: Bearer ' . $this->_dnsPass
1251
					));
1252

    
1253
					// get domain id
1254
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains");
1255
					$domains_output = curl_exec($ch);
1256
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1257
					if ( $http_code == 401 && preg_match('/invalid oauth token/i', $domains_output) ) {
1258
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authentication Failed"));
1259
						return false;
1260
					} else if ( $http_code == 401 ) {
1261
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authorization Failed"));
1262
						return false;
1263
					} else if ( $http_code != 200 ) {
1264
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1265
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1266
						return false;
1267
					}
1268

    
1269
					$domains_result = json_decode($domains_output, TRUE);
1270
					foreach($domains_result["data"] as $domains_entry) {
1271
						if ($domains_entry["domain"] == $this->_dnsDomain) {
1272
							$domain_id = $domains_entry["id"];
1273
						}
1274
					}
1275
					if ( ! $domain_id ) {
1276
						log_error("{$err_prefix} " . gettext("no domain ID for domain") . ": '{$this->_dnsDomain}'");
1277
						return false;
1278
					}
1279
					if ($this->_dnsVerboseLog) {
1280
						log_error("_update(): " . sprintf(gettext("found domain id: %s"), $domain_id));
1281
					}
1282

    
1283
					// get existing record if present
1284
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1285
					$records_output = curl_exec($ch);
1286
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1287
					if ( $http_code != 200 )
1288
					{
1289
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1290
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1291
						return false;
1292
					}
1293

    
1294
					$records_result = json_decode($records_output, TRUE);
1295
					foreach($records_result["data"] as $records_entry) {
1296
						if ( $records_entry["type"] == $record_type && $records_entry["name"] == $record_name ) {
1297
							// not adding support for pagination at this time, hope you have < 100 records!
1298
							$record = $records_entry;
1299
						}
1300
					}
1301
					if ($this->_dnsVerboseLog) {
1302
						log_error("_update(): " . ( $record ? sprintf(gettext("found existing record id: %s"), $record["id"]) : gettext("no matching record found") ));
1303
					}
1304

    
1305
					if (is_array($record)) {
1306
						// update existing record
1307
						$record["target"] = $this->_dnsIP;
1308
						$record["ttl_sec"] = (int) $this->_dnsTTL; // linode may round this up, 0 = zone default
1309
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1310
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1311
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records/" . $record["id"]);
1312
					} else {
1313
						// create a new record
1314
						$record = array(
1315
							"type"    => $record_type,
1316
							"name"    => $record_name,
1317
							"target"  => $this->_dnsIP,
1318
							"ttl_sec" => (int) $this->_dnsTTL, // linode may round this up, 0 = zone default
1319
						);
1320
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1321
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
1322
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1323
					}
1324
					break;
1325
				case 'dynv6':
1326
					$needIP = TRUE;
1327
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv4=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1328
					break;
1329
				case 'dynv6-v6':
1330
					$needIP = TRUE;
1331
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv6=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1332
					break;
1333
			case 'gandi-livedns':
1334
				// NOTE: quite similar to digitalocean case but with json content like azure case
1335
				// Get record href
1336
				$server = 'https://dns.api.gandi.net/api/v5/domains/';
1337
				$url = $server . $this->_dnsDomain . '/records';
1338
				curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Api-Key: {$this->_dnsPass}"));
1339
				curl_setopt($ch, CURLOPT_URL, $url);
1340
				$output = json_decode(curl_exec($ch));
1341
				if (!is_array($output)) {
1342
					$output = array();
1343
				}
1344
				foreach($output as $dnsRecord) {
1345
					// NS records are named @ in DO's API, so check type as well
1346
					if ($this->_dnsHost == $dnsRecord->rrset_name && $dnsRecord->rrset_type == 'A') {
1347
						$recordHref = $dnsRecord->rrset_href;
1348
						break;
1349
					}
1350
				}
1351
				$request_headers = array();
1352
				//$request_headers[] = 'Accept: application/json';
1353
				$request_headers[] = 'X-Api-Key: ' . $this->_dnsPass;
1354
				$request_headers[] = 'Content-Type: application/json';
1355
				//create or update record
1356
				if ($recordHref == null) {
1357
					//create record
1358
					$url = $server . $this->_dnsDomain . '/records';
1359
					$body = '{"rrset_type": "A", "rrset_ttl": ' . $this->_dnsTTL . ', "rrset_name": "' . $this->_dnsHost . '", "rrset_values": ["' . $this->_dnsIP . '"]}';
1360
				} else {
1361
					//update record
1362
					$url = $recordHref;
1363
					$body = '{"rrset_ttl": ' . $this->_dnsTTL . ', "rrset_values": ["' . $this->_dnsIP . '"]}';
1364
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1365
				}
1366
				curl_setopt($ch, CURLOPT_URL, $url);
1367
				curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1368
				curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1369
					break;
1370
				default:
1371
					break;
1372
			}
1373
			if ($this->_dnsService != 'ods') {
1374
				curl_setopt($ch, CURLOPT_HEADER, 1);
1375
				$response = curl_exec($ch);
1376
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1377
				$header = substr($response, 0, $header_size);
1378
				$data = substr($response, $header_size);
1379
				if ($this->_dnsVerboseLog) {
1380
					foreach (explode(PHP_EOL, $header) as $hv) {
1381
						log_error(sprintf("Response Header: %s", rtrim($hv)));
1382
				  	}
1383
					log_error(sprintf("Response Data: %s", $data));
1384
				}
1385
				$this->_checkStatus($ch, $data, $header);
1386
				@curl_close($ch);
1387
			}
1388
		}
1389

    
1390
		/**
1391
		 * Private Function (added 23 Feb 17)
1392
		 *   Send Removal To Selected Service.
1393
		 *
1394
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1395
		 *   is to remove the existing record and add a new record.
1396
		 *
1397
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1398
		 */
1399
		function _remove($existing_ip = NULL) {
1400
			$remove_allowed = false;
1401
			if ($this->_dnsVerboseLog) {
1402
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1403
			}
1404

    
1405
			if (strstr($this->_dnsRequestIf, "_vip")) {
1406
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1407
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1408
			} else {
1409
				$realparentif = $this->_dnsRequestIf;
1410
			}
1411

    
1412
			$ch = curl_init();
1413

    
1414
			if ($this->_useIPv6 == false) {
1415
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1416
			}
1417

    
1418
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1419
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1420
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1421
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1422

    
1423
			switch ($this->_dnsService) {
1424
			case 'dreamhost':
1425
			case 'dreamhost-v6':
1426
				$server = 'https://api.dreamhost.com/';
1427
				$post_data['key'] = $this->_dnsPass;
1428
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1429
				$post_data['cmd'] = 'dns-remove_record';
1430
				$post_data['format'] = 'json';
1431
				$post_data['value'] = $existing_ip;
1432
				$post_data['record'] = $this->_dnsHost;
1433
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1434
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1435
				$port = "";
1436
				if ($this->_dnsServer) {
1437
					$server = $this->_dnsServer;
1438
				}
1439
				if ($this->_dnsPort) {
1440
					$port = ":" . $this->_dnsPort;
1441
				}
1442
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1443
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1444
				$remove_allowed = true;
1445
				break;
1446
			default:
1447
				break;
1448
			}
1449
			if ($remove_allowed) {
1450
				curl_setopt($ch, CURLOPT_HEADER, 1);
1451
				$response = curl_exec($ch);
1452
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1453
				$header = substr($response, 0, $header_size);
1454
				$data = substr($response, $header_size);
1455
				$this->_checkStatus($ch, $data, $header);
1456
				@curl_close($ch);
1457
			}
1458
		}
1459

    
1460
		/**
1461
		 * Private Function (added 23 Feb 17)
1462
		 * Retrieves current DNS records from an external API source.
1463
		 *
1464
		 * Some services cannot perform new operations without the caller
1465
		 * providing existing record information.
1466
		 */
1467
		function _lookup_current() {
1468
			$lookup_allowed = false;
1469
			if ($this->_dnsVerboseLog) {
1470
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1471
			}
1472

    
1473
			if (strstr($this->_dnsRequestIf, "_vip")) {
1474
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1475
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1476
			} else {
1477
				$realparentif = $this->_dnsRequestIf;
1478
			}
1479

    
1480
			$ch = curl_init();
1481

    
1482
			if ($this->_useIPv6 == false) {
1483
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1484
			}
1485

    
1486
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1487
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1488
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1489
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1490

    
1491
			switch ($this->_dnsService) {
1492
			case 'dreamhost':
1493
			case 'dreamhost-v6':
1494
				$server = 'https://api.dreamhost.com/';
1495
				$post_data['key'] = $this->_dnsPass;
1496
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1497
				$post_data['cmd'] = 'dns-list_records';
1498
				$post_data['format'] = 'json';
1499
				$port = "";
1500
				if ($this->_dnsServer) {
1501
					$server = $this->_dnsServer;
1502
				}
1503
				if ($this->_dnsPort) {
1504
					$port = ":" . $this->_dnsPort;
1505
				}
1506
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1507
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1508
				$lookup_allowed = true;
1509
				break;
1510
			default:
1511
				break;
1512
			}
1513
			if ($lookup_allowed) {
1514
				curl_setopt($ch, CURLOPT_HEADER, 1);
1515
				$response = curl_exec($ch);
1516
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1517
				$header = substr($response, 0, $header_size);
1518
				$data = substr($response, $header_size);
1519
				$this->_checkLookupStatus($ch, $data, $header);
1520
				@curl_close($ch);
1521
			}
1522
		}
1523

    
1524
		/*
1525
		 * Private Function (added 23 Feb 17)
1526
		 *   Retrieve Lookup Status from the provided data and/or header
1527
		 */
1528
		function _checkLookupStatus($ch, $data, $header) {
1529
			if ($this->_dnsVerboseLog) {
1530
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1531
			}
1532
			$success_str = "(" . gettext("Success") . ") ";
1533
			$error_str = "(" . gettext("Error") . ") ";
1534
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1535

    
1536
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1537
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1538
				log_error($status);
1539
				$this->status = $status;
1540
				return;
1541
			}
1542
			switch ($this->_dnsService) {
1543
			case 'dreamhost':
1544
			case 'dreamhost-v6':
1545
				$result = json_decode($data,true);
1546
				if($result["result"] != "success") {
1547
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1548
					$this->_debug($data);
1549
					return;
1550
				} else {
1551
					foreach($result["data"] as $key => $row) {
1552
						if($row["record"] == $this->_dnsHost &&
1553
								(($row["type"] == "A" && !$this->_useIPv6)
1554
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1555
								)) {
1556
							if($row["editable"] == 0) {
1557
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1558
								continue;
1559
							}
1560
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1561
						}
1562
					}
1563
				}
1564
				if (!is_array($this->_existingRecords)){
1565
					if ($this->_dnsVerboseLog) {
1566
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1567
					}
1568
				}
1569
				break;
1570
			default:
1571
				break;
1572
			}
1573
		}
1574

    
1575
		/*
1576
		 * Private Function (added 12 July 2005) [beta]
1577
		 *   Retrieve Update Status
1578
		 */
1579
		function _checkStatus($ch, $data, $header) {
1580
			if ($this->_dnsVerboseLog) {
1581
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1582
			}
1583
			$successful_update = false;
1584
			$success_str = "(" . gettext("Success") . ") ";
1585
			$error_str = "(" . gettext("Error") . ") ";
1586
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1587

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

    
2406
			if ($successful_update == true) {
2407
				/* Write WAN IP to cache file */
2408
				$wan_ip = $this->_checkIP();
2409
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2410
					$currentTime = time();
2411
					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));
2412
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2413
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2414
				} else {
2415
					@unlink($this->_cacheFile);
2416
				}
2417
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2418
					$currentTime = time();
2419
					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));
2420
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2421
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2422
				} else {
2423
					@unlink($this->_cacheFile_v6);
2424
				}
2425
			}
2426
			$this->status = $status;
2427
			log_error($status);
2428
		}
2429

    
2430
		/*
2431
		 * Private Function (added 12 July 05) [beta]
2432
		 *   Return Error, Set Last Error, and Die.
2433
		 */
2434
		function _error($errorNumber = '1') {
2435
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2436
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2437
			switch ($errorNumber) {
2438
				case 0:
2439
					break;
2440
				case 2:
2441
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2442
					break;
2443
				case 3:
2444
					$error = $err_str . gettext('No Username Provided.');
2445
					break;
2446
				case 4:
2447
					$error = $err_str . gettext('No Password Provided.');
2448
					break;
2449
				case 5:
2450
					$error = $err_str . gettext('No Hostname Provided.');
2451
					break;
2452
				case 6:
2453
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2454
					break;
2455
				case 7:
2456
					$error = $err_str . gettext('No Update URL Provided.');
2457
					break;
2458
				case 8:
2459
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2460
					break;
2461
				case 9:
2462
					$status = $err_str_r53 . gettext("Invalid TTL");
2463
					break;
2464
				case 10:
2465
					$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);
2466
					break;
2467
				default:
2468
					$error = $err_str . gettext('Unknown Response.');
2469
					/* FIXME: $data isn't in scope here */
2470
					/* $this->_debug($data); */
2471
					break;
2472
			}
2473
			$this->lastError = $error;
2474
			log_error($error);
2475
		}
2476

    
2477
		/*
2478
		 * Private Function (added 12 July 05) [beta]
2479
		 *   - Detect whether or not IP needs to be updated.
2480
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2481
		 *      | work with other systems. pfSense base is FreeBSD.
2482
		 */
2483
		function _detectChange() {
2484
			global $debug;
2485

    
2486
			if ($debug) {
2487
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2488
			}
2489

    
2490
			$currentTime = time();
2491

    
2492
			$wan_ip = $this->_checkIP();
2493
			if ($wan_ip == 0) {
2494
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2495
				return false;
2496
			}
2497
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
2498

    
2499
			if ($this->_useIPv6 == true) {
2500
				if (file_exists($this->_cacheFile_v6)) {
2501
					$contents = file_get_contents($this->_cacheFile_v6);
2502
					list($cacheIP, $cacheTime) = explode('|', $contents);
2503
					$this->_debug($cacheIP . '/' . $cacheTime);
2504
					$initial = false;
2505
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
2506
				} else {
2507
					$cacheIP = '::';
2508
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
2509
					$cacheTime = $currentTime;
2510
					$initial = true;
2511
					$log_error .= gettext("No Cached IPv6 found.");
2512
				}
2513
			} else {
2514
				if (file_exists($this->_cacheFile)) {
2515
					$contents = file_get_contents($this->_cacheFile);
2516
					list($cacheIP, $cacheTime) = explode('|', $contents);
2517
					$this->_debug($cacheIP . '/' . $cacheTime);
2518
					$initial = false;
2519
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
2520
				} else {
2521
					$cacheIP = '0.0.0.0';
2522
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
2523
					$cacheTime = $currentTime;
2524
					$initial = true;
2525
					$log_error .= gettext("No Cached IP found.");
2526
				}
2527
			}
2528
			if ($this->_dnsVerboseLog) {
2529
				log_error($log_error);
2530
			}
2531

    
2532
			// Convert seconds = days * hr/day * min/hr * sec/min
2533
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60;
2534

    
2535
			$needs_updating = FALSE;
2536
			/* lets determine if the item needs updating */
2537
			if ($cacheIP != $wan_ip) {
2538
				$needs_updating = true;
2539
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
2540
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
2541
			}
2542
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
2543
				$needs_updating = true;
2544
				$this->_forceUpdateNeeded = true;
2545
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
2546
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
2547
			}
2548
			if ($initial == true) {
2549
				$needs_updating = true;
2550
				$update_reason .= gettext("Initial update.");
2551
			}
2552

    
2553
			/*   finally if we need updating then store the
2554
			 *   new cache value and return true
2555
			 */
2556
			if ($needs_updating == true) {
2557
				if ($this->_dnsVerboseLog) {
2558
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
2559
				}
2560
				return true;
2561
			}
2562

    
2563
			return false;
2564
		}
2565

    
2566
		/*
2567
		 * Private Function (added 16 July 05) [beta]
2568
		 *   - Writes debug information to a file.
2569
		 *   - This function is only called when a unknown response
2570
		 *   - status is returned from a DynDNS service provider.
2571
		 */
2572
		function _debug($data) {
2573
			global $g;
2574

    
2575
			if (!$g['debug']) {
2576
				return;
2577
			}
2578
			$string = date('m-d-y h:i:s') . ' - (' . $this->_debugID . ') - [' . $this->_dnsService . '] - ' . $data . "\n";
2579
			$file = fopen($this->_debugFile, 'a');
2580
			fwrite($file, $string);
2581
			fclose($file);
2582
		}
2583
		function _checkIP() {
2584
			global $debug;
2585

    
2586
			if ($debug) {
2587
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
2588
			}
2589

    
2590
			if ($this->_useIPv6 == true) {
2591
				$ip_address = get_interface_ipv6($this->_if);
2592
				if (!is_ipaddrv6($ip_address)) {
2593
					return 0;
2594
				}
2595
			} else {
2596
				$ip_address = dyndnsCheckIP($this->_if);
2597
				if (!is_ipaddr($ip_address)) {
2598
					return 0;
2599
				}
2600
			}
2601
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
2602
				if (is_ipaddr($ip_address)) {
2603
					if ($this->_dnsVerboseLog) {
2604
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
2605
					}
2606
				} else {
2607
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
2608
					return 0;
2609
				}
2610
			} else {
2611
				if ($this->_dnsVerboseLog) {
2612
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
2613
				}
2614
			}
2615
			$this->_dnsIP = $ip_address;
2616

    
2617
			return $ip_address;
2618
		}
2619

    
2620
	}
2621

    
2622
?>
(15-15/60)