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-2021 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
				if (!$dnsPass) $this->_error(4);
239
				if (!$dnsHost) $this->_error(5);
240
				if (!$dnsDomain) $this->_error(5);
241
				break;
242
			case "gratisdns":
243
			case "hover":
244
				if (!$dnsUser) $this->_error(3);
245
				if (!$dnsPass) $this->_error(4);
246
				if (!$dnsHost) $this->_error(5);
247
				if (!$dnsDomain) $this->_error(5);
248
				break;
249
			case 'route53-v6':
250
			case 'route53':
251
				if (!$dnsZoneID) $this->_error(8);
252
				if (!$dnsTTL) $this->_error(9);
253
				break;
254
			case 'cloudns':
255
			case "godaddy":
256
			case "godaddy-v6":
257
				if (!$dnsUser) $this->_error(3);
258
				if (!$dnsPass) $this->_error(4);
259
				if (!$dnsHost) $this->_error(5);
260
				if (!$dnsDomain) $this->_error(5);
261
				if (!$dnsTTL) $this->_error(9);
262
				break;
263
			case 'digitalocean':
264
			case 'digitalocean-v6':
265
			case 'linode':
266
			case 'linode-v6':
267
			case 'gandi-livedns':
268
			case 'gandi-livedns-v6':
269
				if (!$dnsPass) $this->_error(4);
270
				if (!$dnsHost) $this->_error(5);
271
				if (!$dnsDomain) $this->_error(5);
272
				if (!$dnsTTL) $this->_error(9);
273
				break;
274
			case 'azure':
275
			case 'azurev6':
276
				if (!$dnsUser) $this->_error(3);
277
				if (!$dnsPass) $this->_error(4);
278
				if (!$dnsHost) $this->_error(5);
279
				if (!$dnsZoneID) $this->_error(8);
280
				if (!$dnsTTL) $this->_error(9);
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 'mythicbeasts-v6':
308
				case 'noip-v6':
309
				case 'noip-free-v6':
310
				case 'dynv6-v6':
311
				case 'easydns-v6':
312
				case 'gandi-livedns-v6':
313
					$this->_useIPv6 = true;
314
					break;
315
				default:
316
					$this->_useIPv6 = false;
317
			}
318
			$this->_dnsService = strtolower($dnsService);
319
			$this->_dnsUser = $dnsUser;
320
			$this->_dnsPass = base64_decode($dnsPass);
321
			$this->_dnsHost = $dnsHost;
322
			$this->_dnsDomain = $dnsDomain;
323
			$this->_dnsServer = $dnsServer;
324
			$this->_dnsPort = $dnsPort;
325
			$this->_dnsWildcard = $dnsWildcard;
326
			$this->_dnsProxied = $dnsProxied;
327
			$this->_dnsMX = $dnsMX;
328
			$this->_dnsZoneID = $dnsZoneID;
329
			$this->_dnsTTL = $dnsTTL;
330
			$this->_if = get_failover_interface($dnsIf);
331
			$this->_checkIP();
332
			$this->_dnsUpdateURL = $dnsUpdateURL;
333
			$this->_dnsResultMatch = $dnsResultMatch;
334
			$this->_dnsRequestIf = get_failover_interface($dnsRequestIf);
335
			if ($this->_dnsVerboseLog) {
336
				log_error(sprintf(gettext('Dynamic DNS (%1$s): running get_failover_interface for %2$s. found %3$s'), $this->_FQDN, $dnsRequestIf, $this->_dnsRequestIf));
337
			}
338
			$this->_dnsRequestIfIP = get_interface_ip($dnsRequestIf);
339
			$this->_dnsMaxCacheAgeDays = 25;
340
			$this->_dnsDummyUpdateDone = false;
341
			$this->_forceUpdateNeeded = $forceUpdate;
342

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

    
350
			$this->_debugID = rand(1000000, 9999999);
351

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

    
448
			unlock($dyndnslck);
449
		}
450

    
451
		/*
452
		 * Private Function (added 12 July 05) [beta]
453
		 *   Send Update To Selected Service.
454
		 */
455
		function _update() {
456

    
457
			if ($this->_dnsVerboseLog) {
458
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
459
			}
460

    
461
			if (strstr($this->_dnsRequestIf, "_vip")) {
462
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
463
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
464
			} else {
465
				$realparentif = $this->_dnsRequestIf;
466
			}
467

    
468
			$ch = curl_init();
469

    
470
			if ($this->_useIPv6 == false) {
471
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
472
			}
473

    
474
			if ($this->_dnsService != 'ods') {
475
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
476
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
477
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
478
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
479
			}
480

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

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

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

    
904
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
905

    
906
					if ((!$this->_dnsUser) || (strpos($this->_dnsUser, '@') !== false)) {
907
						if (strpos($this->_dnsUser, '@') !== false) {
908
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
909
										'X-Auth-Email: ' . $this->_dnsUser,
910
										'X-Auth-Key: ' . $this->_dnsPass,
911
										'Content-Type: application/json'
912
										));
913
						} else {
914
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
915
										'Authorization: Bearer ' . $this->_dnsPass,
916
										'Content-Type: application/json'
917
										));
918
						}
919

    
920
						// Get zone ID
921
						$getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$this->_dnsDomain}";
922
						curl_setopt($ch, CURLOPT_URL, $getZoneId);
923
						$output = json_decode(curl_exec($ch));
924
						$zone = $output->result[0]->id;
925
					} else {
926
						curl_setopt($ch, CURLOPT_HTTPHEADER, array(
927
							'Authorization: Bearer ' . $this->_dnsPass,
928
							'Content-Type: application/json'
929
						));
930

    
931
						$zone = $this->_dnsUser;
932
					}
933

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

    
1073
					//step 1: login to API
1074
					$post_data['username'] = $this->_dnsUser;
1075
					$post_data['password'] = $this->_dnsPass;
1076
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1077
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
1078
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1079
					$output = curl_exec($ch);
1080

    
1081
					//extract the cookies
1082
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1083
					if( count($cookies[1]) > 0 ){
1084
						$cookie_data = implode("; ",$cookies[1]);
1085
					}
1086

    
1087
					//step 2: find the id of the A record
1088
					$post_data = null;
1089
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1090
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1091
					curl_setopt($ch, CURLOPT_HEADER, 0);
1092
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
1093
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1094

    
1095
					$output = curl_exec($ch);
1096
					$pregHost = preg_quote($this->_dnsHost);
1097
					$pregDomain = preg_quote($this->_dnsDomain);
1098
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$pregHost}\",\"type\":\"A\".*?\$/", $output, $hostID);
1099
					$hostID = $hostID[1];
1100
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{[^\}]*?\"name\":\"{$pregHost}\",\"type\":\"A\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
1101
					$hostIP = $hostIP[1];
1102
					unset($pregHost);
1103
					unset($pregDomain);
1104

    
1105
					//step 3: update the IP
1106
					if ($hostID) {
1107
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1108
						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1109
						curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1110
						$post_data['content'] = $this->_dnsIP;
1111
						curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1112
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1113
						curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns/{$hostID}");
1114
						log_error("HostID:{$hostID}, OldIP:{$hostIP}");
1115
					}
1116
					break;
1117
				case 'dreamhost':
1118
				case 'dreamhost-v6':
1119
					$needsIP = TRUE;
1120
					$isv6 = ($this->_dnsService === 'dreamhost-v6');
1121
					$server = 'https://api.dreamhost.com/';
1122
					$post_data['key'] = $this->_dnsPass;
1123
					$post_data['unique_id'] = uniqid($this->_dnsHost);
1124
					$post_data['cmd'] = 'dns-add_record';
1125
					$post_data['format'] = 'json';
1126
					$post_data['value'] = $this->_dnsIP;
1127
					$post_data['record'] = $this->_dnsHost;
1128
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1129
					$post_data['comment'] = "Updated by pfSense:$this->_dnsUser on " . date('c');
1130
					$port = "";
1131
					if ($this->_dnsServer) {
1132
						$server = $this->_dnsServer;
1133
					}
1134
					if ($this->_dnsPort) {
1135
						$port = ":" . $this->_dnsPort;
1136
					}
1137
					curl_setopt($ch, CURLOPT_URL, $server . $port);
1138
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1139
					break;
1140
				case 'digitalocean':
1141
				case 'digitalocean-v6':
1142
					// Get record ID
1143
					$server = 'https://api.digitalocean.com/v2/domains/';
1144
					$isv6 = ($this->_dnsService === 'digitalocean-v6');
1145
					$url = $server . $this->_dnsDomain . '/records';
1146
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$this->_dnsPass}"));
1147
					curl_setopt($ch, CURLOPT_URL, $url);
1148
					$output = json_decode(curl_exec($ch));
1149
					if (!is_array($output->domain_records)) {
1150
						$output->domain_records = array();
1151
					}
1152

    
1153
					// DO's API lists 20 NS records per page, so additional pages needs to be downloaded
1154
					// https://redmine.pfsense.org/issues/10952
1155
					$_domain_records = $output->domain_records;
1156
					$_count = count($_domain_records);
1157
					$_total = 0;
1158
					if (property_exists($output, 'meta')) {
1159
						$meta = $output->meta;
1160
						if (property_exists($meta, 'total')) {
1161
							$_total = $meta->total;
1162
						}
1163
					}
1164
					$_next = '...';
1165
					$_last = '';
1166
					while ($_next != $_last) {
1167
						$_next = '';
1168
						if (property_exists($output, 'links')) {
1169
							$_links = $output->links;
1170
							if (property_exists($_links, 'pages')) {
1171
								$_pages = $_links->pages;
1172
								if (property_exists($_pages, 'next')) {
1173
									$_next = $_pages->next;
1174
								}
1175
								if (property_exists($_pages, 'last')) {
1176
									$_last = $_pages->last;
1177
								}
1178
								if ($_next != '') {
1179
									echo "getting $_next\n";
1180
									curl_setopt($ch, CURLOPT_URL, $_next);
1181
									$output = json_decode(curl_exec($ch));
1182
									if (!is_array($output->domain_records)) {
1183
										$output->domain_records = array();
1184
									}
1185
									$_domain_records = array_merge($_domain_records,$output->domain_records);
1186
								}
1187
							}
1188
						}
1189
					}
1190
					$_count = count($_domain_records);
1191

    
1192
					foreach($_domain_records as $dnsRecord) {
1193
						// NS records are named @ in DO's API, so check type as well 
1194
						// https://redmine.pfsense.org/issues/9171
1195
						if ($this->_dnsHost == $dnsRecord->name && $dnsRecord->type == ($isv6 ? 'AAAA' : 'A')) {
1196
							$recordID = $dnsRecord->id;
1197
							break;
1198
						}
1199
					}
1200

    
1201
					// Create/update record
1202
					if ($recordID == null) {
1203
						$url = $server . $this->_dnsDomain . '/records';
1204
					} else {
1205
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1206
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1207
					}
1208
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1209
					$post_data['ttl'] = $this->_dnsTTL;
1210
					$post_data['name'] = $this->_dnsHost;
1211
					$post_data['data'] = $this->_dnsIP;
1212
					curl_setopt($ch, CURLOPT_URL, $url);
1213
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1214
					break;
1215
				case 'cloudns':
1216
					/* Uses ClouDNS REST API
1217
					   Requires auth-id or sub-auth-id or sub-auth-user */
1218
					// Step 1: Find the Record ID
1219
					$url = 'https://api.cloudns.net/dns/records.json';
1220
					$post_data['auth-id'] = $this->_dnsUser;
1221
					$post_data['auth-password'] = $this->_dnsPass;
1222
					$post_data['domain-name'] = $this->_dnsDomain;
1223
					$post_data['host'] = $this->_dnsHost;
1224
					$post_data['type'] = 'a';
1225
					curl_setopt($ch, CURLOPT_URL, $url);
1226
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1227
					$output = json_decode(curl_exec($ch));
1228
					$recordID = key(get_object_vars($output));
1229

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

    
1325
					// get domain id
1326
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains");
1327
					$domains_output = curl_exec($ch);
1328
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1329
					if ( $http_code == 401 && preg_match('/invalid oauth token/i', $domains_output) ) {
1330
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authentication Failed"));
1331
						return false;
1332
					} else if ( $http_code == 401 ) {
1333
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authorization Failed"));
1334
						return false;
1335
					} else if ( $http_code != 200 ) {
1336
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1337
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1338
						return false;
1339
					}
1340

    
1341
					$domains_result = json_decode($domains_output, TRUE);
1342
					foreach($domains_result["data"] as $domains_entry) {
1343
						if ($domains_entry["domain"] == $this->_dnsDomain) {
1344
							$domain_id = $domains_entry["id"];
1345
						}
1346
					}
1347
					if ( ! $domain_id ) {
1348
						log_error("{$err_prefix} " . gettext("no domain ID for domain") . ": '{$this->_dnsDomain}'");
1349
						return false;
1350
					}
1351
					if ($this->_dnsVerboseLog) {
1352
						log_error("_update(): " . sprintf(gettext("found domain id: %s"), $domain_id));
1353
					}
1354

    
1355
					// get existing record if present
1356
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1357
					$records_output = curl_exec($ch);
1358
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1359
					if ( $http_code != 200 )
1360
					{
1361
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1362
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1363
						return false;
1364
					}
1365

    
1366
					$records_result = json_decode($records_output, TRUE);
1367
					foreach($records_result["data"] as $records_entry) {
1368
						if ( $records_entry["type"] == $record_type && $records_entry["name"] == $record_name ) {
1369
							// not adding support for pagination at this time, hope you have < 100 records!
1370
							$record = $records_entry;
1371
						}
1372
					}
1373
					if ($this->_dnsVerboseLog) {
1374
						log_error("_update(): " . ( $record ? sprintf(gettext("found existing record id: %s"), $record["id"]) : gettext("no matching record found") ));
1375
					}
1376

    
1377
					if (is_array($record)) {
1378
						// update existing record
1379
						$record["target"] = $this->_dnsIP;
1380
						$record["ttl_sec"] = (int) $this->_dnsTTL; // linode may round this up, 0 = zone default
1381
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1382
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1383
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records/" . $record["id"]);
1384
					} else {
1385
						// create a new record
1386
						$record = array(
1387
							"type"    => $record_type,
1388
							"name"    => $record_name,
1389
							"target"  => $this->_dnsIP,
1390
							"ttl_sec" => (int) $this->_dnsTTL, // linode may round this up, 0 = zone default
1391
						);
1392
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1393
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
1394
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1395
					}
1396
					break;
1397
				case 'dynv6':
1398
					$needIP = TRUE;
1399
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv4=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1400
					break;
1401
				case 'dynv6-v6':
1402
					$needIP = TRUE;
1403
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv6=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1404
					break;
1405
				case 'gandi-livedns':
1406
				case 'gandi-livedns-v6':
1407
					// https://api.gandi.net/docs/livedns/#v5-livedns-domains-fqdn-records-rrset_name-rrset_type
1408
					// PUT overrides the existing record or creates if none exists
1409
					$gandi_api = 'https://api.gandi.net/v5/livedns/domains/';
1410
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1411
					$url = "{$gandi_api}{$this->_dnsDomain}/records/{$this->_dnsHost}/{$record_type}";
1412

    
1413
					$headers = array(
1414
						'Authorization: Apikey ' . $this->_dnsPass,
1415
						'Content-Type: application/json'
1416
					);
1417
					$body = array(
1418
						"rrset_ttl"  => $this->_dnsTTL,
1419
						"rrset_values" => array($this->_dnsIP),
1420
					);
1421

    
1422
					curl_setopt($ch, CURLOPT_URL, $url);
1423
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1424
					curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
1425
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
1426
					break;
1427
				default:
1428
					break;
1429
			}
1430
			if ($this->_dnsService != 'ods') {
1431
				curl_setopt($ch, CURLOPT_HEADER, 1);
1432
				$response = curl_exec($ch);
1433
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1434
				$header = substr($response, 0, $header_size);
1435
				$data = substr($response, $header_size);
1436
				if ($this->_dnsVerboseLog) {
1437
					foreach (explode(PHP_EOL, $header) as $hv) {
1438
						log_error(sprintf("Response Header: %s", rtrim($hv)));
1439
				  	}
1440
					log_error(sprintf("Response Data: %s", $data));
1441
				}
1442
				$this->_checkStatus($ch, $data, $header);
1443
				@curl_close($ch);
1444
			}
1445
		}
1446

    
1447
		/**
1448
		 * Private Function (added 23 Feb 17)
1449
		 *   Send Removal To Selected Service.
1450
		 *
1451
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1452
		 *   is to remove the existing record and add a new record.
1453
		 *
1454
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1455
		 */
1456
		function _remove($existing_ip = NULL) {
1457
			$remove_allowed = false;
1458
			if ($this->_dnsVerboseLog) {
1459
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1460
			}
1461

    
1462
			if (strstr($this->_dnsRequestIf, "_vip")) {
1463
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1464
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1465
			} else {
1466
				$realparentif = $this->_dnsRequestIf;
1467
			}
1468

    
1469
			$ch = curl_init();
1470

    
1471
			if ($this->_useIPv6 == false) {
1472
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1473
			}
1474

    
1475
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1476
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1477
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1478
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1479

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

    
1517
		/**
1518
		 * Private Function (added 23 Feb 17)
1519
		 * Retrieves current DNS records from an external API source.
1520
		 *
1521
		 * Some services cannot perform new operations without the caller
1522
		 * providing existing record information.
1523
		 */
1524
		function _lookup_current() {
1525
			$lookup_allowed = false;
1526
			if ($this->_dnsVerboseLog) {
1527
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1528
			}
1529

    
1530
			if (strstr($this->_dnsRequestIf, "_vip")) {
1531
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1532
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1533
			} else {
1534
				$realparentif = $this->_dnsRequestIf;
1535
			}
1536

    
1537
			$ch = curl_init();
1538

    
1539
			if ($this->_useIPv6 == false) {
1540
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1541
			}
1542

    
1543
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1544
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1545
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1546
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1547

    
1548
			switch ($this->_dnsService) {
1549
			case 'dreamhost':
1550
			case 'dreamhost-v6':
1551
				$server = 'https://api.dreamhost.com/';
1552
				$post_data['key'] = $this->_dnsPass;
1553
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1554
				$post_data['cmd'] = 'dns-list_records';
1555
				$post_data['format'] = 'json';
1556
				$port = "";
1557
				if ($this->_dnsServer) {
1558
					$server = $this->_dnsServer;
1559
				}
1560
				if ($this->_dnsPort) {
1561
					$port = ":" . $this->_dnsPort;
1562
				}
1563
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1564
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1565
				$lookup_allowed = true;
1566
				break;
1567
			default:
1568
				break;
1569
			}
1570
			if ($lookup_allowed) {
1571
				curl_setopt($ch, CURLOPT_HEADER, 1);
1572
				$response = curl_exec($ch);
1573
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1574
				$header = substr($response, 0, $header_size);
1575
				$data = substr($response, $header_size);
1576
				$this->_checkLookupStatus($ch, $data, $header);
1577
				@curl_close($ch);
1578
			}
1579
		}
1580

    
1581
		/*
1582
		 * Private Function (added 23 Feb 17)
1583
		 *   Retrieve Lookup Status from the provided data and/or header
1584
		 */
1585
		function _checkLookupStatus($ch, $data, $header) {
1586
			if ($this->_dnsVerboseLog) {
1587
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1588
			}
1589
			$success_str = "(" . gettext("Success") . ") ";
1590
			$error_str = "(" . gettext("Error") . ") ";
1591
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1592

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

    
1632
		/*
1633
		 * Private Function (added 12 July 2005) [beta]
1634
		 *   Retrieve Update Status
1635
		 */
1636
		function _checkStatus($ch, $data, $header) {
1637
			if ($this->_dnsVerboseLog) {
1638
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1639
			}
1640
			$successful_update = false;
1641
			$success_str = "(" . gettext("Success") . ") ";
1642
			$error_str = "(" . gettext("Error") . ") ";
1643
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1644

    
1645
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1646
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1647
				log_error($status);
1648
				$this->status = $status;
1649
				return;
1650
			}
1651
			switch ($this->_dnsService) {
1652
				case 'glesys':
1653
					$status_intro = "GleSYS ({$this->_dnsHost}): ";
1654
					if (preg_match('/Record updated/i', $data)) {
1655
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1656
						$successful_update = true;
1657
					} else {
1658
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1659
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1660
						$this->_debug($data);
1661
					}
1662
					break;
1663
				case 'dnsomatic':
1664
					$status_intro = "DNS-O-Matic ({$this->_dnsHost}): ";
1665
					if (preg_match('/badauth/i', $data)) {
1666
						$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.");
1667
					} else if (preg_match('/notfqdn /i', $data)) {
1668
						$status = $status_intro . gettext("The hostname specified is not a fully-qualified domain name. If no hostnames included, notfqdn will be returned once.");
1669
					} else if (preg_match('/nohost/i', $data)) {
1670
						$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.");
1671
					} else if (preg_match('/numhost/i', $data)) {
1672
						$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.");
1673
					} else if (preg_match('/abuse/i', $data)) {
1674
						$status = $status_intro . gettext("The hostname is blocked for update abuse.");
1675
					} else if (preg_match('/good/i', $data)) {
1676
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1677
						$successful_update = true;
1678
					} else if (preg_match('/dnserr/i', $data)) {
1679
						$status = $status_intro . gettext("DNS error encountered. Stop updating for 30 minutes.");
1680
					} else {
1681
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1682
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1683
						$this->_debug($data);
1684
					}
1685
					break;
1686
				case 'domeneshop':
1687
				case 'domeneshop-v6':
1688
					/* Responds with HTTP 204 on success.
1689
					 * see https://api.domeneshop.no/docs/index.html#tag/ddns/paths/~1dyndns~1update/get */
1690

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

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

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

    
2562
		/*
2563
		 * Private Function (added 12 July 05) [beta]
2564
		 *   - Detect whether or not IP needs to be updated.
2565
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2566
		 *      | work with other systems. pfSense base is FreeBSD.
2567
		 */
2568
		function _detectChange() {
2569
			global $debug;
2570

    
2571
			if ($debug) {
2572
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2573
			}
2574

    
2575
			$currentTime = time();
2576

    
2577
			$wan_ip = $this->_checkIP();
2578
			if ($wan_ip == 0) {
2579
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2580
				return false;
2581
			}
2582
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
2583

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

    
2617
			// Convert seconds = days * hr/day * min/hr * sec/min
2618
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60;
2619

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

    
2638
			/*   finally if we need updating then store the
2639
			 *   new cache value and return true
2640
			 */
2641
			if ($needs_updating == true) {
2642
				if ($this->_dnsVerboseLog) {
2643
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
2644
				}
2645
				return true;
2646
			}
2647

    
2648
			return false;
2649
		}
2650

    
2651
		/*
2652
		 * Private Function (added 16 July 05) [beta]
2653
		 *   - Writes debug information to a file.
2654
		 *   - This function is only called when a unknown response
2655
		 *   - status is returned from a DynDNS service provider.
2656
		 */
2657
		function _debug($data) {
2658
			global $g;
2659

    
2660
			if (!$g['debug']) {
2661
				return;
2662
			}
2663
			$string = date('m-d-y h:i:s') . ' - (' . $this->_debugID . ') - [' . $this->_dnsService . '] - ' . $data . "\n";
2664
			$file = fopen($this->_debugFile, 'a');
2665
			fwrite($file, $string);
2666
			fclose($file);
2667
		}
2668
		function _checkIP() {
2669
			global $debug;
2670

    
2671
			if ($debug) {
2672
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
2673
			}
2674

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

    
2702
			return $ip_address;
2703
		}
2704

    
2705
	}
2706

    
2707
?>
(15-15/61)