Project

General

Profile

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

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

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

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

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

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

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

    
213
			$dyndnslck = lock("DDNS".$dnsID, LOCK_EX);
214

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

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

    
325
			// Ensure that we were able to lookup the IP
326
			if (!is_ipaddr($this->_dnsIP)) {
327
				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));
328
				unlock($dyndnslck);
329
				return;
330
			}
331

    
332
			$this->_debugID = rand(1000000, 9999999);
333

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

    
418
			unlock($dyndnslck);
419
		}
420

    
421
		/*
422
		 * Private Function (added 12 July 05) [beta]
423
		 *   Send Update To Selected Service.
424
		 */
425
		function _update() {
426

    
427
			if ($this->_dnsVerboseLog) {
428
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
429
			}
430

    
431
			if (strstr($this->_dnsRequestIf, "_vip")) {
432
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
433
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
434
			} else {
435
				$realparentif = $this->_dnsRequestIf;
436
			}
437

    
438
			$ch = curl_init();
439

    
440
			if ($this->_useIPv6 == false) {
441
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
442
			}
443

    
444
			if ($this->_dnsService != 'ods') {
445
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
446
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
447
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
448
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
449
			}
450

    
451
			switch ($this->_dnsService) {
452
				case 'glesys':
453
					$needsIP = TRUE;
454
					$server = 'https://api.glesys.com/domain/updaterecord/format/json';
455
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
456
					$post_data['recordid'] = $this->_FQDN;
457
					$post_data['data'] = $this->_dnsIP;
458
					curl_setopt($ch, CURLOPT_URL, $server);
459
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
460
					break;
461
				case 'dyndns':
462
				case 'dyndns-static':
463
				case 'dyndns-custom':
464
					$needsIP = FALSE;
465
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
466
						$this->_dnsWildcard = "ON";
467
					}
468
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
469
					$server = "https://members.dyndns.org/nic/update";
470
					$port = "";
471
					if ($this->_dnsServer) {
472
						$server = $this->_dnsServer;
473
					}
474
					if ($this->_dnsPort) {
475
						$port = ":" . $this->_dnsPort;
476
					}
477
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
478
					break;
479
				case 'dhs':
480
					// DHS is disabled in the GUI because the following doesn't work.
481
					$needsIP = TRUE;
482
					$post_data['hostscmd'] = 'edit';
483
					$post_data['hostscmdstage'] = '2';
484
					$post_data['type'] = '4';
485
					$post_data['updatetype'] = 'Online';
486
					$post_data['mx'] = $this->_dnsMX;
487
					$post_data['mx2'] = '';
488
					$post_data['txt'] = '';
489
					$post_data['offline_url'] = '';
490
					$post_data['cloak'] = 'Y';
491
					$post_data['cloak_title'] = '';
492
					$post_data['ip'] = $this->_dnsIP;
493
					$post_data['domain'] = 'dyn.dhs.org';
494
					$post_data['hostname'] = $this->_dnsHost;
495
					$post_data['submit'] = 'Update';
496
					$server = "https://members.dhs.org/nic/hosts";
497
					$port = "";
498
					if ($this->_dnsServer) {
499
						$server = $this->_dnsServer;
500
					}
501
					if ($this->_dnsPort) {
502
						$port = ":" . $this->_dnsPort;
503
					}
504
					curl_setopt($ch, CURLOPT_URL, $server . $port);
505
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
506
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
507
					break;
508
				case 'noip':
509
				case 'noip-free':
510
					$needsIP = TRUE;
511
					$server = "https://dynupdate.no-ip.com/ducupdate.php";
512
					$port = "";
513
					if ($this->_dnsServer) {
514
						$server = $this->_dnsServer;
515
					}
516
					if ($this->_dnsPort) {
517
						$port = ":" . $this->_dnsPort;
518
					}
519
					if (($this->_dnsService == "noip-free") &&
520
					    ($this->_forceUpdateNeeded == true) &&
521
					    ($this->_dnsDummyUpdateDone == false)) {
522
						// Update the IP to a dummy value to force No-IP free accounts to see a change.
523
						$iptoset = "192.168.1.1";
524
						$this->_dnsDummyUpdateDone = true;
525
						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));
526
					} else {
527
						$iptoset = $this->_dnsIP;
528
					}
529
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&pass=' . urlencode($this->_dnsPass) . '&h[]=' . $this->_dnsHost.'&ip=' . $iptoset);
530
					break;
531
				case 'easydns':
532
					$needsIP = TRUE;
533
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
534
					$server = "https://members.easydns.com/dyn/dyndns.php";
535
					$port = "";
536
					if ($this->_dnsServer) {
537
						$server = $this->_dnsServer;
538
					}
539
					if ($this->_dnsPort) {
540
						$port = ":" . $this->_dnsPort;
541
					}
542
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=' . $this->_dnsBackMX);
543
					break;
544
				case 'hn':
545
					$needsIP = TRUE;
546
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
547
					$server = "http://dup.hn.org/vanity/update";
548
					$port = "";
549
					if ($this->_dnsServer) {
550
						$server = $this->_dnsServer;
551
					}
552
					if ($this->_dnsPort) {
553
						$port = ":" . $this->_dnsPort;
554
					}
555
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?ver=1&IP=' . $this->_dnsIP);
556
					break;
557
				case 'zoneedit':
558
					$needsIP = FALSE;
559
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
560

    
561
					$server = "https://dynamic.zoneedit.com/auth/dynamic.html";
562
					$port = "";
563
					if ($this->_dnsServer) {
564
						$server = $this->_dnsServer;
565
					}
566
					if ($this->_dnsPort) {
567
						$port = ":" . $this->_dnsPort;
568
					}
569
					curl_setopt($ch, CURLOPT_URL, "{$server}{$port}?host=" . $this->_dnsHost . '&dnsto=' . $this->_dnsIP);
570
					break;
571
				case 'dyns':
572
					$needsIP = FALSE;
573
					$server = "http://www.dyns.net/postscript011.php";
574
					$port = "";
575
					if ($this->_dnsServer) {
576
						$server = $this->_dnsServer;
577
					}
578
					if ($this->_dnsPort) {
579
						$port = ":" . $this->_dnsPort;
580
					}
581
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost);
582
					break;
583
				case 'ods':
584
					$needsIP = FALSE;
585
					$misc_errno = 0;
586
					$misc_error = "";
587
					$server = "ods.org";
588
					$port = "";
589
					if ($this->_dnsServer) {
590
						$server = $this->_dnsServer;
591
					}
592
					if ($this->_dnsPort) {
593
						$port = ":" . $this->_dnsPort;
594
					}
595
					$this->con['socket'] = fsockopen("{$server}{$port}", "7070", $misc_errno, $misc_error, 30);
596
					/* Check that we have connected */
597
					if (!$this->con['socket']) {
598
						print "error! could not connect.";
599
						break;
600
					}
601
					/* Here is the loop. Read the incoming data (from the socket connection) */
602
					while (!feof($this->con['socket'])) {
603
						$this->con['buffer']['all'] = trim(fgets($this->con['socket'], 4096));
604
						$code = substr($this->con['buffer']['all'], 0, 3);
605
						sleep(1);
606
						switch ($code) {
607
							case 100:
608
								fputs($this->con['socket'], "LOGIN ".$this->_dnsUser." ".$this->_dnsPass."\n");
609
								break;
610
							case 225:
611
								fputs($this->con['socket'], "DELRR ".$this->_dnsHost." A\n");
612
								break;
613
							case 901:
614
								fputs($this->con['socket'], "ADDRR ".$this->_dnsHost." A ".$this->_dnsIP."\n");
615
								break;
616
							case 795:
617
								fputs($this->con['socket'], "QUIT\n");
618
								break;
619
						}
620
					}
621
					$this->_checkStatus(0, $code);
622
					break;
623
				case 'freedns':
624
				case 'freedns-v6':
625
					$needIP = TRUE;
626
					curl_setopt($ch, CURLOPT_URL, 'https://freedns.afraid.org/dynamic/update.php?' . $this->_dnsPass . '&address=' . $this->_dnsIP);
627
					break;
628
				case 'dnsexit':
629
					$needsIP = TRUE;
630
					curl_setopt($ch, CURLOPT_URL, 'https://www.dnsexit.com/RemoteUpdate.sv?login='.$this->_dnsUser. '&password='.$this->_dnsPass.'&host='.$this->_dnsHost.'&myip='.$this->_dnsIP);
631
					break;
632
				case 'loopia':
633
					$needsIP = TRUE;
634
					if(isset($this->_dnsWildcard) && $this->_dnsWildcard == TRUE) {
635
						$this->_dnsWildcard = "ON";
636
					} else {
637
						$this->_dnsWildcard = "OFF";
638
					}
639
					curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
640
					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");
641
					break;
642
				case 'opendns':
643
					$needsIP = FALSE;
644
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
645
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
646
					$server = "https://updates.opendns.com/nic/update?hostname=". $this->_dnsHost;
647
					$port = "";
648
					if ($this->_dnsServer) {
649
						$server = $this->_dnsServer;
650
					}
651
					if ($this->_dnsPort) {
652
						$port = ":" . $this->_dnsPort;
653
					}
654
					curl_setopt($ch, CURLOPT_URL, $server .$port);
655
					break;
656

    
657
				case 'staticcling':
658
					$needsIP = FALSE;
659
					curl_setopt($ch, CURLOPT_URL, 'https://www.staticcling.org/update.html?login='.$this->_dnsUser.'&pass='.$this->_dnsPass);
660
					break;
661
				case 'dnsomatic':
662
					/* Example syntax
663
						https://username:password@updates.dnsomatic.com/nic/update?hostname=yourhostname&myip=ipaddress&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG
664
					*/
665
					$needsIP = FALSE;
666
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
667
						$this->_dnsWildcard = "ON";
668
					}
669
					/*
670
					Reference: https://www.dnsomatic.com/wiki/api
671
						DNS-O-Matic usernames are 3-25 characters.
672
						DNS-O-Matic passwords are 6-20 characters.
673
						All ASCII letters and numbers accepted.
674
						Dots, dashes, and underscores allowed, but not at the beginning or end of the string.
675
					Required: "rawurlencode" http://www.php.net/manual/en/function.rawurlencode.php
676
						Encodes the given string according to RFC 3986.
677
					*/
678
					$server = "https://" . rawurlencode($this->_dnsUser) . ":" . rawurlencode($this->_dnsPass) . "@updates.dnsomatic.com/nic/update?hostname=";
679
					if ($this->_dnsServer) {
680
						$server = $this->_dnsServer;
681
					}
682
					if ($this->_dnsPort) {
683
						$port = ":" . $this->_dnsPort;
684
					}
685
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NOCHG');
686
					break;
687
				case 'namecheap':
688
					/* Example:
689
						https://dynamicdns.park-your-domain.com/update?host=[host_name]&domain=[domain.com]&password=[domain_password]&ip=[your_ip]
690
					*/
691
					$needsIP = FALSE;
692
					$dnspass = trim($this->_dnsPass);
693
					$server = "https://dynamicdns.park-your-domain.com/update?host={$this->_dnsHost}&domain={$this->_dnsDomain}&password={$dnspass}&ip={$this->_dnsIP}";
694
					curl_setopt($ch, CURLOPT_URL, $server);
695
					break;
696
				case 'duiadns':
697
				case 'duiadns-v6':
698
					$needsIP = FALSE;
699
					$server = "https://ipv4.duiadns.net/dyndns.duia?";
700
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
701
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
702
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
703
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
704
					break;
705
				case 'he-net':
706
				case 'he-net-v6':
707
					$needsIP = FALSE;
708
					$server = "https://dyn.dns.he.net/nic/update?";
709
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
710
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
711
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&password=' . $this->_dnsPass . '&myip=' . $this->_dnsIP);
712
					break;
713
				case 'he-net-tunnelbroker':
714
					$needsIP = FALSE;
715
					$server = "https://ipv4.tunnelbroker.net/ipv4_end.php?";
716
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
717
					curl_setopt($ch, CURLOPT_URL, $server . 'tid=' . $this->_dnsHost);
718
					break;
719
				case 'selfhost':
720
					$needsIP = FALSE;
721
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
722
						$this->_dnsWildcard = "ON";
723
					}
724
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
725
					$server = "https://carol.selfhost.de/nic/update";
726
					$port = "";
727
					if ($this->_dnsServer) {
728
						$server = $this->_dnsServer;
729
					}
730
					if ($this->_dnsPort) {
731
						$port = ":" . $this->_dnsPort;
732
					}
733
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
734
					break;
735
				case 'route53':
736
					require_once("r53.class");
737
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
738
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
739
					$xmlreq = $r53->getRequestBody($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
740
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
741
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
742
					if($this->_dnsVerboseLog){
743
						log_error(sprintf("Sending request to: %s", $apiurl));
744
						foreach($httphead as $hv){
745
							log_error(sprintf("Header: %s", $hv));
746
						}
747
						log_error(sprintf("XMLPOST: %s", $xmlreq));
748
					}
749
					curl_setopt($ch, CURLOPT_URL, $apiurl);
750
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
751
					break;
752
				case 'route53-v6':
753
					require_once("r53.class");
754
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
755
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
756
					$xmlreq = $r53->getRequestBodyV6($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
757
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
758
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
759
					if($this->_dnsVerboseLog){
760
						log_error(sprintf("Sending request to: %s", $apiurl));
761
						foreach($httphead as $hv){
762
							log_error(sprintf("Header: %s", $hv));
763
						}
764
						log_error(sprintf("XMLPOST: %s", $xmlreq));
765
					}
766
					curl_setopt($ch, CURLOPT_URL, $apiurl);
767
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
768
					break;
769
				case 'custom':
770
				case 'custom-v6':
771
					if (strstr($this->dnsUpdateURL, "%IP%")) {$needsIP = TRUE;} else {$needsIP = FALSE;}
772
					if ($this->_dnsUser != '') {
773
						if ($this->_curlIpresolveV4) {
774
							curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
775
						}
776
						if ($this->_curlSslVerifypeer) {
777
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
778
						} else {
779
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
780
						}
781
						curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
782
					}
783
					$server = str_replace("%IP%", $this->_dnsIP, $this->_dnsUpdateURL);
784
					if ($this->_dnsVerboseLog) {
785
						log_error(sprintf(gettext("Sending request to: %s"), $server));
786
					}
787
					curl_setopt($ch, CURLOPT_URL, $server);
788
					break;
789
				case 'cloudflare-v6':
790
				case 'cloudflare':
791
					$this->_FQDN = ltrim($this->_FQDN, '@.');
792
					$isv6 = ($this->_dnsService === 'cloudflare-v6');
793
					$recordType = $isv6 ? "AAAA" : "A";
794
					$needsIP = TRUE;
795
					$dnsServer ='api.cloudflare.com';
796
					$dnsHost = str_replace(' ', '', $this->_dnsHost);
797

    
798
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
799
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
800
						'X-Auth-Email: '.$this->_dnsUser.'',
801
						'X-Auth-Key: '.$this->_dnsPass.'',
802
						'Content-Type: application/json'
803
					));
804

    
805
					// Get zone ID
806
					$getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$this->_dnsDomain}";
807
					curl_setopt($ch, CURLOPT_URL, $getZoneId);
808
					$output = json_decode(curl_exec($ch));
809
					$zone = $output->result[0]->id;
810
					if ($zone) { // If zone ID was found get host ID
811
						$getHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records?name={$this->_FQDN}&type={$recordType}";
812
						curl_setopt($ch, CURLOPT_URL, $getHostId);
813
						$output = json_decode(curl_exec($ch));
814
						$host = $output->result[0]->id;
815
						if ($host) { // If host ID was found update host
816
							$hostData = array(
817
								"content" => "{$this->_dnsIP}",
818
								"type" => "{$recordType}",
819
								"proxied" => $this->_dnsProxied,
820
								"name" => "{$this->_dnsHost}",
821
								"ttl" => empty($this->_dnsTTL) ? 1 : (int) $this->_dnsTTL
822
							);
823
							$data_json = json_encode($hostData);
824
							$updateHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records/{$host}";
825
							curl_setopt($ch, CURLOPT_URL, $updateHostId);
826
							curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
827
							curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
828
						}
829
					}
830
					break;
831
				case 'eurodns':
832
					$needsIP = TRUE;
833
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
834
					$server = "https://update.eurodyndns.org/update/";
835
					$port = "";
836
					if ($this->_dnsPort) {
837
						$port = ":" . $this->_dnsPort;
838
					}
839
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
840
					break;
841
				case 'gratisdns':
842
					$needsIP = TRUE;
843
					$server = "https://ssl.gratisdns.dk/ddns.phtml";
844
					curl_setopt($ch, CURLOPT_URL, $server . '?u=' . $this->_dnsUser . '&p=' . $this->_dnsPass . '&h=' . $this->_dnsHost . '&d=' . $this->_dnsDomain . '&i=' . $this->_dnsIP);
845
					break;
846
				case 'ovh-dynhost':
847
					$needsIP = FALSE;
848
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
849
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
850
					$server = "https://www.ovh.com/nic/update";
851
					$port = "";
852
					if ($this->_dnsServer) {
853
						$server = $this->_dnsServer;
854
					}
855
					if ($this->_dnsPort) {
856
						$port = ":" . $this->_dnsPort;
857
					}
858
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
859
					break;
860
				case 'citynetwork':
861
					$needsIP = TRUE;
862
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
863
					$server = 'https://dyndns.citynetwork.se/nic/update';
864
					$port = "";
865
					if ($this->_dnsServer) {
866
						$server = $this->_dnsServer;
867
					}
868
					if ($this->_dnsPort) {
869
						$port = ":" . $this->_dnsPort;
870
					}
871
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
872
					break;
873
				case 'dnsimple':
874
					/* Uses DNSimple's v2 REST API
875
					   Requires the Account ID as the username (found in the URL when pull up the domain)
876
					   And an API Token for the password (generated in the User Settings -> API tokens area of the website)
877
					   Piggybacks on Route 53's ZoneID field for the DNSimple record ID to update
878
					   The DNS record MUST exist before it can update since it performs a PATCH operation
879
					   Data sent as JSON over HTTPS */
880
					$needsIP = TRUE;
881
					$server = 'https://api.dnsimple.com/v2/';
882
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
883
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-Type: application/json', 'Authorization: Bearer ' . $this->_dnsPass));
884
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsUser . '/zones/' . $this->_dnsHost . '/records/' . $this->_dnsZoneID);
885
					curl_setopt($ch, CURLOPT_POSTFIELDS, '{"content":"' . $this->_dnsIP . '","ttl":"' . $this->_dnsTTL . '"}');
886
					break;
887
				case 'godaddy':
888
				case 'godaddy-v6':
889
					/* Uses GoDaddy's REST API
890
					   Requires username and Account API sso-key passed in header
891
					   Data sent as JSON */
892
					$needsIP = TRUE;
893
					$server = 'https://api.godaddy.com/v1/domains/';
894
					$recordType = $this->_useIPv6 ? "AAAA" : "A";
895
					$url = $server  . $this->_dnsDomain . '/records/' . $recordType . '/' . $this->_dnsHost;
896
					$jsondata = '[{"data":"' . $this->_dnsIP . '","ttl":' . $this->_dnsTTL . '}]';
897
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
898
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
899
						'Accept: application/json',
900
						'Content-Type: application/json',
901
						'Authorization: sso-key ' . $this->_dnsUser . ':' . $this->_dnsPass
902
					));
903
					curl_setopt($ch, CURLOPT_URL, $url);
904
					curl_setopt($ch, CURLOPT_POSTFIELDS, $jsondata);
905
					break;
906
				case 'googledomains':
907
					$needsIP = FALSE;
908
					$post_data['username:password'] = $this->_dnsUser . ':' . $this->_dnsPass;
909
					$post_data['hostname'] = $this->_dnsHost;
910
					$post_data['myip'] = $this->_dnsIP;
911
					$post_data['offline'] = 'no';
912
					$server = "https://domains.google.com/nic/update";
913
					$port = "";
914
					curl_setopt($ch, CURLOPT_URL, 'https://domains.google.com/nic/update');
915
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
916
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
917
					break;
918
				case 'dnsmadeeasy':
919
					$needsIP = TRUE;
920
					$server = "https://cp.dnsmadeeasy.com/servlet/updateip";
921
					curl_setopt($ch, CURLOPT_URL, $server . '?username=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&id=' . $this->_dnsHost . '&ip=' . $this->_dnsIP);
922
					break;
923
				case 'spdyn':
924
				case 'spdyn-v6':
925
					$needsIP = FALSE;
926
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
927
					$server = "https://update.spdyn.de/nic/update";
928
					$port = "";
929
					if ($this->_dnsServer) {
930
						$server = $this->_dnsServer;
931
					}
932
					if ($this->_dnsPort) {
933
						$port = ":" . $this->_dnsPort;
934
					}
935
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
936
					break;
937
				case 'all-inkl':
938
					$needsIP = FALSE;
939
					$server = 'https://dyndns.kasserver.com/';
940
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
941
					curl_setopt($ch, CURLOPT_URL, $server . 'myip=' . $this->_dnsIP);
942
					break;
943
				case 'hover':
944
					$needsIP = FALSE;
945
					$port = "";
946
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
947
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
948

    
949
					//step 1: login to API
950
					$post_data['username'] = $this->_dnsUser;
951
					$post_data['password'] = $this->_dnsPass;
952
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
953
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
954
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
955
					$output = curl_exec($ch);
956

    
957
					//extract the cookies
958
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
959
					if( count($cookies[1]) > 0 ){
960
						$cookie_data = implode("; ",$cookies[1]);
961
					}
962

    
963
					//step 2: find the id of the A record
964
					$post_data = null;
965
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
966
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
967
					curl_setopt($ch, CURLOPT_HEADER, 0);
968
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
969
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
970

    
971
					$output = curl_exec($ch);
972
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$this->_dnsDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$this->_dnsHost}\".*?\$/", $output, $hostID);
973
					$hostID = $hostID[1];
974
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$this->_dnsDomain}.*?entries.*?{[^\}]*?\"name\":\"{$this->_dnsHost}\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
975
					$hostIP = $hostIP[1];
976

    
977
					//step 3: update the IP
978
					if ($hostID) {
979
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
980
						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
981
						curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
982
						$post_data['content'] = $this->_dnsIP;
983
						curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
984
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
985
						curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns/{$hostID}");
986
						log_error("HostID:{$hostID}, OldIP:{$hostIP}");
987
					}
988
					break;
989
				case 'dreamhost':
990
				case 'dreamhost-v6':
991
					$needsIP = TRUE;
992
					$isv6 = ($this->_dnsService === 'dreamhost-v6');
993
					$server = 'https://api.dreamhost.com/';
994
					$post_data['key'] = $this->_dnsPass;
995
					$post_data['unique_id'] = uniqid($this->_dnsHost);
996
					$post_data['cmd'] = 'dns-add_record';
997
					$post_data['format'] = 'json';
998
					$post_data['value'] = $this->_dnsIP;
999
					$post_data['record'] = $this->_dnsHost;
1000
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1001
					$post_data['comment'] = "Updated by pfSense:$this->_dnsUser on ".date('c');
1002
					$port = "";
1003
					if ($this->_dnsServer) {
1004
						$server = $this->_dnsServer;
1005
					}
1006
					if ($this->_dnsPort) {
1007
						$port = ":" . $this->_dnsPort;
1008
					}
1009
					curl_setopt($ch, CURLOPT_URL, $server . $port);
1010
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1011
					break;
1012
				case 'digitalocean':
1013
				case 'digitalocean-v6':
1014
					// Get record ID
1015
					$server = 'https://api.digitalocean.com/v2/domains/';
1016
					$isv6 = ($this->_dnsService === 'digitalocean-v6');
1017
					$url = $server . $this->_dnsDomain . '/records';
1018
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$this->_dnsPass}"));
1019
					curl_setopt($ch, CURLOPT_URL, $url);
1020
					$output = json_decode(curl_exec($ch));
1021
					if (!is_array($output->domain_records)) {
1022
						$output->domain_records = array();
1023
					}
1024
					foreach($output->domain_records as $dnsRecord) {
1025
						// NS records are named @ in DO's API, so check type as well 
1026
						// https://redmine.pfsense.org/issues/9171
1027
						if ($this->_dnsHost == $dnsRecord->name && $dnsRecord->type == 'A') {
1028
							$recordID = $dnsRecord->id;
1029
							break;
1030
						}
1031
					}
1032

    
1033
					// Create/update record
1034
					if ($recordID == null) {
1035
						$url = $server . $this->_dnsDomain . '/records';
1036
					} else {
1037
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1038
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1039
					}
1040
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1041
					$post_data['ttl'] = $this->_dnsTTL;
1042
					$post_data['name'] = $this->_dnsHost;
1043
					$post_data['data'] = $this->_dnsIP;
1044
					curl_setopt($ch, CURLOPT_URL, $url);
1045
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1046
					break;
1047
				case 'cloudns':
1048
					/* Uses ClouDNS REST API
1049
					   Requires auth-id or sub-auth-id or sub-auth-user */
1050
					// Step 1: Find the Record ID
1051
					$url = 'https://api.cloudns.net/dns/records.json';
1052
					$post_data['auth-id'] = $this->_dnsUser;
1053
					$post_data['auth-password'] = $this->_dnsPass;
1054
					$post_data['domain-name'] = $this->_dnsDomain;
1055
					$post_data['host'] = $this->_dnsHost;
1056
					$post_data['type'] = 'a';
1057
					curl_setopt($ch, CURLOPT_URL, $url);
1058
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1059
					$output = json_decode(curl_exec($ch));
1060
					$recordID = key(get_object_vars($output));
1061

    
1062
					// Step 2: Set the record
1063
					$needsIP = TRUE;
1064
					$url = 'https://api.cloudns.net/dns/mod-record.json';
1065
					$post_data = array();
1066
					$post_data['auth-id'] = $this->_dnsUser;
1067
					$post_data['auth-password'] = $this->_dnsPass;
1068
					$post_data['domain-name'] = $this->_dnsDomain;
1069
					$post_data['record-id'] = $recordID;
1070
					$post_data['host'] = $this->_dnsHost;
1071
					$post_data['record'] = $this->_dnsIP;
1072
					$post_data['ttl'] = $this->_dnsTTL;
1073
					curl_setopt($ch, CURLOPT_URL, $url);
1074
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1075
					break;
1076
				case 'azurev6':
1077
				case 'azure':
1078
					$hostname = "{$this->_dnsHost}";
1079
					$resourceid = trim($this->_dnsZoneID);
1080
					$app_id = $this->_dnsUser;
1081
					$client_secret = $this->_dnsPass;
1082
					$newip = $this->_dnsIP;
1083
					$newttl = $this->_dnsTTL;
1084
						// ensure resourceid starts with / and has no trailing /
1085
					$resourceid = '/' . trim($resourceid, '/');
1086
						// extract subscription id from resource id
1087
					preg_match('/\\/subscriptions\\/(?<sid>[^\\/]*)/', $resourceid, $result);
1088
					$subscriptionid = isset($result['sid']) ? $result['sid'] : '';
1089
					if (isset($result['sid'])) {
1090
						$subscriptionid = $result['sid'];
1091
					} else {
1092
						log_error("Azure subscription id not found in resource id");
1093
						return false;
1094
					}
1095
						// find tenant id from subscription id
1096
					curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01");
1097
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1098
					curl_setopt($ch, CURLOPT_HEADER, 1);
1099
					curl_setopt($ch, CURLOPT_NOBODY, 1);
1100
					$output = curl_exec($ch);
1101
					$pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?<tid>[^"]*)/i';
1102
					preg_match($pattern, $output, $result);
1103
					if (isset($result['tid'])) {
1104
						$tenantid = $result['tid'];
1105
					} else {
1106
						log_error("Tenant ID not found");
1107
						return false;
1108
					}
1109
						// get an bearer token
1110
					curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token");
1111
					curl_setopt($ch, CURLOPT_POST, 1);
1112
					$body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret);
1113
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1114
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1115
					$server_output = curl_exec($ch);
1116
					$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1117
					preg_match("/\"access_token\":\"(?<tok>[^\"]*)\"/", $server_output, $result);
1118
					if (isset($result['tok'])) {
1119
						$bearertoken = $result['tok'];
1120
					} else {
1121
						log_error("no valid bearer token");
1122
						return false;
1123
					}
1124
						// Update the DNS record
1125
					if ($this->_useIPv6) {
1126
						$url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01";
1127
						$body = '{"properties":{"TTL":"' . $newttl . '", "AAAARecords":[{"ipv6Address":"' . $newip . '"}]}}';
1128
					} else {
1129
						$url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01";
1130
						$body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}';
1131
					}
1132
					$request_headers = array();
1133
					$request_headers[] = 'Accept: application/json';
1134
					$request_headers[] = 'Authorization: Bearer ' . $bearertoken;
1135
					$request_headers[] = 'Content-Type: application/json';
1136
					curl_setopt($ch, CURLOPT_URL, $url);
1137
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1138
					curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1139
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1140
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1141
					curl_setopt($ch, CURLOPT_HEADER, 1);
1142
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1143
					break;
1144
				case 'linode':
1145
				case 'linode-v6':
1146
					$linode_api = "https://api.linode.com/v4";
1147
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1148
					$record_name = $this->_dnsHost == "@" ? "" : $this->_dnsHost;
1149
					$err_prefix = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): (" . gettext("Error") . ")";
1150
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1151
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1152
						'Accept: application/json',
1153
						'Content-Type: application/json',
1154
						'Authorization: Bearer ' . $this->_dnsPass
1155
					));
1156

    
1157
					// get domain id
1158
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains");
1159
					$domains_output = curl_exec($ch);
1160
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1161
					if ( $http_code == 401 && preg_match('/invalid oauth token/i', $domains_output) ) {
1162
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authentication Failed"));
1163
						return false;
1164
					} else if ( $http_code == 401 ) {
1165
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authorization Failed"));
1166
						return false;
1167
					} else if ( $http_code != 200 ) {
1168
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1169
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1170
						return false;
1171
					}
1172

    
1173
					$domains_result = json_decode($domains_output, TRUE);
1174
					foreach($domains_result["data"] as $domains_entry) {
1175
						if ($domains_entry["domain"] == $this->_dnsDomain) {
1176
							$domain_id = $domains_entry["id"];
1177
						}
1178
					}
1179
					if ( ! $domain_id ) {
1180
						log_error("{$err_prefix} " . gettext("no domain ID for domain") . ": '{$this->_dnsDomain}'");
1181
						return false;
1182
					}
1183
					if ($this->_dnsVerboseLog) {
1184
						log_error("_update(): " . sprintf(gettext("found domain id: %s"), $domain_id));
1185
					}
1186

    
1187
					// get existing record if present
1188
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1189
					$records_output = curl_exec($ch);
1190
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1191
					if ( $http_code != 200 )
1192
					{
1193
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1194
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1195
						return false;
1196
					}
1197

    
1198
					$records_result = json_decode($records_output, TRUE);
1199
					foreach($records_result["data"] as $records_entry) {
1200
						if ( $records_entry["type"] == $record_type && $records_entry["name"] == $record_name ) {
1201
							// not adding support for pagination at this time, hope you have < 100 records!
1202
							$record = $records_entry;
1203
						}
1204
					}
1205
					if ($this->_dnsVerboseLog) {
1206
						log_error("_update(): " . ( $record ? sprintf(gettext("found existing record id: %s"), $record["id"]) : gettext("no matching record found") ));
1207
					}
1208

    
1209
					if (is_array($record)) {
1210
						// update existing record
1211
						$record["target"] = $this->_dnsIP;
1212
						$record["ttl_sec"] = (int) $this->_dnsTTL; // linode may round this up, 0 = zone default
1213
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1214
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1215
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records/" . $record["id"]);
1216
					} else {
1217
						// create a new record
1218
						$record = array(
1219
							"type"    => $record_type,
1220
							"name"    => $record_name,
1221
							"target"  => $this->_dnsIP,
1222
							"ttl_sec" => (int) $this->_dnsTTL, // linode may round this up, 0 = zone default
1223
						);
1224
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1225
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
1226
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1227
					}
1228

    
1229
				break;
1230
			case 'gandi-livedns':
1231
				// NOTE: quite similar to digitalocean case but with json content like azure case
1232
				// Get record href
1233
				$server = 'https://dns.api.gandi.net/api/v5/domains/';
1234
				$url = $server . $this->_dnsDomain . '/records';
1235
				curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Api-Key: {$this->_dnsPass}"));
1236
				curl_setopt($ch, CURLOPT_URL, $url);
1237
				$output = json_decode(curl_exec($ch));
1238
				if (!is_array($output)) {
1239
					$output = array();
1240
				}
1241
				foreach($output as $dnsRecord) {
1242
					// NS records are named @ in DO's API, so check type as well
1243
					if ($this->_dnsHost == $dnsRecord->rrset_name && $dnsRecord->rrset_type == 'A') {
1244
						$recordHref = $dnsRecord->rrset_href;
1245
						break;
1246
					}
1247
				}
1248
				$request_headers = array();
1249
				//$request_headers[] = 'Accept: application/json';
1250
				$request_headers[] = 'X-Api-Key: ' .$this->_dnsPass;
1251
				$request_headers[] = 'Content-Type: application/json';
1252
				//create or update record
1253
				if ($recordHref == null) {
1254
					//create record
1255
					$url = $server . $this->_dnsDomain . '/records';
1256
					$body = '{"rrset_type": "A", "rrset_ttl": ' . $this->_dnsTTL . ', "rrset_name": "' . $this->_dnsHost . '", "rrset_values": ["' . $this->_dnsIP . '"]}';
1257
				} else {
1258
					//update record
1259
					$url = $recordHref;
1260
					$body = '{"rrset_ttl": ' . $this->_dnsTTL . ', "rrset_values": ["' . $this->_dnsIP . '"]}';
1261
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1262
				}
1263
				curl_setopt($ch, CURLOPT_URL, $url);
1264
				curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1265
				curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1266
					break;
1267
				default:
1268
					break;
1269
			}
1270
			if ($this->_dnsService != 'ods') {
1271
				curl_setopt($ch, CURLOPT_HEADER, 1);
1272
				$response = curl_exec($ch);
1273
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1274
				$header = substr($response, 0, $header_size);
1275
				$data = substr($response, $header_size);
1276
				$this->_checkStatus($ch, $data, $header);
1277
				@curl_close($ch);
1278
			}
1279
		}
1280

    
1281
		/**
1282
		 * Private Function (added 23 Feb 17)
1283
		 *   Send Removal To Selected Service.
1284
		 *
1285
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1286
		 *   is to remove the existing record and add a new record.
1287
		 *
1288
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1289
		 */
1290
		function _remove($existing_ip = NULL) {
1291
			$remove_allowed = false;
1292
			if ($this->_dnsVerboseLog) {
1293
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1294
			}
1295

    
1296
			if (strstr($this->_dnsRequestIf, "_vip")) {
1297
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1298
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1299
			} else {
1300
				$realparentif = $this->_dnsRequestIf;
1301
			}
1302

    
1303
			$ch = curl_init();
1304

    
1305
			if ($this->_useIPv6 == false) {
1306
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1307
			}
1308

    
1309
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1310
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1311
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1312
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1313

    
1314
			switch ($this->_dnsService) {
1315
			case 'dreamhost':
1316
			case 'dreamhost-v6':
1317
				$server = 'https://api.dreamhost.com/';
1318
				$post_data['key'] = $this->_dnsPass;
1319
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1320
				$post_data['cmd'] = 'dns-remove_record';
1321
				$post_data['format'] = 'json';
1322
				$post_data['value'] = $existing_ip;
1323
				$post_data['record'] = $this->_dnsHost;
1324
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1325
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1326
				$port = "";
1327
				if ($this->_dnsServer) {
1328
					$server = $this->_dnsServer;
1329
				}
1330
				if ($this->_dnsPort) {
1331
					$port = ":" . $this->_dnsPort;
1332
				}
1333
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1334
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1335
				$remove_allowed = true;
1336
				break;
1337
			default:
1338
				break;
1339
			}
1340
			if ($remove_allowed) {
1341
				curl_setopt($ch, CURLOPT_HEADER, 1);
1342
				$response = curl_exec($ch);
1343
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1344
				$header = substr($response, 0, $header_size);
1345
				$data = substr($response, $header_size);
1346
				$this->_checkStatus($ch, $data, $header);
1347
				@curl_close($ch);
1348
			}
1349
		}
1350

    
1351
		/**
1352
		 * Private Function (added 23 Feb 17)
1353
		 * Retrieves current DNS records from an external API source.
1354
		 *
1355
		 * Some services cannot perform new operations without the caller
1356
		 * providing existing record information.
1357
		 */
1358
		function _lookup_current() {
1359
			$lookup_allowed = false;
1360
			if ($this->_dnsVerboseLog) {
1361
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1362
			}
1363

    
1364
			if (strstr($this->_dnsRequestIf, "_vip")) {
1365
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1366
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1367
			} else {
1368
				$realparentif = $this->_dnsRequestIf;
1369
			}
1370

    
1371
			$ch = curl_init();
1372

    
1373
			if ($this->_useIPv6 == false) {
1374
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1375
			}
1376

    
1377
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1378
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1379
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1380
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1381

    
1382
			switch ($this->_dnsService) {
1383
			case 'dreamhost':
1384
			case 'dreamhost-v6':
1385
				$server = 'https://api.dreamhost.com/';
1386
				$post_data['key'] = $this->_dnsPass;
1387
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1388
				$post_data['cmd'] = 'dns-list_records';
1389
				$post_data['format'] = 'json';
1390
				$port = "";
1391
				if ($this->_dnsServer) {
1392
					$server = $this->_dnsServer;
1393
				}
1394
				if ($this->_dnsPort) {
1395
					$port = ":" . $this->_dnsPort;
1396
				}
1397
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1398
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1399
				$lookup_allowed = true;
1400
				break;
1401
			default:
1402
				break;
1403
			}
1404
			if ($lookup_allowed) {
1405
				curl_setopt($ch, CURLOPT_HEADER, 1);
1406
				$response = curl_exec($ch);
1407
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1408
				$header = substr($response, 0, $header_size);
1409
				$data = substr($response, $header_size);
1410
				$this->_checkLookupStatus($ch, $data, $header);
1411
				@curl_close($ch);
1412
			}
1413
		}
1414

    
1415
		/*
1416
		 * Private Function (added 23 Feb 17)
1417
		 *   Retrieve Lookup Status from the provided data and/or header
1418
		 */
1419
		function _checkLookupStatus($ch, $data, $header) {
1420
			if ($this->_dnsVerboseLog) {
1421
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1422
			}
1423
			$success_str = "(" . gettext("Success") . ") ";
1424
			$error_str = "(" . gettext("Error") . ") ";
1425
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1426

    
1427
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1428
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1429
				log_error($status);
1430
				$this->status = $status;
1431
				return;
1432
			}
1433
			switch ($this->_dnsService) {
1434
			case 'dreamhost':
1435
			case 'dreamhost-v6':
1436
				$result = json_decode($data,true);
1437
				if($result["result"] != "success") {
1438
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1439
					$this->_debug($data);
1440
					return;
1441
				} else {
1442
					foreach($result["data"] as $key => $row) {
1443
						if($row["record"] == $this->_dnsHost &&
1444
								(($row["type"] == "A" && !$this->_useIPv6)
1445
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1446
								)) {
1447
							if($row["editable"] == 0) {
1448
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1449
								continue;
1450
							}
1451
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1452
						}
1453
					}
1454
				}
1455
				if (!is_array($this->_existingRecords)){
1456
					if ($this->_dnsVerboseLog) {
1457
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1458
					}
1459
				}
1460
				break;
1461
			default:
1462
				break;
1463
			}
1464
		}
1465

    
1466
		/*
1467
		 * Private Function (added 12 July 2005) [beta]
1468
		 *   Retrieve Update Status
1469
		 */
1470
		function _checkStatus($ch, $data, $header) {
1471
			if ($this->_dnsVerboseLog) {
1472
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1473
			}
1474
			$successful_update = false;
1475
			$success_str = "(" . gettext("Success") . ") ";
1476
			$error_str = "(" . gettext("Error") . ") ";
1477
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1478

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

    
2295
			if ($successful_update == true) {
2296
				/* Write WAN IP to cache file */
2297
				$wan_ip = $this->_checkIP();
2298
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2299
					$currentTime = time();
2300
					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));
2301
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2302
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2303
				} else {
2304
					@unlink($this->_cacheFile);
2305
				}
2306
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2307
					$currentTime = time();
2308
					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));
2309
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2310
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2311
				} else {
2312
					@unlink($this->_cacheFile_v6);
2313
				}
2314
			}
2315
			$this->status = $status;
2316
			log_error($status);
2317
		}
2318

    
2319
		/*
2320
		 * Private Function (added 12 July 05) [beta]
2321
		 *   Return Error, Set Last Error, and Die.
2322
		 */
2323
		function _error($errorNumber = '1') {
2324
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2325
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2326
			switch ($errorNumber) {
2327
				case 0:
2328
					break;
2329
				case 2:
2330
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2331
					break;
2332
				case 3:
2333
					$error = $err_str . gettext('No Username Provided.');
2334
					break;
2335
				case 4:
2336
					$error = $err_str . gettext('No Password Provided.');
2337
					break;
2338
				case 5:
2339
					$error = $err_str . gettext('No Hostname Provided.');
2340
					break;
2341
				case 6:
2342
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2343
					break;
2344
				case 7:
2345
					$error = $err_str . gettext('No Update URL Provided.');
2346
					break;
2347
				case 8:
2348
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2349
					break;
2350
				case 9:
2351
					$status = $err_str_r53 . gettext("Invalid TTL");
2352
					break;
2353
				case 10:
2354
					$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);
2355
					break;
2356
				default:
2357
					$error = $err_str . gettext('Unknown Response.');
2358
					/* FIXME: $data isn't in scope here */
2359
					/* $this->_debug($data); */
2360
					break;
2361
			}
2362
			$this->lastError = $error;
2363
			log_error($error);
2364
		}
2365

    
2366
		/*
2367
		 * Private Function (added 12 July 05) [beta]
2368
		 *   - Detect whether or not IP needs to be updated.
2369
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2370
		 *      | work with other systems. pfSense base is FreeBSD.
2371
		 */
2372
		function _detectChange() {
2373
			global $debug;
2374

    
2375
			if ($debug) {
2376
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2377
			}
2378

    
2379
			$currentTime = time();
2380

    
2381
			$wan_ip = $this->_checkIP();
2382
			if ($wan_ip == 0) {
2383
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2384
				return false;
2385
			}
2386
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
2387

    
2388
			if ($this->_useIPv6 == true) {
2389
				if (file_exists($this->_cacheFile_v6)) {
2390
					$contents = file_get_contents($this->_cacheFile_v6);
2391
					list($cacheIP, $cacheTime) = explode('|', $contents);
2392
					$this->_debug($cacheIP.'/'.$cacheTime);
2393
					$initial = false;
2394
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
2395
				} else {
2396
					$cacheIP = '::';
2397
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
2398
					$cacheTime = $currentTime;
2399
					$initial = true;
2400
					$log_error .= gettext("No Cached IPv6 found.");
2401
				}
2402
			} else {
2403
				if (file_exists($this->_cacheFile)) {
2404
					$contents = file_get_contents($this->_cacheFile);
2405
					list($cacheIP, $cacheTime) = explode('|', $contents);
2406
					$this->_debug($cacheIP.'/'.$cacheTime);
2407
					$initial = false;
2408
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
2409
				} else {
2410
					$cacheIP = '0.0.0.0';
2411
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
2412
					$cacheTime = $currentTime;
2413
					$initial = true;
2414
					$log_error .= gettext("No Cached IP found.");
2415
				}
2416
			}
2417
			if ($this->_dnsVerboseLog) {
2418
				log_error($log_error);
2419
			}
2420

    
2421
			// Convert seconds = days * hr/day * min/hr * sec/min
2422
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60;
2423

    
2424
			$needs_updating = FALSE;
2425
			/* lets determine if the item needs updating */
2426
			if ($cacheIP != $wan_ip) {
2427
				$needs_updating = true;
2428
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
2429
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
2430
			}
2431
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
2432
				$needs_updating = true;
2433
				$this->_forceUpdateNeeded = true;
2434
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
2435
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
2436
			}
2437
			if ($initial == true) {
2438
				$needs_updating = true;
2439
				$update_reason .= gettext("Initial update.");
2440
			}
2441

    
2442
			/*   finally if we need updating then store the
2443
			 *   new cache value and return true
2444
			 */
2445
			if ($needs_updating == true) {
2446
				if ($this->_dnsVerboseLog) {
2447
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
2448
				}
2449
				return true;
2450
			}
2451

    
2452
			return false;
2453
		}
2454

    
2455
		/*
2456
		 * Private Function (added 16 July 05) [beta]
2457
		 *   - Writes debug information to a file.
2458
		 *   - This function is only called when a unknown response
2459
		 *   - status is returned from a DynDNS service provider.
2460
		 */
2461
		function _debug($data) {
2462
			global $g;
2463

    
2464
			if (!$g['debug']) {
2465
				return;
2466
			}
2467
			$string = date('m-d-y h:i:s').' - ('.$this->_debugID.') - ['.$this->_dnsService.'] - '.$data."\n";
2468
			$file = fopen($this->_debugFile, 'a');
2469
			fwrite($file, $string);
2470
			fclose($file);
2471
		}
2472
		function _checkIP() {
2473
			global $debug;
2474

    
2475
			if ($debug) {
2476
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
2477
			}
2478

    
2479
			if ($this->_useIPv6 == true) {
2480
				$ip_address = get_interface_ipv6($this->_if);
2481
				if (!is_ipaddrv6($ip_address)) {
2482
					return 0;
2483
				}
2484
			} else {
2485
				$ip_address = dyndnsCheckIP($this->_if);
2486
				if (!is_ipaddr($ip_address)) {
2487
					return 0;
2488
				}
2489
			}
2490
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
2491
				if (is_ipaddr($ip_address)) {
2492
					if ($this->_dnsVerboseLog) {
2493
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
2494
					}
2495
				} else {
2496
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
2497
					return 0;
2498
				}
2499
			} else {
2500
				if ($this->_dnsVerboseLog) {
2501
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
2502
				}
2503
			}
2504
			$this->_dnsIP = $ip_address;
2505

    
2506
			return $ip_address;
2507
		}
2508

    
2509
	}
2510

    
2511
?>
(15-15/60)