Project

General

Profile

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

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

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

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

    
203
			global $config, $g, $dyndns_split_domain_types;
204
			if (in_array($dnsService, $dyndns_split_domain_types)) {
205
				$this->_FQDN = $dnsHost . "." . $dnsDomain;
206
			} else {
207
				$this->_FQDN = $dnsHost;
208
			}
209

    
210
			$this->_cacheFile = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.cache";
211
			$this->_cacheFile_v6 = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}_v6.cache";
212
			$this->_debugFile = "{$g['varetc_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.debug";
213

    
214
			$this->_curlIpresolveV4 = $curlIpresolveV4;
215
			$this->_curlSslVerifypeer = $curlSslVerifypeer;
216
			$this->_dnsVerboseLog = $dnsVerboseLog;
217
			if ($this->_dnsVerboseLog) {
218
				log_error(gettext("Dynamic DNS: updatedns() starting"));
219
			}
220

    
221
			$dyndnslck = lock("DDNS" . $dnsID, LOCK_EX);
222

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

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

    
341
			// Ensure that we were able to lookup the IP
342
			if (!is_ipaddr($this->_dnsIP)) {
343
				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));
344
				unlock($dyndnslck);
345
				return;
346
			}
347

    
348
			$this->_debugID = rand(1000000, 9999999);
349

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

    
443
			unlock($dyndnslck);
444
		}
445

    
446
		/*
447
		 * Private Function (added 12 July 05) [beta]
448
		 *   Send Update To Selected Service.
449
		 */
450
		function _update() {
451

    
452
			if ($this->_dnsVerboseLog) {
453
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
454
			}
455

    
456
			if (strstr($this->_dnsRequestIf, "_vip")) {
457
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
458
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
459
			} else {
460
				$realparentif = $this->_dnsRequestIf;
461
			}
462

    
463
			$ch = curl_init();
464

    
465
			if ($this->_useIPv6 == false) {
466
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
467
			}
468

    
469
			if ($this->_dnsService != 'ods') {
470
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
471
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
472
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
473
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
474
			}
475

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

    
630
					$server = "https://dynamic.zoneedit.com/auth/dynamic.html";
631
					$port = "";
632
					if ($this->_dnsServer) {
633
						$server = $this->_dnsServer;
634
					}
635
					if ($this->_dnsPort) {
636
						$port = ":" . $this->_dnsPort;
637
					}
638
					curl_setopt($ch, CURLOPT_URL, "{$server}{$port}?host=" . $this->_dnsHost . '&dnsto=' . $this->_dnsIP);
639
					break;
640
				case 'dyns':
641
					$needsIP = FALSE;
642
					$server = "http://www.dyns.net/postscript011.php";
643
					$port = "";
644
					if ($this->_dnsServer) {
645
						$server = $this->_dnsServer;
646
					}
647
					if ($this->_dnsPort) {
648
						$port = ":" . $this->_dnsPort;
649
					}
650
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost);
651
					break;
652
				case 'ods':
653
					$needsIP = FALSE;
654
					$misc_errno = 0;
655
					$misc_error = "";
656
					$server = "ods.org";
657
					$port = "";
658
					if ($this->_dnsServer) {
659
						$server = $this->_dnsServer;
660
					}
661
					if ($this->_dnsPort) {
662
						$port = ":" . $this->_dnsPort;
663
					}
664
					$this->con['socket'] = fsockopen("{$server}{$port}", "7070", $misc_errno, $misc_error, 30);
665
					/* Check that we have connected */
666
					if (!$this->con['socket']) {
667
						print "error! could not connect.";
668
						break;
669
					}
670
					/* Here is the loop. Read the incoming data (from the socket connection) */
671
					while (!feof($this->con['socket'])) {
672
						$this->con['buffer']['all'] = trim(fgets($this->con['socket'], 4096));
673
						$code = substr($this->con['buffer']['all'], 0, 3);
674
						sleep(1);
675
						switch ($code) {
676
							case 100:
677
								fputs($this->con['socket'], "LOGIN " . $this->_dnsUser . " " . $this->_dnsPass . "\n");
678
								break;
679
							case 225:
680
								fputs($this->con['socket'], "DELRR " . $this->_dnsHost . " A\n");
681
								break;
682
							case 901:
683
								fputs($this->con['socket'], "ADDRR " . $this->_dnsHost . " A " . $this->_dnsIP . "\n");
684
								break;
685
							case 795:
686
								fputs($this->con['socket'], "QUIT\n");
687
								break;
688
						}
689
					}
690
					$this->_checkStatus(0, $code);
691
					break;
692
				case 'freedns':
693
				case 'freedns-v6':
694
					$needIP = TRUE;
695
					curl_setopt($ch, CURLOPT_URL, 'https://freedns.afraid.org/dynamic/update.php?' . $this->_dnsPass . '&address=' . $this->_dnsIP);
696
					break;
697
				case 'freedns2':
698
					$needIP = TRUE;
699
					curl_setopt($ch, CURLOPT_URL, 'https://sync.afraid.org/u/' . $this->_dnsPass . '/?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
700
					break;
701
				case 'freedns2-v6':
702
					$needIP = TRUE;
703
					curl_setopt($ch, CURLOPT_URL, 'https://v6.sync.afraid.org/u/' . $this->_dnsPass . '/?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
704
					break;
705
				case 'dnsexit':
706
					$needsIP = TRUE;
707
					curl_setopt($ch, CURLOPT_URL, 'https://update.dnsexit.com/RemoteUpdate.sv?login=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
708
					break;
709
				case 'loopia':
710
					$needsIP = TRUE;
711
					if(isset($this->_dnsWildcard) && $this->_dnsWildcard == TRUE) {
712
						$this->_dnsWildcard = "ON";
713
					} else {
714
						$this->_dnsWildcard = "OFF";
715
					}
716
					curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
717
					curl_setopt($ch, CURLOPT_URL, "https://dyndns.loopia.se/?system=custom&hostname={$this->_dnsHost}&myip={$this->_dnsIP}&wildcard={$this->_dnsWildcard}&mx={$this->_dnsMX}&backmx=NO");
718
					break;
719
				case 'opendns':
720
					$needsIP = FALSE;
721
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
722
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
723
					$server = "https://updates.opendns.com/nic/update?hostname=" . $this->_dnsHost;
724
					$port = "";
725
					if ($this->_dnsServer) {
726
						$server = $this->_dnsServer;
727
					}
728
					if ($this->_dnsPort) {
729
						$port = ":" . $this->_dnsPort;
730
					}
731
					curl_setopt($ch, CURLOPT_URL, $server . $port);
732
					break;
733

    
734
				case 'staticcling':
735
					$needsIP = FALSE;
736
					curl_setopt($ch, CURLOPT_URL, 'https://www.staticcling.org/update.html?login=' . $this->_dnsUser . '&pass=' . $this->_dnsPass);
737
					break;
738
				case 'dnsomatic':
739
					/* Example syntax
740
						https://username:password@updates.dnsomatic.com/nic/update?hostname=yourhostname&myip=ipaddress&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG
741
					*/
742
					$needsIP = FALSE;
743
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
744
						$this->_dnsWildcard = "ON";
745
					}
746
					/*
747
					Reference: https://www.dnsomatic.com/wiki/api
748
						DNS-O-Matic usernames are 3-25 characters.
749
						DNS-O-Matic passwords are 6-20 characters.
750
						All ASCII letters and numbers accepted.
751
						Dots, dashes, and underscores allowed, but not at the beginning or end of the string.
752
					Required: "rawurlencode" http://www.php.net/manual/en/function.rawurlencode.php
753
						Encodes the given string according to RFC 3986.
754
					*/
755
					$server = "https://" . rawurlencode($this->_dnsUser) . ":" . rawurlencode($this->_dnsPass) . "@updates.dnsomatic.com/nic/update?hostname=";
756
					if ($this->_dnsServer) {
757
						$server = $this->_dnsServer;
758
					}
759
					if ($this->_dnsPort) {
760
						$port = ":" . $this->_dnsPort;
761
					}
762
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NOCHG');
763
					break;
764
				case 'domeneshop':
765
				case 'domeneshop-v6':
766
					/* Example:
767
						https://{token}:{secret}@api.domeneshop.no/v0/dyndns/update?hostname=example.com&myip=127.0.0.1
768
					*/
769
					$needsIP = FALSE;
770
					$server = "https://{$this->_dnsUser}:{$this->_dnsPass}@api.domeneshop.no/v0/dyndns/update?hostname={$this->_dnsHost}&myip={$this->_dnsIP}";
771
					curl_setopt($ch, CURLOPT_URL, $server);
772
					break;
773
				case 'namecheap':
774
					/* Example:
775
						https://dynamicdns.park-your-domain.com/update?host=[host_name]&domain=[domain.com]&password=[domain_password]&ip=[your_ip]
776
					*/
777
					$needsIP = FALSE;
778
					$dnspass = trim($this->_dnsPass);
779
					$server = "https://dynamicdns.park-your-domain.com/update?host={$this->_dnsHost}&domain={$this->_dnsDomain}&password={$dnspass}&ip={$this->_dnsIP}";
780
					curl_setopt($ch, CURLOPT_URL, $server);
781
					break;
782
				case 'duiadns':
783
				case 'duiadns-v6':
784
					$needsIP = FALSE;
785
					$server = "https://ipv4.duiadns.net/dyndns.duia?";
786
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
787
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
788
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
789
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
790
					break;
791
				case 'he-net':
792
				case 'he-net-v6':
793
					$needsIP = FALSE;
794
					$server = "https://dyn.dns.he.net/nic/update?";
795
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
796
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
797
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&password=' . $this->_dnsPass . '&myip=' . $this->_dnsIP);
798
					break;
799
				case 'he-net-tunnelbroker':
800
					$needsIP = FALSE;
801
					$server = "https://ipv4.tunnelbroker.net/ipv4_end.php?";
802
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
803
					curl_setopt($ch, CURLOPT_URL, $server . 'tid=' . $this->_dnsHost);
804
					break;
805
				case 'selfhost':
806
					$needsIP = FALSE;
807
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
808
						$this->_dnsWildcard = "ON";
809
					}
810
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
811
					$server = "https://carol.selfhost.de/nic/update";
812
					$port = "";
813
					if ($this->_dnsServer) {
814
						$server = $this->_dnsServer;
815
					}
816
					if ($this->_dnsPort) {
817
						$port = ":" . $this->_dnsPort;
818
					}
819
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
820
					break;
821
				case 'route53':
822
					require_once("r53.class");
823
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
824
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
825
					$xmlreq = $r53->getRequestBody($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
826
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
827
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
828
					if($this->_dnsVerboseLog){
829
						log_error(sprintf("Sending request to: %s", $apiurl));
830
						foreach($httphead as $hv){
831
							log_error(sprintf("Header: %s", $hv));
832
						}
833
						log_error(sprintf("XMLPOST: %s", $xmlreq));
834
					}
835
					curl_setopt($ch, CURLOPT_URL, $apiurl);
836
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
837
					break;
838
				case 'route53-v6':
839
					require_once("r53.class");
840
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
841
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
842
					$xmlreq = $r53->getRequestBodyV6($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
843
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
844
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
845
					if($this->_dnsVerboseLog){
846
						log_error(sprintf("Sending request to: %s", $apiurl));
847
						foreach($httphead as $hv){
848
							log_error(sprintf("Header: %s", $hv));
849
						}
850
						log_error(sprintf("XMLPOST: %s", $xmlreq));
851
					}
852
					curl_setopt($ch, CURLOPT_URL, $apiurl);
853
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
854
					break;
855
				case 'custom':
856
				case 'custom-v6':
857
					if (strstr($this->dnsUpdateURL, "%IP%")) {$needsIP = TRUE;} else {$needsIP = FALSE;}
858
					if ($this->_dnsUser != '') {
859
						if ($this->_curlIpresolveV4) {
860
							curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
861
						}
862
						if ($this->_curlSslVerifypeer) {
863
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
864
						} else {
865
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
866
						}
867
						curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
868
					}
869
					$server = str_replace("%IP%", $this->_dnsIP, $this->_dnsUpdateURL);
870
					if ($this->_dnsVerboseLog) {
871
						log_error(sprintf(gettext("Sending request to: %s"), $server));
872
					}
873
					curl_setopt($ch, CURLOPT_URL, $server);
874
					break;
875
				case 'cloudflare-v6':
876
				case 'cloudflare':
877
					$this->_FQDN = ltrim($this->_FQDN, '@.');
878
					$isv6 = ($this->_dnsService === 'cloudflare-v6');
879
					$recordType = $isv6 ? "AAAA" : "A";
880
					$needsIP = TRUE;
881
					$dnsServer ='api.cloudflare.com';
882
					$dnsHost = str_replace(' ', '', $this->_dnsHost);
883

    
884
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
885

    
886
					if (strpos($this->_dnsUser, '@') !== false) {
887
						curl_setopt($ch, CURLOPT_HTTPHEADER, array(
888
							'X-Auth-Email: ' . $this->_dnsUser,
889
							'X-Auth-Key: ' . $this->_dnsPass,
890
							'Content-Type: application/json'
891
						));
892

    
893
						// Get zone ID
894
						$getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$this->_dnsDomain}";
895
						curl_setopt($ch, CURLOPT_URL, $getZoneId);
896
						$output = json_decode(curl_exec($ch));
897
						$zone = $output->result[0]->id;
898
					} else {
899
						curl_setopt($ch, CURLOPT_HTTPHEADER, array(
900
							'Authorization: Bearer ' . $this->_dnsPass,
901
							'Content-Type: application/json'
902
						));
903

    
904
						$zone = $this->_dnsUser;
905
					}
906

    
907
					if ($zone) { // If zone ID was found get host ID
908
						$getHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records?name={$this->_FQDN}&type={$recordType}";
909
						curl_setopt($ch, CURLOPT_URL, $getHostId);
910
						$output = json_decode(curl_exec($ch));
911
						$host = $output->result[0]->id;
912
						if ($host) { // If host ID was found update host
913
							$hostData = array(
914
								"content" => "{$this->_dnsIP}",
915
								"type" => "{$recordType}",
916
								"proxied" => $this->_dnsProxied,
917
								"name" => "{$this->_dnsHost}",
918
								"ttl" => empty($this->_dnsTTL) ? 1 : (int) $this->_dnsTTL
919
							);
920
							$data_json = json_encode($hostData);
921
							$updateHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records/{$host}";
922
							curl_setopt($ch, CURLOPT_URL, $updateHostId);
923
							curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
924
							curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
925
						}
926
					}
927
					break;
928
				case 'eurodns':
929
					$needsIP = TRUE;
930
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
931
					$server = "https://update.eurodyndns.org/update/";
932
					$port = "";
933
					if ($this->_dnsPort) {
934
						$port = ":" . $this->_dnsPort;
935
					}
936
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
937
					break;
938
				case 'gratisdns':
939
					$needsIP = TRUE;
940
					$server = "https://ssl.gratisdns.dk/ddns.phtml";
941
					curl_setopt($ch, CURLOPT_URL, $server . '?u=' . $this->_dnsUser . '&p=' . $this->_dnsPass . '&h=' . $this->_dnsHost . '&d=' . $this->_dnsDomain . '&i=' . $this->_dnsIP);
942
					break;
943
				case 'ovh-dynhost':
944
					$needsIP = FALSE;
945
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
946
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
947
					$server = "https://www.ovh.com/nic/update";
948
					$port = "";
949
					if ($this->_dnsServer) {
950
						$server = $this->_dnsServer;
951
					}
952
					if ($this->_dnsPort) {
953
						$port = ":" . $this->_dnsPort;
954
					}
955
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
956
					break;
957
				case 'citynetwork':
958
					$needsIP = TRUE;
959
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
960
					$server = 'https://dyndns.citynetwork.se/nic/update';
961
					$port = "";
962
					if ($this->_dnsServer) {
963
						$server = $this->_dnsServer;
964
					}
965
					if ($this->_dnsPort) {
966
						$port = ":" . $this->_dnsPort;
967
					}
968
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
969
					break;
970
				case 'dnsimple':
971
					/* Uses DNSimple's v2 REST API
972
					   Requires the Account ID as the username (found in the URL when pull up the domain)
973
					   And an API Token for the password (generated in the User Settings -> API tokens area of the website)
974
					   Piggybacks on Route 53's ZoneID field for the DNSimple record ID to update
975
					   The DNS record MUST exist before it can update since it performs a PATCH operation
976
					   Data sent as JSON over HTTPS */
977
					$needsIP = TRUE;
978
					$server = 'https://api.dnsimple.com/v2/';
979
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
980
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-Type: application/json', 'Authorization: Bearer ' . $this->_dnsPass));
981
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsUser . '/zones/' . $this->_dnsHost . '/records/' . $this->_dnsZoneID);
982
					curl_setopt($ch, CURLOPT_POSTFIELDS, '{"content":"' . $this->_dnsIP . '","ttl":"' . $this->_dnsTTL . '"}');
983
					break;
984
				case 'godaddy':
985
				case 'godaddy-v6':
986
					/* Uses GoDaddy's REST API
987
					   Requires username and Account API sso-key passed in header
988
					   Data sent as JSON */
989
					$needsIP = TRUE;
990
					$server = 'https://api.godaddy.com/v1/domains/';
991
					$recordType = $this->_useIPv6 ? "AAAA" : "A";
992
					$url = $server . $this->_dnsDomain . '/records/' . $recordType . '/' . $this->_dnsHost;
993
					$jsondata = '[{"data":"' . $this->_dnsIP . '","ttl":' . $this->_dnsTTL . '}]';
994
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
995
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
996
						'Accept: application/json',
997
						'Content-Type: application/json',
998
						'Authorization: sso-key ' . $this->_dnsUser . ':' . $this->_dnsPass
999
					));
1000
					curl_setopt($ch, CURLOPT_URL, $url);
1001
					curl_setopt($ch, CURLOPT_POSTFIELDS, $jsondata);
1002
					break;
1003
				case 'googledomains':
1004
					$needsIP = FALSE;
1005
					$post_data['username:password'] = $this->_dnsUser . ':' . $this->_dnsPass;
1006
					$post_data['hostname'] = $this->_dnsHost;
1007
					$post_data['myip'] = $this->_dnsIP;
1008
					$post_data['offline'] = 'no';
1009
					$server = "https://domains.google.com/nic/update";
1010
					$port = "";
1011
					curl_setopt($ch, CURLOPT_URL, 'https://domains.google.com/nic/update');
1012
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1013
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1014
					break;
1015
				case 'dnsmadeeasy':
1016
					$needsIP = TRUE;
1017
					$server = "https://cp.dnsmadeeasy.com/servlet/updateip";
1018
					curl_setopt($ch, CURLOPT_URL, $server . '?username=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&id=' . $this->_dnsHost . '&ip=' . $this->_dnsIP);
1019
					break;
1020
				case 'spdyn':
1021
				case 'spdyn-v6':
1022
					$needsIP = FALSE;
1023
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1024
					$server = "https://update.spdyn.de/nic/update";
1025
					$port = "";
1026
					if ($this->_dnsServer) {
1027
						$server = $this->_dnsServer;
1028
					}
1029
					if ($this->_dnsPort) {
1030
						$port = ":" . $this->_dnsPort;
1031
					}
1032
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1033
					break;
1034
				case 'all-inkl':
1035
					$needsIP = FALSE;
1036
					$server = 'https://dyndns.kasserver.com/';
1037
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1038
					curl_setopt($ch, CURLOPT_URL, $server . 'myip=' . $this->_dnsIP);
1039
					break;
1040
				case 'hover':
1041
					$needsIP = FALSE;
1042
					$port = "";
1043
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1044
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1045

    
1046
					//step 1: login to API
1047
					$post_data['username'] = $this->_dnsUser;
1048
					$post_data['password'] = $this->_dnsPass;
1049
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1050
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
1051
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1052
					$output = curl_exec($ch);
1053

    
1054
					//extract the cookies
1055
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1056
					if( count($cookies[1]) > 0 ){
1057
						$cookie_data = implode("; ",$cookies[1]);
1058
					}
1059

    
1060
					//step 2: find the id of the A record
1061
					$post_data = null;
1062
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1063
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1064
					curl_setopt($ch, CURLOPT_HEADER, 0);
1065
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
1066
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1067

    
1068
					$output = curl_exec($ch);
1069
					$pregHost = preg_quote($this->_dnsHost);
1070
					$pregDomain = preg_quote($this->_dnsDomain);
1071
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$pregHost}\",\"type\":\"A\".*?\$/", $output, $hostID);
1072
					$hostID = $hostID[1];
1073
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{[^\}]*?\"name\":\"{$pregHost}\",\"type\":\"A\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
1074
					$hostIP = $hostIP[1];
1075
					unset($pregHost);
1076
					unset($pregDomain);
1077

    
1078
					//step 3: update the IP
1079
					if ($hostID) {
1080
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1081
						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1082
						curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1083
						$post_data['content'] = $this->_dnsIP;
1084
						curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1085
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1086
						curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns/{$hostID}");
1087
						log_error("HostID:{$hostID}, OldIP:{$hostIP}");
1088
					}
1089
					break;
1090
				case 'dreamhost':
1091
				case 'dreamhost-v6':
1092
					$needsIP = TRUE;
1093
					$isv6 = ($this->_dnsService === 'dreamhost-v6');
1094
					$server = 'https://api.dreamhost.com/';
1095
					$post_data['key'] = $this->_dnsPass;
1096
					$post_data['unique_id'] = uniqid($this->_dnsHost);
1097
					$post_data['cmd'] = 'dns-add_record';
1098
					$post_data['format'] = 'json';
1099
					$post_data['value'] = $this->_dnsIP;
1100
					$post_data['record'] = $this->_dnsHost;
1101
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1102
					$post_data['comment'] = "Updated by pfSense:$this->_dnsUser on " . date('c');
1103
					$port = "";
1104
					if ($this->_dnsServer) {
1105
						$server = $this->_dnsServer;
1106
					}
1107
					if ($this->_dnsPort) {
1108
						$port = ":" . $this->_dnsPort;
1109
					}
1110
					curl_setopt($ch, CURLOPT_URL, $server . $port);
1111
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1112
					break;
1113
				case 'digitalocean':
1114
				case 'digitalocean-v6':
1115
					// Get record ID
1116
					$server = 'https://api.digitalocean.com/v2/domains/';
1117
					$isv6 = ($this->_dnsService === 'digitalocean-v6');
1118
					$url = $server . $this->_dnsDomain . '/records';
1119
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$this->_dnsPass}"));
1120
					curl_setopt($ch, CURLOPT_URL, $url);
1121
					$output = json_decode(curl_exec($ch));
1122
					if (!is_array($output->domain_records)) {
1123
						$output->domain_records = array();
1124
					}
1125

    
1126
					// DO's API lists 20 NS records per page, so additional pages needs to be downloaded
1127
					// https://redmine.pfsense.org/issues/10952
1128
					$_domain_records = $output->domain_records;
1129
					$_count = count($_domain_records);
1130
					$_total = 0;
1131
					if (property_exists($output, 'meta')) {
1132
						$meta = $output->meta;
1133
						if (property_exists($meta, 'total')) {
1134
							$_total = $meta->total;
1135
						}
1136
					}
1137
					$_next = '...';
1138
					$_last = '';
1139
					while ($_next != $_last) {
1140
						$_next = '';
1141
						if (property_exists($output, 'links')) {
1142
							$_links = $output->links;
1143
							if (property_exists($_links, 'pages')) {
1144
								$_pages = $_links->pages;
1145
								if (property_exists($_pages, 'next')) {
1146
									$_next = $_pages->next;
1147
								}
1148
								if (property_exists($_pages, 'last')) {
1149
									$_last = $_pages->last;
1150
								}
1151
								if ($_next != '') {
1152
									echo "getting $_next\n";
1153
									curl_setopt($ch, CURLOPT_URL, $_next);
1154
									$output = json_decode(curl_exec($ch));
1155
									if (!is_array($output->domain_records)) {
1156
										$output->domain_records = array();
1157
									}
1158
									$_domain_records = array_merge($_domain_records,$output->domain_records);
1159
								}
1160
							}
1161
						}
1162
					}
1163
					$_count = count($_domain_records);
1164

    
1165
					foreach($_domain_records as $dnsRecord) {
1166
						// NS records are named @ in DO's API, so check type as well 
1167
						// https://redmine.pfsense.org/issues/9171
1168
						if ($this->_dnsHost == $dnsRecord->name && $dnsRecord->type == ($isv6 ? 'AAAA' : 'A')) {
1169
							$recordID = $dnsRecord->id;
1170
							break;
1171
						}
1172
					}
1173

    
1174
					// Create/update record
1175
					if ($recordID == null) {
1176
						$url = $server . $this->_dnsDomain . '/records';
1177
					} else {
1178
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1179
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1180
					}
1181
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1182
					$post_data['ttl'] = $this->_dnsTTL;
1183
					$post_data['name'] = $this->_dnsHost;
1184
					$post_data['data'] = $this->_dnsIP;
1185
					curl_setopt($ch, CURLOPT_URL, $url);
1186
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1187
					break;
1188
				case 'cloudns':
1189
					/* Uses ClouDNS REST API
1190
					   Requires auth-id or sub-auth-id or sub-auth-user */
1191
					// Step 1: Find the Record ID
1192
					$url = 'https://api.cloudns.net/dns/records.json';
1193
					$post_data['auth-id'] = $this->_dnsUser;
1194
					$post_data['auth-password'] = $this->_dnsPass;
1195
					$post_data['domain-name'] = $this->_dnsDomain;
1196
					$post_data['host'] = $this->_dnsHost;
1197
					$post_data['type'] = 'a';
1198
					curl_setopt($ch, CURLOPT_URL, $url);
1199
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1200
					$output = json_decode(curl_exec($ch));
1201
					$recordID = key(get_object_vars($output));
1202

    
1203
					// Step 2: Set the record
1204
					$needsIP = TRUE;
1205
					$url = 'https://api.cloudns.net/dns/mod-record.json';
1206
					$post_data = array();
1207
					$post_data['auth-id'] = $this->_dnsUser;
1208
					$post_data['auth-password'] = $this->_dnsPass;
1209
					$post_data['domain-name'] = $this->_dnsDomain;
1210
					$post_data['record-id'] = $recordID;
1211
					$post_data['host'] = $this->_dnsHost;
1212
					$post_data['record'] = $this->_dnsIP;
1213
					$post_data['ttl'] = $this->_dnsTTL;
1214
					curl_setopt($ch, CURLOPT_URL, $url);
1215
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1216
					break;
1217
				case 'azurev6':
1218
				case 'azure':
1219
					$hostname = "{$this->_dnsHost}";
1220
					$resourceid = trim($this->_dnsZoneID);
1221
					$app_id = $this->_dnsUser;
1222
					$client_secret = $this->_dnsPass;
1223
					$newip = $this->_dnsIP;
1224
					$newttl = $this->_dnsTTL;
1225
						// ensure resourceid starts with / and has no trailing /
1226
					$resourceid = '/' . trim($resourceid, '/');
1227
						// extract subscription id from resource id
1228
					preg_match('/\\/subscriptions\\/(?<sid>[^\\/]*)/', $resourceid, $result);
1229
					$subscriptionid = isset($result['sid']) ? $result['sid'] : '';
1230
					if (isset($result['sid'])) {
1231
						$subscriptionid = $result['sid'];
1232
					} else {
1233
						log_error("Azure subscription id not found in resource id");
1234
						return false;
1235
					}
1236
						// find tenant id from subscription id
1237
					curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01");
1238
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1239
					curl_setopt($ch, CURLOPT_HEADER, 1);
1240
					curl_setopt($ch, CURLOPT_NOBODY, 1);
1241
					$output = curl_exec($ch);
1242
					$pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?<tid>[^"]*)/i';
1243
					preg_match($pattern, $output, $result);
1244
					if (isset($result['tid'])) {
1245
						$tenantid = $result['tid'];
1246
					} else {
1247
						log_error("Tenant ID not found");
1248
						return false;
1249
					}
1250
						// get an bearer token
1251
					curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token");
1252
					curl_setopt($ch, CURLOPT_POST, 1);
1253
					$body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret);
1254
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1255
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1256
					$server_output = curl_exec($ch);
1257
					$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1258
					preg_match("/\"access_token\":\"(?<tok>[^\"]*)\"/", $server_output, $result);
1259
					if (isset($result['tok'])) {
1260
						$bearertoken = $result['tok'];
1261
					} else {
1262
						log_error("no valid bearer token");
1263
						return false;
1264
					}
1265
						// Update the DNS record
1266
					if ($this->_useIPv6) {
1267
						$url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01";
1268
						$body = '{"properties":{"TTL":"' . $newttl . '", "AAAARecords":[{"ipv6Address":"' . $newip . '"}]}}';
1269
					} else {
1270
						$url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01";
1271
						$body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}';
1272
					}
1273
					$request_headers = array();
1274
					$request_headers[] = 'Accept: application/json';
1275
					$request_headers[] = 'Authorization: Bearer ' . $bearertoken;
1276
					$request_headers[] = 'Content-Type: application/json';
1277
					curl_setopt($ch, CURLOPT_URL, $url);
1278
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1279
					curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1280
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1281
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1282
					curl_setopt($ch, CURLOPT_HEADER, 1);
1283
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1284
					break;
1285
				case 'linode':
1286
				case 'linode-v6':
1287
					$linode_api = "https://api.linode.com/v4";
1288
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1289
					$record_name = $this->_dnsHost == "@" ? "" : $this->_dnsHost;
1290
					$err_prefix = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): (" . gettext("Error") . ")";
1291
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1292
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1293
						'Accept: application/json',
1294
						'Content-Type: application/json',
1295
						'Authorization: Bearer ' . $this->_dnsPass
1296
					));
1297

    
1298
					// get domain id
1299
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains");
1300
					$domains_output = curl_exec($ch);
1301
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1302
					if ( $http_code == 401 && preg_match('/invalid oauth token/i', $domains_output) ) {
1303
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authentication Failed"));
1304
						return false;
1305
					} else if ( $http_code == 401 ) {
1306
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authorization Failed"));
1307
						return false;
1308
					} else if ( $http_code != 200 ) {
1309
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1310
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1311
						return false;
1312
					}
1313

    
1314
					$domains_result = json_decode($domains_output, TRUE);
1315
					foreach($domains_result["data"] as $domains_entry) {
1316
						if ($domains_entry["domain"] == $this->_dnsDomain) {
1317
							$domain_id = $domains_entry["id"];
1318
						}
1319
					}
1320
					if ( ! $domain_id ) {
1321
						log_error("{$err_prefix} " . gettext("no domain ID for domain") . ": '{$this->_dnsDomain}'");
1322
						return false;
1323
					}
1324
					if ($this->_dnsVerboseLog) {
1325
						log_error("_update(): " . sprintf(gettext("found domain id: %s"), $domain_id));
1326
					}
1327

    
1328
					// get existing record if present
1329
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1330
					$records_output = curl_exec($ch);
1331
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1332
					if ( $http_code != 200 )
1333
					{
1334
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1335
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1336
						return false;
1337
					}
1338

    
1339
					$records_result = json_decode($records_output, TRUE);
1340
					foreach($records_result["data"] as $records_entry) {
1341
						if ( $records_entry["type"] == $record_type && $records_entry["name"] == $record_name ) {
1342
							// not adding support for pagination at this time, hope you have < 100 records!
1343
							$record = $records_entry;
1344
						}
1345
					}
1346
					if ($this->_dnsVerboseLog) {
1347
						log_error("_update(): " . ( $record ? sprintf(gettext("found existing record id: %s"), $record["id"]) : gettext("no matching record found") ));
1348
					}
1349

    
1350
					if (is_array($record)) {
1351
						// update existing record
1352
						$record["target"] = $this->_dnsIP;
1353
						$record["ttl_sec"] = (int) $this->_dnsTTL; // linode may round this up, 0 = zone default
1354
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1355
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1356
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records/" . $record["id"]);
1357
					} else {
1358
						// create a new record
1359
						$record = array(
1360
							"type"    => $record_type,
1361
							"name"    => $record_name,
1362
							"target"  => $this->_dnsIP,
1363
							"ttl_sec" => (int) $this->_dnsTTL, // linode may round this up, 0 = zone default
1364
						);
1365
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1366
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
1367
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1368
					}
1369
					break;
1370
				case 'dynv6':
1371
					$needIP = TRUE;
1372
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv4=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1373
					break;
1374
				case 'dynv6-v6':
1375
					$needIP = TRUE;
1376
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv6=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1377
					break;
1378
			case 'gandi-livedns':
1379
				// NOTE: quite similar to digitalocean case but with json content like azure case
1380
				// Get record href
1381
				$server = 'https://dns.api.gandi.net/api/v5/domains/';
1382
				$url = $server . $this->_dnsDomain . '/records';
1383
				curl_setopt($ch, CURLOPT_HTTPHEADER, array("X-Api-Key: {$this->_dnsPass}"));
1384
				curl_setopt($ch, CURLOPT_URL, $url);
1385
				$output = json_decode(curl_exec($ch));
1386
				if (!is_array($output)) {
1387
					$output = array();
1388
				}
1389
				foreach($output as $dnsRecord) {
1390
					// NS records are named @ in DO's API, so check type as well
1391
					if ($this->_dnsHost == $dnsRecord->rrset_name && $dnsRecord->rrset_type == 'A') {
1392
						$recordHref = $dnsRecord->rrset_href;
1393
						break;
1394
					}
1395
				}
1396
				$request_headers = array();
1397
				//$request_headers[] = 'Accept: application/json';
1398
				$request_headers[] = 'X-Api-Key: ' . $this->_dnsPass;
1399
				$request_headers[] = 'Content-Type: application/json';
1400
				//create or update record
1401
				if ($recordHref == null) {
1402
					//create record
1403
					$url = $server . $this->_dnsDomain . '/records';
1404
					$body = '{"rrset_type": "A", "rrset_ttl": ' . $this->_dnsTTL . ', "rrset_name": "' . $this->_dnsHost . '", "rrset_values": ["' . $this->_dnsIP . '"]}';
1405
				} else {
1406
					//update record
1407
					$url = $recordHref;
1408
					$body = '{"rrset_ttl": ' . $this->_dnsTTL . ', "rrset_values": ["' . $this->_dnsIP . '"]}';
1409
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1410
				}
1411
				curl_setopt($ch, CURLOPT_URL, $url);
1412
				curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1413
				curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1414
					break;
1415
				default:
1416
					break;
1417
			}
1418
			if ($this->_dnsService != 'ods') {
1419
				curl_setopt($ch, CURLOPT_HEADER, 1);
1420
				$response = curl_exec($ch);
1421
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1422
				$header = substr($response, 0, $header_size);
1423
				$data = substr($response, $header_size);
1424
				if ($this->_dnsVerboseLog) {
1425
					foreach (explode(PHP_EOL, $header) as $hv) {
1426
						log_error(sprintf("Response Header: %s", rtrim($hv)));
1427
				  	}
1428
					log_error(sprintf("Response Data: %s", $data));
1429
				}
1430
				$this->_checkStatus($ch, $data, $header);
1431
				@curl_close($ch);
1432
			}
1433
		}
1434

    
1435
		/**
1436
		 * Private Function (added 23 Feb 17)
1437
		 *   Send Removal To Selected Service.
1438
		 *
1439
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1440
		 *   is to remove the existing record and add a new record.
1441
		 *
1442
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1443
		 */
1444
		function _remove($existing_ip = NULL) {
1445
			$remove_allowed = false;
1446
			if ($this->_dnsVerboseLog) {
1447
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1448
			}
1449

    
1450
			if (strstr($this->_dnsRequestIf, "_vip")) {
1451
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1452
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1453
			} else {
1454
				$realparentif = $this->_dnsRequestIf;
1455
			}
1456

    
1457
			$ch = curl_init();
1458

    
1459
			if ($this->_useIPv6 == false) {
1460
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1461
			}
1462

    
1463
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1464
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1465
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1466
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1467

    
1468
			switch ($this->_dnsService) {
1469
			case 'dreamhost':
1470
			case 'dreamhost-v6':
1471
				$server = 'https://api.dreamhost.com/';
1472
				$post_data['key'] = $this->_dnsPass;
1473
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1474
				$post_data['cmd'] = 'dns-remove_record';
1475
				$post_data['format'] = 'json';
1476
				$post_data['value'] = $existing_ip;
1477
				$post_data['record'] = $this->_dnsHost;
1478
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1479
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1480
				$port = "";
1481
				if ($this->_dnsServer) {
1482
					$server = $this->_dnsServer;
1483
				}
1484
				if ($this->_dnsPort) {
1485
					$port = ":" . $this->_dnsPort;
1486
				}
1487
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1488
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1489
				$remove_allowed = true;
1490
				break;
1491
			default:
1492
				break;
1493
			}
1494
			if ($remove_allowed) {
1495
				curl_setopt($ch, CURLOPT_HEADER, 1);
1496
				$response = curl_exec($ch);
1497
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1498
				$header = substr($response, 0, $header_size);
1499
				$data = substr($response, $header_size);
1500
				$this->_checkStatus($ch, $data, $header);
1501
				@curl_close($ch);
1502
			}
1503
		}
1504

    
1505
		/**
1506
		 * Private Function (added 23 Feb 17)
1507
		 * Retrieves current DNS records from an external API source.
1508
		 *
1509
		 * Some services cannot perform new operations without the caller
1510
		 * providing existing record information.
1511
		 */
1512
		function _lookup_current() {
1513
			$lookup_allowed = false;
1514
			if ($this->_dnsVerboseLog) {
1515
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1516
			}
1517

    
1518
			if (strstr($this->_dnsRequestIf, "_vip")) {
1519
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1520
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1521
			} else {
1522
				$realparentif = $this->_dnsRequestIf;
1523
			}
1524

    
1525
			$ch = curl_init();
1526

    
1527
			if ($this->_useIPv6 == false) {
1528
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1529
			}
1530

    
1531
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1532
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1533
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1534
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1535

    
1536
			switch ($this->_dnsService) {
1537
			case 'dreamhost':
1538
			case 'dreamhost-v6':
1539
				$server = 'https://api.dreamhost.com/';
1540
				$post_data['key'] = $this->_dnsPass;
1541
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1542
				$post_data['cmd'] = 'dns-list_records';
1543
				$post_data['format'] = 'json';
1544
				$port = "";
1545
				if ($this->_dnsServer) {
1546
					$server = $this->_dnsServer;
1547
				}
1548
				if ($this->_dnsPort) {
1549
					$port = ":" . $this->_dnsPort;
1550
				}
1551
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1552
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1553
				$lookup_allowed = true;
1554
				break;
1555
			default:
1556
				break;
1557
			}
1558
			if ($lookup_allowed) {
1559
				curl_setopt($ch, CURLOPT_HEADER, 1);
1560
				$response = curl_exec($ch);
1561
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1562
				$header = substr($response, 0, $header_size);
1563
				$data = substr($response, $header_size);
1564
				$this->_checkLookupStatus($ch, $data, $header);
1565
				@curl_close($ch);
1566
			}
1567
		}
1568

    
1569
		/*
1570
		 * Private Function (added 23 Feb 17)
1571
		 *   Retrieve Lookup Status from the provided data and/or header
1572
		 */
1573
		function _checkLookupStatus($ch, $data, $header) {
1574
			if ($this->_dnsVerboseLog) {
1575
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1576
			}
1577
			$success_str = "(" . gettext("Success") . ") ";
1578
			$error_str = "(" . gettext("Error") . ") ";
1579
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1580

    
1581
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1582
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1583
				log_error($status);
1584
				$this->status = $status;
1585
				return;
1586
			}
1587
			switch ($this->_dnsService) {
1588
			case 'dreamhost':
1589
			case 'dreamhost-v6':
1590
				$result = json_decode($data,true);
1591
				if($result["result"] != "success") {
1592
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1593
					$this->_debug($data);
1594
					return;
1595
				} else {
1596
					foreach($result["data"] as $key => $row) {
1597
						if($row["record"] == $this->_dnsHost &&
1598
								(($row["type"] == "A" && !$this->_useIPv6)
1599
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1600
								)) {
1601
							if($row["editable"] == 0) {
1602
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1603
								continue;
1604
							}
1605
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1606
						}
1607
					}
1608
				}
1609
				if (!is_array($this->_existingRecords)){
1610
					if ($this->_dnsVerboseLog) {
1611
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1612
					}
1613
				}
1614
				break;
1615
			default:
1616
				break;
1617
			}
1618
		}
1619

    
1620
		/*
1621
		 * Private Function (added 12 July 2005) [beta]
1622
		 *   Retrieve Update Status
1623
		 */
1624
		function _checkStatus($ch, $data, $header) {
1625
			if ($this->_dnsVerboseLog) {
1626
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1627
			}
1628
			$successful_update = false;
1629
			$success_str = "(" . gettext("Success") . ") ";
1630
			$error_str = "(" . gettext("Error") . ") ";
1631
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1632

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

    
2483
			if ($successful_update == true) {
2484
				/* Write WAN IP to cache file */
2485
				$wan_ip = $this->_checkIP();
2486
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2487
					$currentTime = time();
2488
					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));
2489
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2490
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2491
				} else {
2492
					@unlink($this->_cacheFile);
2493
				}
2494
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2495
					$currentTime = time();
2496
					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));
2497
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2498
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2499
				} else {
2500
					@unlink($this->_cacheFile_v6);
2501
				}
2502
			}
2503
			$this->status = $status;
2504
			log_error($status);
2505
		}
2506

    
2507
		/*
2508
		 * Private Function (added 12 July 05) [beta]
2509
		 *   Return Error, Set Last Error, and Die.
2510
		 */
2511
		function _error($errorNumber = '1') {
2512
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2513
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2514
			switch ($errorNumber) {
2515
				case 0:
2516
					break;
2517
				case 2:
2518
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2519
					break;
2520
				case 3:
2521
					$error = $err_str . gettext('No Username Provided.');
2522
					break;
2523
				case 4:
2524
					$error = $err_str . gettext('No Password Provided.');
2525
					break;
2526
				case 5:
2527
					$error = $err_str . gettext('No Hostname Provided.');
2528
					break;
2529
				case 6:
2530
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2531
					break;
2532
				case 7:
2533
					$error = $err_str . gettext('No Update URL Provided.');
2534
					break;
2535
				case 8:
2536
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2537
					break;
2538
				case 9:
2539
					$status = $err_str_r53 . gettext("Invalid TTL");
2540
					break;
2541
				case 10:
2542
					$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);
2543
					break;
2544
				default:
2545
					$error = $err_str . gettext('Unknown Response.');
2546
					/* FIXME: $data isn't in scope here */
2547
					/* $this->_debug($data); */
2548
					break;
2549
			}
2550
			$this->lastError = $error;
2551
			log_error($error);
2552
		}
2553

    
2554
		/*
2555
		 * Private Function (added 12 July 05) [beta]
2556
		 *   - Detect whether or not IP needs to be updated.
2557
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2558
		 *      | work with other systems. pfSense base is FreeBSD.
2559
		 */
2560
		function _detectChange() {
2561
			global $debug;
2562

    
2563
			if ($debug) {
2564
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2565
			}
2566

    
2567
			$currentTime = time();
2568

    
2569
			$wan_ip = $this->_checkIP();
2570
			if ($wan_ip == 0) {
2571
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2572
				return false;
2573
			}
2574
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
2575

    
2576
			if ($this->_useIPv6 == true) {
2577
				if (file_exists($this->_cacheFile_v6)) {
2578
					$contents = file_get_contents($this->_cacheFile_v6);
2579
					list($cacheIP, $cacheTime) = explode('|', $contents);
2580
					$this->_debug($cacheIP . '/' . $cacheTime);
2581
					$initial = false;
2582
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
2583
				} else {
2584
					$cacheIP = '::';
2585
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
2586
					$cacheTime = $currentTime;
2587
					$initial = true;
2588
					$log_error .= gettext("No Cached IPv6 found.");
2589
				}
2590
			} else {
2591
				if (file_exists($this->_cacheFile)) {
2592
					$contents = file_get_contents($this->_cacheFile);
2593
					list($cacheIP, $cacheTime) = explode('|', $contents);
2594
					$this->_debug($cacheIP . '/' . $cacheTime);
2595
					$initial = false;
2596
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
2597
				} else {
2598
					$cacheIP = '0.0.0.0';
2599
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
2600
					$cacheTime = $currentTime;
2601
					$initial = true;
2602
					$log_error .= gettext("No Cached IP found.");
2603
				}
2604
			}
2605
			if ($this->_dnsVerboseLog) {
2606
				log_error($log_error);
2607
			}
2608

    
2609
			// Convert seconds = days * hr/day * min/hr * sec/min
2610
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60;
2611

    
2612
			$needs_updating = FALSE;
2613
			/* lets determine if the item needs updating */
2614
			if ($cacheIP != $wan_ip) {
2615
				$needs_updating = true;
2616
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
2617
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
2618
			}
2619
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
2620
				$needs_updating = true;
2621
				$this->_forceUpdateNeeded = true;
2622
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
2623
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
2624
			}
2625
			if ($initial == true) {
2626
				$needs_updating = true;
2627
				$update_reason .= gettext("Initial update.");
2628
			}
2629

    
2630
			/*   finally if we need updating then store the
2631
			 *   new cache value and return true
2632
			 */
2633
			if ($needs_updating == true) {
2634
				if ($this->_dnsVerboseLog) {
2635
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
2636
				}
2637
				return true;
2638
			}
2639

    
2640
			return false;
2641
		}
2642

    
2643
		/*
2644
		 * Private Function (added 16 July 05) [beta]
2645
		 *   - Writes debug information to a file.
2646
		 *   - This function is only called when a unknown response
2647
		 *   - status is returned from a DynDNS service provider.
2648
		 */
2649
		function _debug($data) {
2650
			global $g;
2651

    
2652
			if (!$g['debug']) {
2653
				return;
2654
			}
2655
			$string = date('m-d-y h:i:s') . ' - (' . $this->_debugID . ') - [' . $this->_dnsService . '] - ' . $data . "\n";
2656
			$file = fopen($this->_debugFile, 'a');
2657
			fwrite($file, $string);
2658
			fclose($file);
2659
		}
2660
		function _checkIP() {
2661
			global $debug;
2662

    
2663
			if ($debug) {
2664
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
2665
			}
2666

    
2667
			if ($this->_useIPv6 == true) {
2668
				$ip_address = get_interface_ipv6($this->_if);
2669
				if (!is_ipaddrv6($ip_address)) {
2670
					return 0;
2671
				}
2672
			} else {
2673
				$ip_address = dyndnsCheckIP($this->_if);
2674
				if (!is_ipaddr($ip_address)) {
2675
					return 0;
2676
				}
2677
			}
2678
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
2679
				if (is_ipaddr($ip_address)) {
2680
					if ($this->_dnsVerboseLog) {
2681
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
2682
					}
2683
				} else {
2684
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
2685
					return 0;
2686
				}
2687
			} else {
2688
				if ($this->_dnsVerboseLog) {
2689
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
2690
				}
2691
			}
2692
			$this->_dnsIP = $ip_address;
2693

    
2694
			return $ip_address;
2695
		}
2696

    
2697
	}
2698

    
2699
?>
(15-15/61)