Project

General

Profile

Bug #10196 » dyndns.class.fix.txt

Edited file - János K, 01/21/2020 05:48 AM

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

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

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

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

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

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

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

    
211
			$dyndnslck = lock("DDNS".$dnsID, LOCK_EX);
212

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

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

    
318
			// Ensure that we were able to lookup the IP
319
			if (!is_ipaddr($this->_dnsIP)) {
320
				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));
321
				unlock($dyndnslck);
322
				return;
323
			}
324

    
325
			$this->_debugID = rand(1000000, 9999999);
326

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

    
407
			unlock($dyndnslck);
408
		}
409

    
410
		/*
411
		 * Private Function (added 12 July 05) [beta]
412
		 *   Send Update To Selected Service.
413
		 */
414
		function _update() {
415

    
416
			if ($this->_dnsVerboseLog) {
417
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
418
			}
419

    
420
			if (strstr($this->_dnsRequestIf, "_vip")) {
421
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
422
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
423
			} else {
424
				$realparentif = $this->_dnsRequestIf;
425
			}
426

    
427
			$ch = curl_init();
428

    
429
			if ($this->_useIPv6 == false) {
430
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
431
			}
432

    
433
			if ($this->_dnsService != 'ods') {
434
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
435
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
436
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
437
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
438
			}
439

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

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

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

    
783
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
784
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
785
						'X-Auth-Email: '.$this->_dnsUser.'',
786
						'X-Auth-Key: '.$this->_dnsPass.'',
787
						'Content-Type: application/json'
788
					));
789

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

    
934
					//step 1: login to API
935
					$post_data['username'] = $this->_dnsUser;
936
					$post_data['password'] = $this->_dnsPass;
937
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
938
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
939
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
940
					$output = curl_exec($ch);
941

    
942
					//extract the cookies
943
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
944
					if( count($cookies[1]) > 0 ){
945
						$cookie_data = implode("; ",$cookies[1]);
946
					}
947

    
948
					//step 2: find the id of the A record
949
					$post_data = null;
950
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
951
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
952
					curl_setopt($ch, CURLOPT_HEADER, 0);
953
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
954
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
955

    
956
					$output = curl_exec($ch);
957
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$this->_dnsDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$this->_dnsHost}\".*?\$/", $output, $hostID);
958
					$hostID = $hostID[1];
959
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$this->_dnsDomain}.*?entries.*?{[^\}]*?\"name\":\"{$this->_dnsHost}\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
960
					$hostIP = $hostIP[1];
961

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

    
1016
					// Create/update record
1017
					if ($recordID == null) {
1018
						$url = $server . $this->_dnsDomain . '/records';
1019
					} else {
1020
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1021
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1022
					}
1023
					$post_data['type'] = 'A';
1024
					$post_data['ttl'] = $this->_dnsTTL;
1025
					$post_data['name'] = $this->_dnsHost;
1026
					$post_data['data'] = $this->_dnsIP;
1027
					curl_setopt($ch, CURLOPT_URL, $url);
1028
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1029
					break;
1030
				case 'cloudns':
1031
					/* Uses ClouDNS REST API
1032
					   Requires auth-id or sub-auth-id or sub-auth-user */
1033
					// Step 1: Find the Record ID
1034
					$url = 'https://api.cloudns.net/dns/records.json';
1035
					$post_data['auth-id'] = $this->_dnsUser;
1036
					$post_data['auth-password'] = $this->_dnsPass;
1037
					$post_data['domain-name'] = $this->_dnsDomain;
1038
					$post_data['host'] = $this->_dnsHost;
1039
					$post_data['type'] = 'a';
1040
					curl_setopt($ch, CURLOPT_URL, $url);
1041
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1042
					$output = json_decode(curl_exec($ch));
1043
					$recordID = key(get_object_vars($output));
1044

    
1045
					// Step 2: Set the record
1046
					$needsIP = TRUE;
1047
					$url = 'https://api.cloudns.net/dns/mod-record.json';
1048
					$post_data = array();
1049
					$post_data['auth-id'] = $this->_dnsUser;
1050
					$post_data['auth-password'] = $this->_dnsPass;
1051
					$post_data['domain-name'] = $this->_dnsDomain;
1052
					$post_data['record-id'] = $recordID;
1053
					$post_data['host'] = $this->_dnsHost;
1054
					$post_data['record'] = $this->_dnsIP;
1055
					$post_data['ttl'] = $this->_dnsTTL;
1056
					curl_setopt($ch, CURLOPT_URL, $url);
1057
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1058
					break;
1059
			case 'azurev6':
1060
			case 'azure':
1061
				$hostname = "{$this->_dnsHost}";
1062
				$resourceid = trim($this->_dnsZoneID);
1063
				$app_id = $this->_dnsUser;
1064
				$client_secret = $this->_dnsPass;
1065
				$newip = $this->_dnsIP;
1066
				$newttl = $this->_dnsTTL;
1067
					// ensure resourceid starts with / and has no trailing /
1068
				$resourceid = '/' . trim($resourceid, '/');
1069
					// extract subscription id from resource id
1070
				preg_match('/\\/subscriptions\\/(?<sid>[^\\/]*)/', $resourceid, $result);
1071
				$subscriptionid = isset($result['sid']) ? $result['sid'] : '';
1072
				if (isset($result['sid'])) {
1073
					$subscriptionid = $result['sid'];
1074
				} else {
1075
					log_error("Azure subscription id not found in resource id");
1076
					return false;
1077
				}
1078
					// find tenant id from subscription id
1079
				curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01");
1080
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1081
				curl_setopt($ch, CURLOPT_HEADER, 1);
1082
				curl_setopt($ch, CURLOPT_NOBODY, 1);
1083
				$output = curl_exec($ch);
1084
				$pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?<tid>[^"]*)/i';
1085
				preg_match($pattern, $output, $result);
1086
				if (isset($result['tid'])) {
1087
					$tenantid = $result['tid'];
1088
				} else {
1089
					log_error("Tenant ID not found");
1090
					return false;
1091
				}
1092
					// get an bearer token
1093
				curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token");
1094
				curl_setopt($ch, CURLOPT_POST, 1);
1095
				$body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret);
1096
				curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1097
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1098
				$server_output = curl_exec($ch);
1099
				$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1100
				preg_match("/\"access_token\":\"(?<tok>[^\"]*)\"/", $server_output, $result);
1101
				if (isset($result['tok'])) {
1102
					$bearertoken = $result['tok'];
1103
				} else {
1104
					log_error("no valid bearer token");
1105
					return false;
1106
				}
1107
					// Update the DNS record
1108
				if ($this->_useIPv6) {
1109
					$url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01";
1110
					$body = '{"properties":{"TTL":"' . $newttl . '", "AaaaRecords":[{"ipv6Address":"' . $newip . '"}]}}';
1111
				} else {
1112
					$url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01";
1113
					$body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}';
1114
				}
1115
				$request_headers = array();
1116
				$request_headers[] = 'Accept: application/json';
1117
				$request_headers[] = 'Authorization: Bearer ' . $bearertoken;
1118
				$request_headers[] = 'Content-Type: application/json';
1119
				curl_setopt($ch, CURLOPT_URL, $url);
1120
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1121
				curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1122
				curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1123
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1124
				curl_setopt($ch, CURLOPT_HEADER, 1);
1125
				curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1126
				break;
1127

    
1128
			default:
1129
				break;
1130
			}
1131
			if ($this->_dnsService != 'ods') {
1132
				curl_setopt($ch, CURLOPT_HEADER, 1);
1133
				$response = curl_exec($ch);
1134
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1135
				$header = substr($response, 0, $header_size);
1136
				$data = substr($response, $header_size);
1137
				$this->_checkStatus($ch, $data, $header);
1138
				@curl_close($ch);
1139
			}
1140
		}
1141

    
1142
		/**
1143
		 * Private Function (added 23 Feb 17)
1144
		 *   Send Removal To Selected Service.
1145
		 *
1146
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1147
		 *   is to remove the existing record and add a new record.
1148
		 *
1149
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1150
		 */
1151
		function _remove($existing_ip = NULL) {
1152
			$remove_allowed = false;
1153
			if ($this->_dnsVerboseLog) {
1154
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1155
			}
1156

    
1157
			if (strstr($this->_dnsRequestIf, "_vip")) {
1158
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1159
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1160
			} else {
1161
				$realparentif = $this->_dnsRequestIf;
1162
			}
1163

    
1164
			$ch = curl_init();
1165

    
1166
			if ($this->_useIPv6 == false) {
1167
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1168
			}
1169

    
1170
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1171
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1172
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1173
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1174

    
1175
			switch ($this->_dnsService) {
1176
			case 'dreamhost':
1177
			case 'dreamhost-v6':
1178
				$server = 'https://api.dreamhost.com/';
1179
				$post_data['key'] = $this->_dnsPass;
1180
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1181
				$post_data['cmd'] = 'dns-remove_record';
1182
				$post_data['format'] = 'json';
1183
				$post_data['value'] = $existing_ip;
1184
				$post_data['record'] = $this->_dnsHost;
1185
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1186
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1187
				$port = "";
1188
				if ($this->_dnsServer) {
1189
					$server = $this->_dnsServer;
1190
				}
1191
				if ($this->_dnsPort) {
1192
					$port = ":" . $this->_dnsPort;
1193
				}
1194
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1195
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1196
				$remove_allowed = true;
1197
				break;
1198
			default:
1199
				break;
1200
			}
1201
			if ($remove_allowed) {
1202
				curl_setopt($ch, CURLOPT_HEADER, 1);
1203
				$response = curl_exec($ch);
1204
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1205
				$header = substr($response, 0, $header_size);
1206
				$data = substr($response, $header_size);
1207
				$this->_checkStatus($ch, $data, $header);
1208
				@curl_close($ch);
1209
			}
1210
		}
1211

    
1212
		/**
1213
		 * Private Function (added 23 Feb 17)
1214
		 * Retrieves current DNS records from an external API source.
1215
		 *
1216
		 * Some services cannot perform new operations without the caller
1217
		 * providing existing record information.
1218
		 */
1219
		function _lookup_current() {
1220
			$lookup_allowed = false;
1221
			if ($this->_dnsVerboseLog) {
1222
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1223
			}
1224

    
1225
			if (strstr($this->_dnsRequestIf, "_vip")) {
1226
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1227
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1228
			} else {
1229
				$realparentif = $this->_dnsRequestIf;
1230
			}
1231

    
1232
			$ch = curl_init();
1233

    
1234
			if ($this->_useIPv6 == false) {
1235
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1236
			}
1237

    
1238
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1239
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1240
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1241
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1242

    
1243
			switch ($this->_dnsService) {
1244
			case 'dreamhost':
1245
			case 'dreamhost-v6':
1246
				$server = 'https://api.dreamhost.com/';
1247
				$post_data['key'] = $this->_dnsPass;
1248
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1249
				$post_data['cmd'] = 'dns-list_records';
1250
				$post_data['format'] = 'json';
1251
				$port = "";
1252
				if ($this->_dnsServer) {
1253
					$server = $this->_dnsServer;
1254
				}
1255
				if ($this->_dnsPort) {
1256
					$port = ":" . $this->_dnsPort;
1257
				}
1258
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1259
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1260
				$lookup_allowed = true;
1261
				break;
1262
			default:
1263
				break;
1264
			}
1265
			if ($lookup_allowed) {
1266
				curl_setopt($ch, CURLOPT_HEADER, 1);
1267
				$response = curl_exec($ch);
1268
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1269
				$header = substr($response, 0, $header_size);
1270
				$data = substr($response, $header_size);
1271
				$this->_checkLookupStatus($ch, $data, $header);
1272
				@curl_close($ch);
1273
			}
1274
		}
1275

    
1276
		/*
1277
		 * Private Function (added 23 Feb 17)
1278
		 *   Retrieve Lookup Status from the provided data and/or header
1279
		 */
1280
		function _checkLookupStatus($ch, $data, $header) {
1281
			if ($this->_dnsVerboseLog) {
1282
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1283
			}
1284
			$success_str = "(" . gettext("Success") . ") ";
1285
			$error_str = "(" . gettext("Error") . ") ";
1286
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1287

    
1288
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1289
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1290
				log_error($status);
1291
				$this->status = $status;
1292
				return;
1293
			}
1294
			switch ($this->_dnsService) {
1295
			case 'dreamhost':
1296
			case 'dreamhost-v6':
1297
				$result = json_decode($data,true);
1298
				if($result["result"] != "success") {
1299
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1300
					$this->_debug($data);
1301
					return;
1302
				} else {
1303
					foreach($result["data"] as $key => $row) {
1304
						if($row["record"] == $this->_dnsHost &&
1305
								(($row["type"] == "A" && !$this->_useIPv6)
1306
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1307
								)) {
1308
							if($row["editable"] == 0) {
1309
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1310
								continue;
1311
							}
1312
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1313
						}
1314
					}
1315
				}
1316
				if (!is_array($this->_existingRecords)){
1317
					if ($this->_dnsVerboseLog) {
1318
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1319
					}
1320
				}
1321
				break;
1322
			default:
1323
				break;
1324
			}
1325
		}
1326

    
1327
		/*
1328
		 * Private Function (added 12 July 2005) [beta]
1329
		 *   Retrieve Update Status
1330
		 */
1331
		function _checkStatus($ch, $data, $header) {
1332
			if ($this->_dnsVerboseLog) {
1333
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1334
			}
1335
			$successful_update = false;
1336
			$success_str = "(" . gettext("Success") . ") ";
1337
			$error_str = "(" . gettext("Error") . ") ";
1338
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1339

    
1340
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1341
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1342
				log_error($status);
1343
				$this->status = $status;
1344
				return;
1345
			}
1346
			switch ($this->_dnsService) {
1347
				case 'glesys':
1348
					$status_intro = "GleSYS ({$this->_dnsHost}): ";
1349
					if (preg_match('/Record updated/i', $data)) {
1350
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1351
						$successful_update = true;
1352
					} else {
1353
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1354
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1355
						$this->_debug($data);
1356
					}
1357
					break;
1358
				case 'dnsomatic':
1359
					$status_intro = "DNS-O-Matic ({$this->_dnsHost}): ";
1360
					if (preg_match('/badauth/i', $data)) {
1361
						$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.");
1362
					} else if (preg_match('/notfqdn /i', $data)) {
1363
						$status = $status_intro . gettext("The hostname specified is not a fully-qualified domain name. If no hostnames included, notfqdn will be returned once.");
1364
					} else if (preg_match('/nohost/i', $data)) {
1365
						$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.");
1366
					} else if (preg_match('/numhost/i', $data)) {
1367
						$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.");
1368
					} else if (preg_match('/abuse/i', $data)) {
1369
						$status = $status_intro . gettext("The hostname is blocked for update abuse.");
1370
					} else if (preg_match('/good/i', $data)) {
1371
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1372
						$successful_update = true;
1373
					} else if (preg_match('/dnserr/i', $data)) {
1374
						$status = $status_intro . gettext("DNS error encountered. Stop updating for 30 minutes.");
1375
					} else {
1376
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1377
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1378
						$this->_debug($data);
1379
					}
1380
					break;
1381
				case 'citynetwork':
1382
					if (preg_match('/notfqdn/i', $data)) {
1383
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1384
					} else if (preg_match('/nohost/i', $data)) {
1385
						$status = $status_intro . $error_str . gettext("No such host");
1386
					} else if (preg_match('/nochg/i', $data)) {
1387
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1388
						$successful_update = true;
1389
					} else if (preg_match('/good/i', $data)) {
1390
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1391
						$successful_update = true;
1392
					} else if (preg_match('/badauth/i', $data)) {
1393
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1394
					} else {
1395
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1396
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1397
						$this->_debug($data);
1398
					}
1399
					break;
1400
				case 'ovh-dynhost':
1401
				case 'dyndns':
1402
					if (preg_match('/notfqdn/i', $data)) {
1403
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1404
					} else if (preg_match('/nochg/i', $data)) {
1405
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1406
						$successful_update = true;
1407
					} else if (preg_match('/good/i', $data)) {
1408
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1409
						$successful_update = true;
1410
					} else if (preg_match('/noauth/i', $data)) {
1411
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1412
					} else {
1413
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1414
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1415
						$this->_debug($data);
1416
					}
1417
					break;
1418
				case 'dyndns-static':
1419
					if (preg_match('/notfqdn/i', $data)) {
1420
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1421
					} else if (preg_match('/nochg/i', $data)) {
1422
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1423
						$successful_update = true;
1424
					} else if (preg_match('/good/i', $data)) {
1425
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1426
						$successful_update = true;
1427
					} else if (preg_match('/noauth/i', $data)) {
1428
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1429
					} else {
1430
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1431
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1432
						$this->_debug($data);
1433
					}
1434
					break;
1435
				case 'dyndns-custom':
1436
					if (preg_match('/notfqdn/i', $data)) {
1437
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1438
					} else if (preg_match('/nochg/i', $data)) {
1439
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1440
						$successful_update = true;
1441
					} else if (preg_match('/good/i', $data)) {
1442
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1443
						$successful_update = true;
1444
					} else if (preg_match('/noauth/i', $data)) {
1445
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1446
					} else {
1447
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1448
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1449
						$this->_debug($data);
1450
					}
1451
					break;
1452
				case 'dhs':
1453
					break;
1454
				case 'noip':
1455
				case 'noip-free':
1456
					list($ip, $code) = explode(":", $data);
1457
					switch ($code) {
1458
						case 0:
1459
							$status = $status_intro . $success_str . gettext("IP address is current, no update performed.");
1460
							$successful_update = true;
1461
							break;
1462
						case 1:
1463
							$status = $status_intro . $success_str . gettext("DNS hostname update successful.");
1464
							$successful_update = true;
1465
							break;
1466
						case 2:
1467
							$status = $status_intro . $error_str . gettext("Hostname supplied does not exist.");
1468
							break;
1469
						case 3:
1470
							$status = $status_intro . $error_str . gettext("Invalid Username.");
1471
							break;
1472
						case 4:
1473
							$status = $status_intro . $error_str . gettext("Invalid Password.");
1474
							break;
1475
						case 5:
1476
							$status = $status_intro . $error_str . gettext("Too many updates sent.");
1477
							break;
1478
						case 6:
1479
							$status = $status_intro . $error_str . gettext("Account disabled due to violation of No-IP terms of service.");
1480
							break;
1481
						case 7:
1482
							$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.");
1483
							break;
1484
						case 8:
1485
							$status = $status_intro . $error_str . gettext("Disabled / Locked Hostname.");
1486
							break;
1487
						case 9:
1488
							$status = $status_intro . $error_str . gettext("Host updated is configured as a web redirect and no update was performed.");
1489
							break;
1490
						case 10:
1491
							$status = $status_intro . $error_str . gettext("Group supplied does not exist.");
1492
							break;
1493
						case 11:
1494
							$status = $status_intro . $success_str . gettext("DNS group update is successful.");
1495
							$successful_update = true;
1496
							break;
1497
						case 12:
1498
							$status = $status_intro . $success_str . gettext("DNS group is current, no update performed.");
1499
							$successful_update = true;
1500
							break;
1501
						case 13:
1502
							$status = $status_intro . $error_str . gettext("Update client support not available for supplied hostname or group.");
1503
							break;
1504
						case 14:
1505
							$status = $status_intro . $error_str . gettext("Hostname supplied does not have offline settings configured.");
1506
							break;
1507
						case 99:
1508
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
1509
							break;
1510
						case 100:
1511
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
1512
							break;
1513
						default:
1514
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1515
							$this->_debug(gettext("Unknown Response:") . " " . $data);
1516
							break;
1517
					}
1518
					break;
1519
				case 'easydns':
1520
					if (preg_match('/NOACCESS/i', $data)) {
1521
						$status = $status_intro . $error_str . gettext("Authentication Failed: Username and/or Password was Incorrect.");
1522
					} else if (preg_match('/NOSERVICE/i', $data)) {
1523
						$status = $status_intro . $error_str . gettext("No Service: Dynamic DNS Service has been disabled for this domain.");
1524
					} else if (preg_match('/ILLEGAL INPUT/i', $data)) {
1525
						$status = $status_intro . $error_str . gettext("Illegal Input: Self-Explanatory");
1526
					} else if (preg_match('/TOOSOON/i', $data)) {
1527
						$status = $status_intro . $error_str . gettext("Too Soon: Not Enough Time Has Elapsed Since Last Update");
1528
					} else if (preg_match('/NOERROR/i', $data)) {
1529
						$status = $status_intro . $success_str . gettext("IP Updated Successfully!");
1530
						$successful_update = true;
1531
					} else {
1532
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1533
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1534
						$this->_debug($data);
1535
					}
1536
					break;
1537
				case 'hn':
1538
					/* FIXME: add checks */
1539
					break;
1540
				case 'zoneedit':
1541
					if (preg_match('/799/i', $data)) {
1542
						$status = $status_intro . "(" . gettext("Error 799") . ") " . gettext("Update Failed!");
1543
					} else if (preg_match('/700/i', $data)) {
1544
						$status = $status_intro . "(" . gettext("Error 700") . ") " . gettext("Update Failed!");
1545
					} else if (preg_match('/200/i', $data)) {
1546
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1547
						$successful_update = true;
1548
					} else if (preg_match('/201/i', $data)) {
1549
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1550
						$successful_update = true;
1551
					} else {
1552
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1553
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1554
						$this->_debug($data);
1555
					}
1556
					break;
1557
				case 'dyns':
1558
					if (preg_match("/400/i", $data)) {
1559
						$status = $status_intro . $error_str . gettext("Bad Request - The URL was malformed. Required parameters were not provided.");
1560
					} else if (preg_match('/402/i', $data)) {
1561
						$status = $status_intro . $error_str . gettext("Update Too Soon - Attempted to update too quickly since last change.");
1562
					} else if (preg_match('/403/i', $data)) {
1563
						$status = $status_intro . $error_str . gettext("Database Error - There was a server-sided database error.");
1564
					} else if (preg_match('/405/i', $data)) {
1565
						$status = $status_intro . $error_str . sprintf(gettext('Hostname Error - The hostname (%1$s) doesn\'t belong to user (%2$s).'), $this->_dnsHost, $this->_dnsUser);
1566
					} else if (preg_match('/200/i', $data)) {
1567
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1568
						$successful_update = true;
1569
					} else {
1570
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1571
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1572
						$this->_debug($data);
1573
					}
1574
					break;
1575
				case 'ods':
1576
					if (preg_match("/299/i", $data)) {
1577
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1578
						$successful_update = true;
1579
					} else {
1580
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1581
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1582
						$this->_debug($data);
1583
					}
1584
					break;
1585
				case 'freedns':
1586
				case 'freedns-v6':
1587
					if (preg_match("/has not changed./i", $data)) {
1588
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1589
						$successful_update = true;
1590
					} else if (preg_match("/Updated/i", $data)) {
1591
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1592
						$successful_update = true;
1593
					} else {
1594
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1595
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1596
						$this->_debug($data);
1597
					}
1598
					break;
1599
				case 'dnsexit':
1600
					if (preg_match("/is the same/i", $data)) {
1601
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1602
						$successful_update = true;
1603
					} else if (preg_match("/Success/i", $data)) {
1604
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1605
						$successful_update = true;
1606
					} else {
1607
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1608
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1609
						$this->_debug($data);
1610
					}
1611
					break;
1612
				case 'loopia':
1613
					if (preg_match("/nochg/i", $data)) {
1614
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1615
						$successful_update = true;
1616
					} else if (preg_match("/good/i", $data)) {
1617
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1618
						$successful_update = true;
1619
					} else if (preg_match('/badauth/i', $data)) {
1620
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1621
					} else {
1622
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1623
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1624
						$this->_debug($data);
1625
					}
1626
					break;
1627
				case 'opendns':
1628
					if (preg_match('/badauth/i', $data)) {
1629
						$status = $status_intro . $error_str . gettext("Not a valid username or password!");
1630
					} else if (preg_match('/nohost/i', $data)) {
1631
						$status = $status_intro . $error_str . gettext("Hostname specified does not exist.");
1632
						$successful_update = true;
1633
					} else if (preg_match('/good/i', $data)) {
1634
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1635
						$successful_update = true;
1636
					} else if (preg_match('/yours/i', $data)) {
1637
						$status = $status_intro . $error_str . gettext("Hostname specified exists, but not under the username specified.");
1638
					} else if (preg_match('/abuse/i', $data)) {
1639
						$status = $status_intro . $error_str . gettext("Updating too frequently, considered abuse.");
1640
					} else {
1641
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1642
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1643
						$this->_debug($data);
1644
					}
1645
					break;
1646
				case 'staticcling':
1647
					if (preg_match("/invalid ip/i", $data)) {
1648
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
1649
					} else if (preg_match('/required info missing/i', $data)) {
1650
						$status = $status_intro . $error_str . gettext("Bad Request - Required parameters were not provided.");
1651
					} else if (preg_match('/invalid characters/i', $data)) {
1652
						$status = $status_intro . $error_str . gettext("Bad Request - Illegal characters in either the username or the password.");
1653
					} else if (preg_match('/bad password/i', $data)) {
1654
						$status = $status_intro . $error_str . gettext("Invalid password.");
1655
					} else if (preg_match('/account locked/i', $data)) {
1656
						$status = $status_intro . $error_str . gettext("This account has been administratively locked.");
1657
					} else if (preg_match('/update too frequent/i', $data)) {
1658
						$status = $status_intro . $error_str . gettext("Updating too frequently.");
1659
					} else if (preg_match('/DB error/i', $data)) {
1660
						$status = $status_intro . $error_str . gettext("Server side error.");
1661
					} else if (preg_match('/success/i', $data)) {
1662
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1663
						$successful_update = true;
1664
					} else {
1665
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1666
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1667
						$this->_debug($data);
1668
					}
1669
					break;
1670
				case 'namecheap':
1671
					$tmp = str_replace("^M", "", $data);
1672
					$ncresponse = @xml2array($tmp);
1673
					if (preg_match("/internal server error/i", $data)) {
1674
						$status = $status_intro . $error_str . gettext("Server side error.");
1675
					} else if (preg_match("/request is badly formed/i", $data)) {
1676
						$status = $status_intro . $error_str . gettext("Badly Formed Request (check the settings).");
1677
					} else if ($ncresponse['interface-response']['ErrCount'] === "0") {
1678
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1679
						$successful_update = true;
1680
					} else if (is_numeric($ncresponse['interface-response']['ErrCount']) && ($ncresponse['interface-response']['ErrCount'] > 0)) {
1681
						$status = $status_intro . $error_str . implode(", ", $ncresponse["interface-response"]["errors"]);
1682
						$successful_update = true;
1683
					} else {
1684
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1685
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1686
						$this->_debug($data);
1687
					}
1688
					break;
1689
				case 'duiadns':
1690
				case 'duiadns-v6':
1691
					if (preg_match("/error/i", $data)) {
1692
						$status = $status_intro . $error_str . gettext("Server side error.");
1693
					} else if (preg_match('/nohost/i', $data)) {
1694
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
1695
					} else if (preg_match('/badauth/i', $data)) {
1696
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
1697
					} else if (preg_match('/good/i', $data)) {
1698
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1699
						$successful_update = true;
1700
					} else if (preg_match('/nochg/i', $data)) {
1701
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
1702
						$successful_update = true;
1703
					} else {
1704
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1705
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1706
						$this->_debug($data);
1707
					}
1708
					break;
1709
				case 'he-net':
1710
				case 'he-net-v6':
1711
					if (preg_match("/badip/i", $data)) {
1712
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
1713
					} else if (preg_match('/nohost/i', $data)) {
1714
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
1715
					} else if (preg_match('/badauth/i', $data)) {
1716
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
1717
					} else if (preg_match('/good/i', $data)) {
1718
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1719
						$successful_update = true;
1720
					} else if (preg_match('/nochg/i', $data)) {
1721
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
1722
						$successful_update = true;
1723
					} else {
1724
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1725
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1726
						$this->_debug($data);
1727
					}
1728
					break;
1729
				case 'he-net-tunnelbroker':
1730
					/*
1731
					-ERROR: Missing parameter(s).
1732
					-ERROR: Invalid API key or password
1733
					-ERROR: Tunnel not found
1734
					-ERROR: Another tunnel exists for this IP.
1735
					-ERROR: This tunnel is already associated with this IP address
1736
					+OK: Tunnel endpoint updated to: x.x.x.x
1737
					*/
1738
					if (preg_match("/Missing parameter/i", $data)) {
1739
						$status = $status_intro . $error_str . gettext("Bad Request - Missing/Invalid Parameters.");
1740
					} else if (preg_match('/Tunnel not found/i', $data)) {
1741
						$status = $status_intro . $error_str . gettext("Bad Request - Invalid Tunnel ID.");
1742
					} else if (preg_match('/Invalid API key or password/i', $data)) {
1743
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
1744
					} else if (preg_match('/OK:/i', $data)) {
1745
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1746
						$successful_update = true;
1747
					} else if (preg_match('/This tunnel is already associated with this IP address/i', $data)) {
1748
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
1749
						$successful_update = true;
1750
					} else {
1751
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1752
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1753
						$this->_debug($data);
1754
					}
1755
					break;
1756
				case 'selfhost':
1757
					if (preg_match('/notfqdn/i', $data)) {
1758
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1759
					} else if (preg_match('/nochg/i', $data)) {
1760
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
1761
						$successful_update = true;
1762
					} else if (preg_match('/good/i', $data)) {
1763
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1764
						$successful_update = true;
1765
					} else if (preg_match('/noauth/i', $data)) {
1766
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1767
					} else {
1768
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1769
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1770
						$this->_debug($data);
1771
					}
1772
					break;
1773
				case 'route53':
1774
				case 'route53-v6':
1775
					if(preg_match('/ErrorResponse/', $data)){
1776
						$status = $status_intro . $error_str . gettext("Route53 API call failed");
1777
						log_error(sprintf("error message: %s", $data));
1778
						$status_update = false;
1779
					} else {
1780
						$status = $status_intro . $success_str . gettext("IP address changed successfully");
1781
						$successful_update = true;
1782
					}
1783
					break;
1784
				case 'custom':
1785
				case 'custom-v6':
1786
					$successful_update = false;
1787
					if ($this->_dnsResultMatch == "") {
1788
						$successful_update = true;
1789
					} else {
1790
						$this->_dnsResultMatch = str_replace("%IP%", $this->_dnsIP, $this->_dnsResultMatch);
1791
						$matches = preg_split("/(?<!\\\\)\\|/", $this->_dnsResultMatch);
1792
						foreach ($matches as $match) {
1793
							$match= str_replace("\\|", "|", $match);
1794
							if (strcmp($match, trim($data, "\t\n\r")) == 0) {
1795
								$successful_update = true;
1796
							}
1797
						}
1798
						unset ($matches);
1799
					}
1800
					if ($successful_update == true) {
1801
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1802
					} else {
1803
						$status = $status_intro . $error_str . gettext("Result did not match.") . " [" . $data . "]";
1804
					}
1805
					break;
1806
				case 'cloudflare-v6':
1807
				case 'cloudflare':
1808
					$output = json_decode($data);
1809
					if ($output->result->content === $this->_dnsIP) {
1810
						$status = $status_intro . $success_str . sprintf(gettext('%1$s updated to %2$s'), $this->_dnsHost, $this->_dnsIP);
1811
						$successful_update = true;
1812
					} elseif ($output->errors[0]->code === 9103) {
1813
						$status = $status_intro . $error_str . gettext("Invalid Credentials! Don't forget to use API Key for password field with Cloudflare.");
1814
					} elseif (($output->success) && (!$output->result[0]->id)) {
1815
						$status = $status_intro . $error_str . gettext("Zone or Host ID was not found, check the hostname.");
1816
					} else {
1817
						$status = $status_intro . gettext("UNKNOWN ERROR") . " - " . $output->errors[0]->message;
1818
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1819
					}
1820
					break;
1821
				case 'eurodns':
1822
					if (preg_match('/notfqdn/i', $data)) {
1823
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1824
					} else if (preg_match('/nochg/i', $data)) {
1825
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1826
						$successful_update = true;
1827
					} else if (preg_match('/good/i', $data)) {
1828
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1829
						$successful_update = true;
1830
					} else if (preg_match('/badauth/i', $data)) {
1831
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1832
					} else {
1833
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1834
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1835
						$this->_debug($data);
1836
					}
1837
					break;
1838
				case 'gratisdns':
1839
					if (preg_match('/Forkerte værdier/i', $data)) {
1840
						$status = $status_intro . $error_str . gettext("Wrong values - Update could not be completed.");
1841
					} else if (preg_match('/Bruger login: Bruger eksistere ikke/i', $data)) {
1842
						$status = $status_intro . $error_str . gettext("Unknown username - User does not exist.");
1843
					} else if (preg_match('/Bruger login: 1Fejl i kodeord/i', $data)) {
1844
						$status = $status_intro . $error_str . gettext("Wrong password - Remember password is case sensitive.");
1845
					} else if (preg_match('/Domæne kan IKKE administreres af bruger/i', $data)) {
1846
						$status = $status_intro . $error_str . gettext("User unable to administer the selected domain.");
1847
					} else if (preg_match('/OK/i', $data)) {
1848
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1849
						$successful_update = true;
1850
					} else {
1851
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1852
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1853
						$this->_debug($data);
1854
					}
1855
					break;
1856
				case 'digitalocean':
1857
					// Creating new records returns an HTTP 201, updating existing records get 200
1858
					// https://redmine.pfsense.org/issues/9171
1859
					if (preg_match("/HTTP\/2\s20[0,1]/i", $header)) {
1860
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1861
						$successful_update = true;
1862
					} else {
1863
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1864
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1865
						$this->_debug($data);
1866
					}
1867
					break;
1868
				case 'dnsimple':
1869
					/* Responds with HTTP 200 on success.
1870
					   Responds with HTTP 4xx on error.
1871
					   Returns JSON data as body */
1872
;
1873
					if (preg_match("/\s200\sOK/i", $header)) {
1874
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1875
						$successful_update = true;
1876
					} else if (preg_match("/\s4\d\d\s/i", $header)) {
1877
						$arrbody = json_decode($data, true);
1878
						$message = $arrbody['message'] . ".";
1879
						if (isset($arrbody['errors']['content'])) {
1880
							foreach ($arrbody['errors']['content'] as $key => $content) {
1881
								$message .= " " . $content . ".";
1882
							}
1883
						}
1884
						$status = $status_intro . $error_str . $message;
1885
					} else {
1886
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1887
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1888
						$this->_debug($data);
1889
					}
1890
					break;
1891
				case 'godaddy':
1892
				case 'godaddy-v6':
1893
					/* Responds with HTTP 200 on success.
1894
					   Responds with HTTP 4xx or  on error.
1895
					   Returns JSON data as body */
1896
;
1897
					if (preg_match("/\s200\sOK/i", $header)) {
1898
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1899
						$successful_update = true;
1900
					} else if (preg_match("/\s4\d\d\s/i", $header)) {
1901
						$arrbody = json_decode($data, true);
1902
						$message = $arrbody['message'] . ".";
1903
						if (isset($arrbody['fields'])) {
1904
							foreach ($arrbody['fields'] as $error) {
1905
								$message .= " " . $error['path'] . ": " . $error['message'] . ".";
1906
							}
1907
						}
1908
						$status = $status_intro . $error_str . $message;
1909
					} else {
1910
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1911
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1912
						$this->_debug($data);
1913
					}
1914
					break;
1915
				case 'googledomains':
1916
					if (preg_match('/notfqdn/i', $data)) {
1917
						$status = $status_intro . $error_str . gettext("Not A FQDN");
1918
					} else if (preg_match('/nochg/i', $data)) {
1919
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1920
						$successful_update = true;
1921
					} else if (preg_match('/good/i', $data)) {
1922
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1923
						$successful_update = true;
1924
					} else if (preg_match('/badauth/i', $data)) {
1925
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1926
					} else if (preg_match('/nohost/i', $data)) {
1927
						$status = $status_intro . $error_str . gettext("Hostname does not exist or DynDNS not enabled");
1928
					} else if (preg_match('/badagent/i', $data)) {
1929
						$status = $status_intro . $error_str . gettext("Bad request");
1930
					} else if (preg_match('/abuse/i', $data)) {
1931
						$status = $status_intro . $error_str . gettext("Dynamic DNS access has been blocked!");
1932
					} else if (preg_match('/911/i', $data)) {
1933
						$status = $status_intro . $error_str . gettext("Error on Google's end, retry in 5 minutes");
1934
					} else {
1935
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1936
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1937
						$this->_debug($data);
1938
					}
1939
					break;
1940
				case 'dnsmadeeasy':
1941
					switch ($data) {
1942
						case 'success':
1943
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1944
							$successful_update = true;
1945
							break;
1946
						case 'error-auth':
1947
							$status = $status_intro . $error_str . gettext("Invalid username or password");
1948
							break;
1949
						case 'error-auth-suspend':
1950
							$status = $status_intro . $error_str . gettext("Account suspended");
1951
							break;
1952
						case 'error-auth-voided':
1953
							$status = $status_intro . $error_str . gettext("Account revoked");
1954
							break;
1955
						case 'error-record-invalid':
1956
							$status = $status_intro . $error_str . gettext("Record does not exist in the system. Unable to update record");
1957
							break;
1958
						case 'error-record-auth':
1959
							$status = $status_intro . $error_str . gettext("User does not have access to this record");
1960
							break;
1961
						case 'error-record-ip-same':
1962
							$status = $status_intro . $success_str . gettext("No Change In IP Address");
1963
							$successful_update = true;
1964
							break;
1965
						case 'error-system':
1966
							$status = $status_intro . $error_str . gettext("General system error recognized by the system");
1967
							break;
1968
						case 'error':
1969
							$status = $status_intro . $error_str . gettext("General system error unrecognized by the system");
1970
							break;
1971
						default:
1972
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1973
							log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1974
							$this->_debug($data);
1975
							break;
1976
					}
1977
					break;
1978
				case 'spdyn':
1979
				case 'spdyn-v6':
1980
					if (preg_match('/notfqdn/i', $data)) {
1981
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1982
					} else if (preg_match('/nohost/i', $data)) {
1983
						$status = $status_intro . $error_str . gettext("No such host");
1984
					} else if (preg_match('/nochg/i', $data)) {
1985
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1986
						$successful_update = true;
1987
					} else if (preg_match('/good/i', $data)) {
1988
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1989
						$successful_update = true;
1990
					} else if (preg_match('/badauth/i', $data)) {
1991
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1992
					} else {
1993
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1994
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1995
						$this->_debug($data);
1996
					}
1997
					break;
1998
				case 'all-inkl':
1999
 					if (preg_match('/good\s'.$this->_dnsIP.'/i', $data)) {
2000
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2001
 							$successful_update = true;
2002
 					} else if (preg_match('/good/i', $data)) {
2003
						$status = $status_intro . $error_str . gettext("Result did not match.");
2004
					} else if (preg_match("/\s401\sUnauthorized/i", $header)) {
2005
						$status = $status_intro . $error_str . gettext("Invalid username or password");
2006
					}
2007
					else {
2008
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2009
							log_error($status_intro . gettext("PAYLOAD:") . " " . $header.$data);
2010
 							$this->_debug($data);
2011
 							$this->_debug($header);
2012
 					}
2013
 					break;
2014
				case 'hover':
2015
					if (preg_match('/succeeded":true/i', $data)) {
2016
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2017
						$successful_update = true;
2018
					} else {
2019
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2020
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2021
						$this->_debug($data);
2022
					}
2023
					break;
2024
				case 'cloudns':
2025
					$result = json_decode($data, true);
2026
					if ($result['status'] == 'Success') {
2027
						$successful_update = true;
2028
					} else {
2029
						log_error($result['status'] . "(" . $result['statusDescription'] . ")");
2030
					}
2031
					break;
2032
				case 'dreamhost':
2033
				case 'dreamhost-v6':
2034
					$result = json_decode($data,true);
2035
					if ($this->_dnsVerboseLog) {
2036
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2037
					}
2038
					switch ($result['data']) {
2039
					case 'success':
2040
					case 'record_added':
2041
					case 'record_removed':
2042
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2043
						$successful_update = true;
2044
						break;
2045
					case 'no_record':
2046
					case 'no_such_record ':
2047
						$status = $status_intro . $error_str . gettext("No record exists.");
2048
						break;
2049
					case 'no_type':
2050
					case 'no_such_type ':
2051
						$status = $status_intro . $error_str . gettext("No type exists.");
2052
						break;
2053
					case 'no_value':
2054
					case 'no_such_value ':
2055
						$status = $status_intro . $error_str . gettext("No value exists.");
2056
						break;
2057
					case 'no_such_zone':
2058
						$status = $status_intro . $error_str . gettext("No such zone exists.");
2059
						break;
2060
					case 'invalid_record':
2061
						$status = $status_intro . $error_str . gettext("The specified record is invalid.");
2062
						break;
2063
					case 'invalid_type':
2064
						$status = $status_intro . $error_str . gettext("The specified type is invalid.");
2065
						break;
2066
					case 'invalid_value':
2067
						$status = $status_intro . $error_str . gettext("The specified value is invalid.");
2068
						break;
2069
					case 'not_editable ':
2070
						$status = $status_intro . $error_str . gettext("Record is not editable.");
2071
						break;
2072
					case 'record_already_exists_not_editable':
2073
						$status = $status_intro . $error_str . gettext("Record exists but is not editable.");
2074
						break;
2075
					case 'record_already_exists_remove_first':
2076
						$status = $status_intro . $error_str . gettext("Record exists and must be removed before adding.");
2077
						break;
2078
					case 'internal_error_updating_zone':
2079
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2080
						break;
2081
					case 'internal_error_could_not_load_zone':
2082
						$status = $status_intro . $error_str . gettext("A remote server error occurred loading the zone.");
2083
						break;
2084
					case 'internal_error_could_not_update_zone':
2085
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2086
						break;
2087
					case 'internal_error_could_not_add_record':
2088
						$status = $status_intro . $error_str . gettext("A remote server error occurred adding a new record.");
2089
						break;
2090
					case 'internal_error_could_not_destroy_record ':
2091
						$status = $status_intro . $error_str . gettext("A remote server error occurred removing an existing record.");
2092
						break;
2093
					default:
2094
						break;
2095
					}
2096
				case 'azure':
2097
				case 'azurev6':
2098
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2099
					if ($http_code == 401) {
2100
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2101
					} else if ($http_code == 201) {
2102
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2103
						$successful_update = true;
2104
					} else if ($http_code == 200) {
2105
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2106
						$successful_update = true;
2107
					} else {
2108
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2109
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2110
						$this->_debug($data);
2111
					}
2112
					break;
2113
				default:
2114
					break;
2115
			}
2116

    
2117
			if ($successful_update == true) {
2118
				/* Write WAN IP to cache file */
2119
				$wan_ip = $this->_checkIP();
2120
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2121
					$currentTime = time();
2122
					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));
2123
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2124
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2125
				} else {
2126
					@unlink($this->_cacheFile);
2127
				}
2128
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2129
					$currentTime = time();
2130
					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));
2131
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2132
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2133
				} else {
2134
					@unlink($this->_cacheFile_v6);
2135
				}
2136
			}
2137
			$this->status = $status;
2138
			log_error($status);
2139
		}
2140

    
2141
		/*
2142
		 * Private Function (added 12 July 05) [beta]
2143
		 *   Return Error, Set Last Error, and Die.
2144
		 */
2145
		function _error($errorNumber = '1') {
2146
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2147
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2148
			switch ($errorNumber) {
2149
				case 0:
2150
					break;
2151
				case 2:
2152
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2153
					break;
2154
				case 3:
2155
					$error = $err_str . gettext('No Username Provided.');
2156
					break;
2157
				case 4:
2158
					$error = $err_str . gettext('No Password Provided.');
2159
					break;
2160
				case 5:
2161
					$error = $err_str . gettext('No Hostname Provided.');
2162
					break;
2163
				case 6:
2164
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2165
					break;
2166
				case 7:
2167
					$error = $err_str . gettext('No Update URL Provided.');
2168
					break;
2169
				case 8:
2170
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2171
					break;
2172
				case 9:
2173
					$status = $err_str_r53 . gettext("Invalid TTL");
2174
					break;
2175
				case 10:
2176
					$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);
2177
					break;
2178
				default:
2179
					$error = $err_str . gettext('Unknown Response.');
2180
					/* FIXME: $data isn't in scope here */
2181
					/* $this->_debug($data); */
2182
					break;
2183
			}
2184
			$this->lastError = $error;
2185
			log_error($error);
2186
		}
2187

    
2188
		/*
2189
		 * Private Function (added 12 July 05) [beta]
2190
		 *   - Detect whether or not IP needs to be updated.
2191
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2192
		 *      | work with other systems. pfSense base is FreeBSD.
2193
		 */
2194
		function _detectChange() {
2195
			global $debug;
2196

    
2197
			if ($debug) {
2198
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2199
			}
2200

    
2201
			$currentTime = time();
2202

    
2203
			$wan_ip = $this->_checkIP();
2204
			if ($wan_ip == 0) {
2205
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2206
				return false;
2207
			}
2208
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
2209

    
2210
			if ($this->_useIPv6 == true) {
2211
				if (file_exists($this->_cacheFile_v6)) {
2212
					$contents = file_get_contents($this->_cacheFile_v6);
2213
					list($cacheIP, $cacheTime) = explode('|', $contents);
2214
					$this->_debug($cacheIP.'/'.$cacheTime);
2215
					$initial = false;
2216
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
2217
				} else {
2218
					$cacheIP = '::';
2219
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
2220
					$cacheTime = $currentTime;
2221
					$initial = true;
2222
					$log_error .= gettext("No Cached IPv6 found.");
2223
				}
2224
			} else {
2225
				if (file_exists($this->_cacheFile)) {
2226
					$contents = file_get_contents($this->_cacheFile);
2227
					list($cacheIP, $cacheTime) = explode('|', $contents);
2228
					$this->_debug($cacheIP.'/'.$cacheTime);
2229
					$initial = false;
2230
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
2231
				} else {
2232
					$cacheIP = '0.0.0.0';
2233
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
2234
					$cacheTime = $currentTime;
2235
					$initial = true;
2236
					$log_error .= gettext("No Cached IP found.");
2237
				}
2238
			}
2239
			if ($this->_dnsVerboseLog) {
2240
				log_error($log_error);
2241
			}
2242

    
2243
			// Convert seconds = days * hr/day * min/hr * sec/min
2244
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60;
2245

    
2246
			$needs_updating = FALSE;
2247
			/* lets determine if the item needs updating */
2248
			if ($cacheIP != $wan_ip) {
2249
				$needs_updating = true;
2250
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
2251
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
2252
			}
2253
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
2254
				$needs_updating = true;
2255
				$this->_forceUpdateNeeded = true;
2256
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
2257
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
2258
			}
2259
			if ($initial == true) {
2260
				$needs_updating = true;
2261
				$update_reason .= gettext("Initial update.");
2262
			}
2263

    
2264
			/*   finally if we need updating then store the
2265
			 *   new cache value and return true
2266
			 */
2267
			if ($needs_updating == true) {
2268
				if ($this->_dnsVerboseLog) {
2269
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
2270
				}
2271
				return true;
2272
			}
2273

    
2274
			return false;
2275
		}
2276

    
2277
		/*
2278
		 * Private Function (added 16 July 05) [beta]
2279
		 *   - Writes debug information to a file.
2280
		 *   - This function is only called when a unknown response
2281
		 *   - status is returned from a DynDNS service provider.
2282
		 */
2283
		function _debug($data) {
2284
			global $g;
2285

    
2286
			if (!$g['debug']) {
2287
				return;
2288
			}
2289
			$string = date('m-d-y h:i:s').' - ('.$this->_debugID.') - ['.$this->_dnsService.'] - '.$data."\n";
2290
			$file = fopen($this->_debugFile, 'a');
2291
			fwrite($file, $string);
2292
			fclose($file);
2293
		}
2294
		function _checkIP() {
2295
			global $debug;
2296

    
2297
			if ($debug) {
2298
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
2299
			}
2300

    
2301
			if ($this->_useIPv6 == true) {
2302
				$ip_address = get_interface_ipv6($this->_if);
2303
				if (!is_ipaddrv6($ip_address)) {
2304
					return 0;
2305
				}
2306
			} else {
2307
				$ip_address = dyndnsCheckIP($this->_if);
2308
				if (!is_ipaddr($ip_address)) {
2309
					return 0;
2310
				}
2311
			}
2312
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
2313
				if (is_ipaddr($ip_address)) {
2314
					if ($this->_dnsVerboseLog) {
2315
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
2316
					}
2317
				} else {
2318
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
2319
					return 0;
2320
				}
2321
			} else {
2322
				if ($this->_dnsVerboseLog) {
2323
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
2324
				}
2325
			}
2326
			$this->_dnsIP = $ip_address;
2327

    
2328
			return $ip_address;
2329
		}
2330

    
2331
	}
2332

    
2333
?>
(3-3/4)