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-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2019 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 * http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23

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

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

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

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

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

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

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

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

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

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

    
334
			$this->_debugID = rand(1000000, 9999999);
335

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

    
420
			unlock($dyndnslck);
421
		}
422

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

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

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

    
440
			$ch = curl_init();
441

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1304
			$ch = curl_init();
1305

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

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

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

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

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

    
1372
			$ch = curl_init();
1373

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

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

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

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

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

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

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

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

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

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

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

    
2380
			$currentTime = time();
2381

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

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

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

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

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

    
2453
			return false;
2454
		}
2455

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

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

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

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

    
2507
			return $ip_address;
2508
		}
2509

    
2510
	}
2511

    
2512
?>
(15-15/60)