Project

General

Profile

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

    
24
	/*
25
	 * PHP.updateDNS (pfSense version)
26
	 *
27
	 * +====================================================+
28
	 *  Services Supported:
29
	 *    - DynDns (dyndns.org) [dynamic, static, custom]
30
	 *    - No-IP (no-ip.com)
31
	 *    - EasyDNS (easydns.com)
32
	 *    - DHS (www.dhs.org)
33
	 *    - HN (hn.org) -- incomplete checking!
34
	 *    - DynS (dyns.org)
35
	 *    - ZoneEdit (zoneedit.com)
36
	 *    - FreeDNS (freedns.afraid.org)
37
	 *    - FreeDNS IPv6 (freedns.afraid.org)
38
	 *    - Loopia (loopia.se)
39
	 *    - StaticCling (staticcling.org)
40
	 *    - DNSexit (dnsexit.com)
41
	 *    - OpenDNS (opendns.com)
42
	 *    - Namecheap (namecheap.com)
43
	 *    - HE.net (dns.he.net)
44
	 *    - HE.net IPv6 (dns.he.net)
45
	 *    - HE.net Tunnelbroker IP update (ipv4.tunnelbroker.net)
46
	 *    - SelfHost (selfhost.de)
47
	 *    - Amazon Route 53 (aws.amazon.com)
48
	 *    - DNS-O-Matic (dnsomatic.com)
49
	 *    - Custom DDNS (any URL)
50
	 *    - Custom DDNS IPv6 (any URL)
51
	 *    - Cloudflare (www.cloudflare.com)
52
	 *    - Cloudflare IPv6 (www.cloudflare.com)
53
	 *    - Eurodns (eurodns.com)
54
	 *    - Gandi LiveDNS (www.gandi.net)
55
	 *    - GratisDNS (gratisdns.dk)
56
	 *    - City Network (citynetwork.se)
57
	 *    - GleSYS (glesys.com)
58
	 *    - DNSimple (dnsimple.com)
59
	 *    - Google Domains (domains.google.com)
60
	 *    - DNS Made Easy (www.dnsmadeeasy.com)
61
	 *    - SPDYN (spdyn.de)
62
	 *    - SPDYN IPv6 (spdyn.de)
63
	 *    - All-Inkl (all-inkl.com)
64
	 *    - DuiaDNS (www.duiadns.net)
65
	 *    - DuiaDNS IPv6 (www.duiadns.net)
66
	 *    - Hover (www.hover.com)
67
	 *    - DreamHost DNS (www.dreamhost.com)
68
	 *    - ClouDNS (www.cloudns.net)
69
	 *    - GoDaddy (www.godaddy.com)
70
	 *    - Azure DNS (azure.microsoft.com)
71
	 * +----------------------------------------------------+
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
				case 'noip-v6':
298
				case 'noip-free-v6':
299
					$this->_useIPv6 = true;
300
					break;
301
				default:
302
					$this->_useIPv6 = false;
303
			}
304
			$this->_dnsService = strtolower($dnsService);
305
			$this->_dnsUser = $dnsUser;
306
			$this->_dnsPass = base64_decode($dnsPass);
307
			$this->_dnsHost = $dnsHost;
308
			$this->_dnsDomain = $dnsDomain;
309
			$this->_dnsServer = $dnsServer;
310
			$this->_dnsPort = $dnsPort;
311
			$this->_dnsWildcard = $dnsWildcard;
312
			$this->_dnsProxied = $dnsProxied;
313
			$this->_dnsMX = $dnsMX;
314
			$this->_dnsZoneID = $dnsZoneID;
315
			$this->_dnsTTL = $dnsTTL;
316
			$this->_if = get_failover_interface($dnsIf);
317
			$this->_checkIP();
318
			$this->_dnsUpdateURL = $dnsUpdateURL;
319
			$this->_dnsResultMatch = $dnsResultMatch;
320
			$this->_dnsRequestIf = get_failover_interface($dnsRequestIf);
321
			if ($this->_dnsVerboseLog) {
322
				log_error(sprintf(gettext('Dynamic DNS (%1$s): running get_failover_interface for %2$s. found %3$s'), $this->_FQDN, $dnsRequestIf, $this->_dnsRequestIf));
323
			}
324
			$this->_dnsRequestIfIP = get_interface_ip($dnsRequestIf);
325
			$this->_dnsMaxCacheAgeDays = 25;
326
			$this->_dnsDummyUpdateDone = false;
327
			$this->_forceUpdateNeeded = $forceUpdate;
328

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

    
336
			$this->_debugID = rand(1000000, 9999999);
337

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

    
424
			unlock($dyndnslck);
425
		}
426

    
427
		/*
428
		 * Private Function (added 12 July 05) [beta]
429
		 *   Send Update To Selected Service.
430
		 */
431
		function _update() {
432

    
433
			if ($this->_dnsVerboseLog) {
434
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
435
			}
436

    
437
			if (strstr($this->_dnsRequestIf, "_vip")) {
438
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
439
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
440
			} else {
441
				$realparentif = $this->_dnsRequestIf;
442
			}
443

    
444
			$ch = curl_init();
445

    
446
			if ($this->_useIPv6 == false) {
447
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
448
			}
449

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

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

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

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

    
835
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
836

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

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

    
855
						$zone = $this->_dnsUser;
856
					}
857

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

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

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

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

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

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

    
1085
					// Create/update record
1086
					if ($recordID == null) {
1087
						$url = $server . $this->_dnsDomain . '/records';
1088
					} else {
1089
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1090
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1091
					}
1092
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1093
					$post_data['ttl'] = $this->_dnsTTL;
1094
					$post_data['name'] = $this->_dnsHost;
1095
					$post_data['data'] = $this->_dnsIP;
1096
					curl_setopt($ch, CURLOPT_URL, $url);
1097
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1098
					break;
1099
				case 'cloudns':
1100
					/* Uses ClouDNS REST API
1101
					   Requires auth-id or sub-auth-id or sub-auth-user */
1102
					// Step 1: Find the Record ID
1103
					$url = 'https://api.cloudns.net/dns/records.json';
1104
					$post_data['auth-id'] = $this->_dnsUser;
1105
					$post_data['auth-password'] = $this->_dnsPass;
1106
					$post_data['domain-name'] = $this->_dnsDomain;
1107
					$post_data['host'] = $this->_dnsHost;
1108
					$post_data['type'] = 'a';
1109
					curl_setopt($ch, CURLOPT_URL, $url);
1110
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1111
					$output = json_decode(curl_exec($ch));
1112
					$recordID = key(get_object_vars($output));
1113

    
1114
					// Step 2: Set the record
1115
					$needsIP = TRUE;
1116
					$url = 'https://api.cloudns.net/dns/mod-record.json';
1117
					$post_data = array();
1118
					$post_data['auth-id'] = $this->_dnsUser;
1119
					$post_data['auth-password'] = $this->_dnsPass;
1120
					$post_data['domain-name'] = $this->_dnsDomain;
1121
					$post_data['record-id'] = $recordID;
1122
					$post_data['host'] = $this->_dnsHost;
1123
					$post_data['record'] = $this->_dnsIP;
1124
					$post_data['ttl'] = $this->_dnsTTL;
1125
					curl_setopt($ch, CURLOPT_URL, $url);
1126
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1127
					break;
1128
				case 'azurev6':
1129
				case 'azure':
1130
					$hostname = "{$this->_dnsHost}";
1131
					$resourceid = trim($this->_dnsZoneID);
1132
					$app_id = $this->_dnsUser;
1133
					$client_secret = $this->_dnsPass;
1134
					$newip = $this->_dnsIP;
1135
					$newttl = $this->_dnsTTL;
1136
						// ensure resourceid starts with / and has no trailing /
1137
					$resourceid = '/' . trim($resourceid, '/');
1138
						// extract subscription id from resource id
1139
					preg_match('/\\/subscriptions\\/(?<sid>[^\\/]*)/', $resourceid, $result);
1140
					$subscriptionid = isset($result['sid']) ? $result['sid'] : '';
1141
					if (isset($result['sid'])) {
1142
						$subscriptionid = $result['sid'];
1143
					} else {
1144
						log_error("Azure subscription id not found in resource id");
1145
						return false;
1146
					}
1147
						// find tenant id from subscription id
1148
					curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01");
1149
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1150
					curl_setopt($ch, CURLOPT_HEADER, 1);
1151
					curl_setopt($ch, CURLOPT_NOBODY, 1);
1152
					$output = curl_exec($ch);
1153
					$pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?<tid>[^"]*)/i';
1154
					preg_match($pattern, $output, $result);
1155
					if (isset($result['tid'])) {
1156
						$tenantid = $result['tid'];
1157
					} else {
1158
						log_error("Tenant ID not found");
1159
						return false;
1160
					}
1161
						// get an bearer token
1162
					curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token");
1163
					curl_setopt($ch, CURLOPT_POST, 1);
1164
					$body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret);
1165
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1166
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1167
					$server_output = curl_exec($ch);
1168
					$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1169
					preg_match("/\"access_token\":\"(?<tok>[^\"]*)\"/", $server_output, $result);
1170
					if (isset($result['tok'])) {
1171
						$bearertoken = $result['tok'];
1172
					} else {
1173
						log_error("no valid bearer token");
1174
						return false;
1175
					}
1176
						// Update the DNS record
1177
					if ($this->_useIPv6) {
1178
						$url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01";
1179
						$body = '{"properties":{"TTL":"' . $newttl . '", "AAAARecords":[{"ipv6Address":"' . $newip . '"}]}}';
1180
					} else {
1181
						$url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01";
1182
						$body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}';
1183
					}
1184
					$request_headers = array();
1185
					$request_headers[] = 'Accept: application/json';
1186
					$request_headers[] = 'Authorization: Bearer ' . $bearertoken;
1187
					$request_headers[] = 'Content-Type: application/json';
1188
					curl_setopt($ch, CURLOPT_URL, $url);
1189
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1190
					curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1191
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1192
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1193
					curl_setopt($ch, CURLOPT_HEADER, 1);
1194
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1195
					break;
1196
				case 'linode':
1197
				case 'linode-v6':
1198
					$linode_api = "https://api.linode.com/v4";
1199
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1200
					$record_name = $this->_dnsHost == "@" ? "" : $this->_dnsHost;
1201
					$err_prefix = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): (" . gettext("Error") . ")";
1202
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1203
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1204
						'Accept: application/json',
1205
						'Content-Type: application/json',
1206
						'Authorization: Bearer ' . $this->_dnsPass
1207
					));
1208

    
1209
					// get domain id
1210
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains");
1211
					$domains_output = curl_exec($ch);
1212
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1213
					if ( $http_code == 401 && preg_match('/invalid oauth token/i', $domains_output) ) {
1214
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authentication Failed"));
1215
						return false;
1216
					} else if ( $http_code == 401 ) {
1217
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authorization Failed"));
1218
						return false;
1219
					} else if ( $http_code != 200 ) {
1220
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1221
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1222
						return false;
1223
					}
1224

    
1225
					$domains_result = json_decode($domains_output, TRUE);
1226
					foreach($domains_result["data"] as $domains_entry) {
1227
						if ($domains_entry["domain"] == $this->_dnsDomain) {
1228
							$domain_id = $domains_entry["id"];
1229
						}
1230
					}
1231
					if ( ! $domain_id ) {
1232
						log_error("{$err_prefix} " . gettext("no domain ID for domain") . ": '{$this->_dnsDomain}'");
1233
						return false;
1234
					}
1235
					if ($this->_dnsVerboseLog) {
1236
						log_error("_update(): " . sprintf(gettext("found domain id: %s"), $domain_id));
1237
					}
1238

    
1239
					// get existing record if present
1240
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1241
					$records_output = curl_exec($ch);
1242
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1243
					if ( $http_code != 200 )
1244
					{
1245
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1246
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1247
						return false;
1248
					}
1249

    
1250
					$records_result = json_decode($records_output, TRUE);
1251
					foreach($records_result["data"] as $records_entry) {
1252
						if ( $records_entry["type"] == $record_type && $records_entry["name"] == $record_name ) {
1253
							// not adding support for pagination at this time, hope you have < 100 records!
1254
							$record = $records_entry;
1255
						}
1256
					}
1257
					if ($this->_dnsVerboseLog) {
1258
						log_error("_update(): " . ( $record ? sprintf(gettext("found existing record id: %s"), $record["id"]) : gettext("no matching record found") ));
1259
					}
1260

    
1261
					if (is_array($record)) {
1262
						// update existing record
1263
						$record["target"] = $this->_dnsIP;
1264
						$record["ttl_sec"] = (int) $this->_dnsTTL; // linode may round this up, 0 = zone default
1265
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1266
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1267
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records/" . $record["id"]);
1268
					} else {
1269
						// create a new record
1270
						$record = array(
1271
							"type"    => $record_type,
1272
							"name"    => $record_name,
1273
							"target"  => $this->_dnsIP,
1274
							"ttl_sec" => (int) $this->_dnsTTL, // linode may round this up, 0 = zone default
1275
						);
1276
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1277
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
1278
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1279
					}
1280

    
1281
				break;
1282
			case 'gandi-livedns':
1283
				// NOTE: quite similar to digitalocean case but with json content like azure case
1284
				// Get record href
1285
				$server = 'https://dns.api.gandi.net/api/v5/domains/';
1286
				$url = $server . $this->_dnsDomain . '/records';
1287
				curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Api-Key: {$this->_dnsPass}"));
1288
				curl_setopt($ch, CURLOPT_URL, $url);
1289
				$output = json_decode(curl_exec($ch));
1290
				if (!is_array($output)) {
1291
					$output = array();
1292
				}
1293
				foreach($output as $dnsRecord) {
1294
					// NS records are named @ in DO's API, so check type as well
1295
					if ($this->_dnsHost == $dnsRecord->rrset_name && $dnsRecord->rrset_type == 'A') {
1296
						$recordHref = $dnsRecord->rrset_href;
1297
						break;
1298
					}
1299
				}
1300
				$request_headers = array();
1301
				//$request_headers[] = 'Accept: application/json';
1302
				$request_headers[] = 'X-Api-Key: ' . $this->_dnsPass;
1303
				$request_headers[] = 'Content-Type: application/json';
1304
				//create or update record
1305
				if ($recordHref == null) {
1306
					//create record
1307
					$url = $server . $this->_dnsDomain . '/records';
1308
					$body = '{"rrset_type": "A", "rrset_ttl": ' . $this->_dnsTTL . ', "rrset_name": "' . $this->_dnsHost . '", "rrset_values": ["' . $this->_dnsIP . '"]}';
1309
				} else {
1310
					//update record
1311
					$url = $recordHref;
1312
					$body = '{"rrset_ttl": ' . $this->_dnsTTL . ', "rrset_values": ["' . $this->_dnsIP . '"]}';
1313
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1314
				}
1315
				curl_setopt($ch, CURLOPT_URL, $url);
1316
				curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1317
				curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1318
					break;
1319
				default:
1320
					break;
1321
			}
1322
			if ($this->_dnsService != 'ods') {
1323
				curl_setopt($ch, CURLOPT_HEADER, 1);
1324
				$response = curl_exec($ch);
1325
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1326
				$header = substr($response, 0, $header_size);
1327
				$data = substr($response, $header_size);
1328
				$this->_checkStatus($ch, $data, $header);
1329
				@curl_close($ch);
1330
			}
1331
		}
1332

    
1333
		/**
1334
		 * Private Function (added 23 Feb 17)
1335
		 *   Send Removal To Selected Service.
1336
		 *
1337
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1338
		 *   is to remove the existing record and add a new record.
1339
		 *
1340
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1341
		 */
1342
		function _remove($existing_ip = NULL) {
1343
			$remove_allowed = false;
1344
			if ($this->_dnsVerboseLog) {
1345
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1346
			}
1347

    
1348
			if (strstr($this->_dnsRequestIf, "_vip")) {
1349
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1350
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1351
			} else {
1352
				$realparentif = $this->_dnsRequestIf;
1353
			}
1354

    
1355
			$ch = curl_init();
1356

    
1357
			if ($this->_useIPv6 == false) {
1358
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1359
			}
1360

    
1361
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1362
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1363
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1364
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1365

    
1366
			switch ($this->_dnsService) {
1367
			case 'dreamhost':
1368
			case 'dreamhost-v6':
1369
				$server = 'https://api.dreamhost.com/';
1370
				$post_data['key'] = $this->_dnsPass;
1371
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1372
				$post_data['cmd'] = 'dns-remove_record';
1373
				$post_data['format'] = 'json';
1374
				$post_data['value'] = $existing_ip;
1375
				$post_data['record'] = $this->_dnsHost;
1376
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1377
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1378
				$port = "";
1379
				if ($this->_dnsServer) {
1380
					$server = $this->_dnsServer;
1381
				}
1382
				if ($this->_dnsPort) {
1383
					$port = ":" . $this->_dnsPort;
1384
				}
1385
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1386
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1387
				$remove_allowed = true;
1388
				break;
1389
			default:
1390
				break;
1391
			}
1392
			if ($remove_allowed) {
1393
				curl_setopt($ch, CURLOPT_HEADER, 1);
1394
				$response = curl_exec($ch);
1395
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1396
				$header = substr($response, 0, $header_size);
1397
				$data = substr($response, $header_size);
1398
				$this->_checkStatus($ch, $data, $header);
1399
				@curl_close($ch);
1400
			}
1401
		}
1402

    
1403
		/**
1404
		 * Private Function (added 23 Feb 17)
1405
		 * Retrieves current DNS records from an external API source.
1406
		 *
1407
		 * Some services cannot perform new operations without the caller
1408
		 * providing existing record information.
1409
		 */
1410
		function _lookup_current() {
1411
			$lookup_allowed = false;
1412
			if ($this->_dnsVerboseLog) {
1413
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1414
			}
1415

    
1416
			if (strstr($this->_dnsRequestIf, "_vip")) {
1417
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1418
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1419
			} else {
1420
				$realparentif = $this->_dnsRequestIf;
1421
			}
1422

    
1423
			$ch = curl_init();
1424

    
1425
			if ($this->_useIPv6 == false) {
1426
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1427
			}
1428

    
1429
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1430
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1431
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1432
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1433

    
1434
			switch ($this->_dnsService) {
1435
			case 'dreamhost':
1436
			case 'dreamhost-v6':
1437
				$server = 'https://api.dreamhost.com/';
1438
				$post_data['key'] = $this->_dnsPass;
1439
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1440
				$post_data['cmd'] = 'dns-list_records';
1441
				$post_data['format'] = 'json';
1442
				$port = "";
1443
				if ($this->_dnsServer) {
1444
					$server = $this->_dnsServer;
1445
				}
1446
				if ($this->_dnsPort) {
1447
					$port = ":" . $this->_dnsPort;
1448
				}
1449
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1450
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1451
				$lookup_allowed = true;
1452
				break;
1453
			default:
1454
				break;
1455
			}
1456
			if ($lookup_allowed) {
1457
				curl_setopt($ch, CURLOPT_HEADER, 1);
1458
				$response = curl_exec($ch);
1459
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1460
				$header = substr($response, 0, $header_size);
1461
				$data = substr($response, $header_size);
1462
				$this->_checkLookupStatus($ch, $data, $header);
1463
				@curl_close($ch);
1464
			}
1465
		}
1466

    
1467
		/*
1468
		 * Private Function (added 23 Feb 17)
1469
		 *   Retrieve Lookup Status from the provided data and/or header
1470
		 */
1471
		function _checkLookupStatus($ch, $data, $header) {
1472
			if ($this->_dnsVerboseLog) {
1473
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1474
			}
1475
			$success_str = "(" . gettext("Success") . ") ";
1476
			$error_str = "(" . gettext("Error") . ") ";
1477
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1478

    
1479
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1480
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1481
				log_error($status);
1482
				$this->status = $status;
1483
				return;
1484
			}
1485
			switch ($this->_dnsService) {
1486
			case 'dreamhost':
1487
			case 'dreamhost-v6':
1488
				$result = json_decode($data,true);
1489
				if($result["result"] != "success") {
1490
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1491
					$this->_debug($data);
1492
					return;
1493
				} else {
1494
					foreach($result["data"] as $key => $row) {
1495
						if($row["record"] == $this->_dnsHost &&
1496
								(($row["type"] == "A" && !$this->_useIPv6)
1497
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1498
								)) {
1499
							if($row["editable"] == 0) {
1500
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1501
								continue;
1502
							}
1503
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1504
						}
1505
					}
1506
				}
1507
				if (!is_array($this->_existingRecords)){
1508
					if ($this->_dnsVerboseLog) {
1509
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1510
					}
1511
				}
1512
				break;
1513
			default:
1514
				break;
1515
			}
1516
		}
1517

    
1518
		/*
1519
		 * Private Function (added 12 July 2005) [beta]
1520
		 *   Retrieve Update Status
1521
		 */
1522
		function _checkStatus($ch, $data, $header) {
1523
			if ($this->_dnsVerboseLog) {
1524
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1525
			}
1526
			$successful_update = false;
1527
			$success_str = "(" . gettext("Success") . ") ";
1528
			$error_str = "(" . gettext("Error") . ") ";
1529
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1530

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

    
2349
			if ($successful_update == true) {
2350
				/* Write WAN IP to cache file */
2351
				$wan_ip = $this->_checkIP();
2352
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2353
					$currentTime = time();
2354
					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));
2355
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2356
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2357
				} else {
2358
					@unlink($this->_cacheFile);
2359
				}
2360
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2361
					$currentTime = time();
2362
					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));
2363
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2364
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2365
				} else {
2366
					@unlink($this->_cacheFile_v6);
2367
				}
2368
			}
2369
			$this->status = $status;
2370
			log_error($status);
2371
		}
2372

    
2373
		/*
2374
		 * Private Function (added 12 July 05) [beta]
2375
		 *   Return Error, Set Last Error, and Die.
2376
		 */
2377
		function _error($errorNumber = '1') {
2378
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2379
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2380
			switch ($errorNumber) {
2381
				case 0:
2382
					break;
2383
				case 2:
2384
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2385
					break;
2386
				case 3:
2387
					$error = $err_str . gettext('No Username Provided.');
2388
					break;
2389
				case 4:
2390
					$error = $err_str . gettext('No Password Provided.');
2391
					break;
2392
				case 5:
2393
					$error = $err_str . gettext('No Hostname Provided.');
2394
					break;
2395
				case 6:
2396
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2397
					break;
2398
				case 7:
2399
					$error = $err_str . gettext('No Update URL Provided.');
2400
					break;
2401
				case 8:
2402
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2403
					break;
2404
				case 9:
2405
					$status = $err_str_r53 . gettext("Invalid TTL");
2406
					break;
2407
				case 10:
2408
					$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);
2409
					break;
2410
				default:
2411
					$error = $err_str . gettext('Unknown Response.');
2412
					/* FIXME: $data isn't in scope here */
2413
					/* $this->_debug($data); */
2414
					break;
2415
			}
2416
			$this->lastError = $error;
2417
			log_error($error);
2418
		}
2419

    
2420
		/*
2421
		 * Private Function (added 12 July 05) [beta]
2422
		 *   - Detect whether or not IP needs to be updated.
2423
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2424
		 *      | work with other systems. pfSense base is FreeBSD.
2425
		 */
2426
		function _detectChange() {
2427
			global $debug;
2428

    
2429
			if ($debug) {
2430
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2431
			}
2432

    
2433
			$currentTime = time();
2434

    
2435
			$wan_ip = $this->_checkIP();
2436
			if ($wan_ip == 0) {
2437
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2438
				return false;
2439
			}
2440
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
2441

    
2442
			if ($this->_useIPv6 == true) {
2443
				if (file_exists($this->_cacheFile_v6)) {
2444
					$contents = file_get_contents($this->_cacheFile_v6);
2445
					list($cacheIP, $cacheTime) = explode('|', $contents);
2446
					$this->_debug($cacheIP . '/' . $cacheTime);
2447
					$initial = false;
2448
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
2449
				} else {
2450
					$cacheIP = '::';
2451
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
2452
					$cacheTime = $currentTime;
2453
					$initial = true;
2454
					$log_error .= gettext("No Cached IPv6 found.");
2455
				}
2456
			} else {
2457
				if (file_exists($this->_cacheFile)) {
2458
					$contents = file_get_contents($this->_cacheFile);
2459
					list($cacheIP, $cacheTime) = explode('|', $contents);
2460
					$this->_debug($cacheIP . '/' . $cacheTime);
2461
					$initial = false;
2462
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
2463
				} else {
2464
					$cacheIP = '0.0.0.0';
2465
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
2466
					$cacheTime = $currentTime;
2467
					$initial = true;
2468
					$log_error .= gettext("No Cached IP found.");
2469
				}
2470
			}
2471
			if ($this->_dnsVerboseLog) {
2472
				log_error($log_error);
2473
			}
2474

    
2475
			// Convert seconds = days * hr/day * min/hr * sec/min
2476
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60;
2477

    
2478
			$needs_updating = FALSE;
2479
			/* lets determine if the item needs updating */
2480
			if ($cacheIP != $wan_ip) {
2481
				$needs_updating = true;
2482
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
2483
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
2484
			}
2485
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
2486
				$needs_updating = true;
2487
				$this->_forceUpdateNeeded = true;
2488
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
2489
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
2490
			}
2491
			if ($initial == true) {
2492
				$needs_updating = true;
2493
				$update_reason .= gettext("Initial update.");
2494
			}
2495

    
2496
			/*   finally if we need updating then store the
2497
			 *   new cache value and return true
2498
			 */
2499
			if ($needs_updating == true) {
2500
				if ($this->_dnsVerboseLog) {
2501
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
2502
				}
2503
				return true;
2504
			}
2505

    
2506
			return false;
2507
		}
2508

    
2509
		/*
2510
		 * Private Function (added 16 July 05) [beta]
2511
		 *   - Writes debug information to a file.
2512
		 *   - This function is only called when a unknown response
2513
		 *   - status is returned from a DynDNS service provider.
2514
		 */
2515
		function _debug($data) {
2516
			global $g;
2517

    
2518
			if (!$g['debug']) {
2519
				return;
2520
			}
2521
			$string = date('m-d-y h:i:s') . ' - (' . $this->_debugID . ') - [' . $this->_dnsService . '] - ' . $data . "\n";
2522
			$file = fopen($this->_debugFile, 'a');
2523
			fwrite($file, $string);
2524
			fclose($file);
2525
		}
2526
		function _checkIP() {
2527
			global $debug;
2528

    
2529
			if ($debug) {
2530
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
2531
			}
2532

    
2533
			if ($this->_useIPv6 == true) {
2534
				$ip_address = get_interface_ipv6($this->_if);
2535
				if (!is_ipaddrv6($ip_address)) {
2536
					return 0;
2537
				}
2538
			} else {
2539
				$ip_address = dyndnsCheckIP($this->_if);
2540
				if (!is_ipaddr($ip_address)) {
2541
					return 0;
2542
				}
2543
			}
2544
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
2545
				if (is_ipaddr($ip_address)) {
2546
					if ($this->_dnsVerboseLog) {
2547
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
2548
					}
2549
				} else {
2550
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
2551
					return 0;
2552
				}
2553
			} else {
2554
				if ($this->_dnsVerboseLog) {
2555
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
2556
				}
2557
			}
2558
			$this->_dnsIP = $ip_address;
2559

    
2560
			return $ip_address;
2561
		}
2562

    
2563
	}
2564

    
2565
?>
(15-15/60)