Project

General

Profile

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

    
162
	class updatedns {
163
		var $_cacheFile;
164
		var $_cacheFile_v6;
165
		var $_debugFile;
166
		var $_UserAgent = 'phpDynDNS/0.7';
167
		var $_errorVerbosity = 0;
168
		var $_dnsService;
169
		var $_dnsUser;
170
		var $_dnsPass;
171
		var $_dnsHost;
172
		var $_dnsDomain;
173
		var $_FQDN;
174
		var $_dnsIP;
175
		var $_dnsWildcard;
176
		var $_dnsProxied;
177
		var $_dnsMX;
178
		var $_dnsBackMX;
179
		var $_dnsServer;
180
		var $_dnsPort;
181
		var $_dnsUpdateURL;
182
		var $_dnsZoneID;
183
		var $_dnsTTL;
184
		var $status;
185
		var $_debugID;
186
		var $_if;
187
		var $_dnsResultMatch;
188
		var $_dnsRequestIf;
189
		var $_dnsRequestIfIP;
190
		var $_dnsVerboseLog;
191
		var $_curlIpresolveV4;
192
		var $_curlSslVerifypeer;
193
		var $_dnsMaxCacheAgeDays;
194
		var $_dnsDummyUpdateDone;
195
		var $_forceUpdateNeeded;
196
		var $_useIPv6;
197
		var $_existingRecords;
198
		var $_curlProxy;
199

    
200
		/*
201
		 * Public Constructor Function (added 12 July 05) [beta]
202
		 *   - Gets the dice rolling for the update.
203
		 *   - $dnsResultMatch should only be used with $dnsService = 'custom'
204
		 *   -  $dnsResultMatch is parsed for '%IP%', which is the IP the provider was updated to,
205
		 *   -  it is otherwise expected to be exactly identical to what is returned by the Provider.
206
		 *   - $dnsUser, and $dnsPass indicate HTTP Auth for custom DNS, if they are needed in the URL (GET Variables), include them in $dnsUpdateURL.
207
		 *   - $For custom requests, $dnsUpdateURL is parsed for '%IP%', which is replaced with the new IP.
208
		 */
209
		function __construct($dnsService = '', $dnsHost = '', $dnsDomain = '', $dnsUser = '', $dnsPass = '',
210
					$dnsWildcard = 'OFF', $dnsProxied = false, $dnsMX = '', $dnsIf = '', $dnsBackMX = '',
211
					$dnsServer = '', $dnsPort = '', $dnsUpdateURL = '', $forceUpdate = false,
212
					$dnsZoneID ='', $dnsTTL='', $dnsResultMatch = '', $dnsRequestIf = '', $dnsMaxCacheAge = '',
213
					$dnsID = '', $dnsVerboseLog = false, $curlIpresolveV4 = false, $curlSslVerifypeer = true,
214
					$curlProxy = false) {
215

    
216
			global $config, $g, $dyndns_split_domain_types;
217
			if (in_array($dnsService, $dyndns_split_domain_types)) {
218
				$this->_FQDN = $dnsHost . "." . $dnsDomain;
219
			} else {
220
				$this->_FQDN = $dnsHost;
221
			}
222

    
223
			$this->_cacheFile = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.cache";
224
			$this->_cacheFile_v6 = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}_v6.cache";
225
			$this->_debugFile = "{$g['varetc_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.debug";
226

    
227
			$this->_curlIpresolveV4 = $curlIpresolveV4;
228
			$this->_curlSslVerifypeer = $curlSslVerifypeer;
229
			$this->_curlProxy = $curlProxy;
230
			$this->_dnsVerboseLog = $dnsVerboseLog;
231
			if ($this->_dnsVerboseLog) {
232
				log_error(gettext("Dynamic DNS: updatedns() starting"));
233
			}
234

    
235
			$dyndnslck = lock("DDNS" . $dnsID, LOCK_EX);
236

    
237
			if (!$dnsService) $this->_error(2);
238
			switch ($dnsService) {
239
			case 'custom':
240
			case 'custom-v6':
241
				if (!$dnsUpdateURL) $this->_error(7);
242
				break;
243
			// providers in an alphabetical order (based on the first provider in a group of cases)
244
			case 'azure':
245
			case 'azurev6':
246
				if (!$dnsUser) $this->_error(3);
247
				if (!$dnsPass) $this->_error(4);
248
				if (!$dnsHost) $this->_error(5);
249
				if (!$dnsZoneID) $this->_error(8);
250
				if (!$dnsTTL) $this->_error(9);
251
				break;
252
			case 'cloudflare':
253
			case 'cloudflare-v6':
254
				if (!$dnsPass) $this->_error(4);
255
				if (!$dnsHost) $this->_error(5);
256
				if (!$dnsDomain) $this->_error(5);
257
				break;
258
			case 'cloudns':
259
			case 'godaddy':
260
			case 'godaddy-v6':
261
				if (!$dnsUser) $this->_error(3);
262
				if (!$dnsPass) $this->_error(4);
263
				if (!$dnsHost) $this->_error(5);
264
				if (!$dnsDomain) $this->_error(5);
265
				if (!$dnsTTL) $this->_error(9);
266
				break;
267
			case 'desec':
268
			case 'desec-v6':
269
				if (!$dnsPass) $this->_error(4);
270
				if (!$dnsHost) $this->_error(5);
271
				break;
272
			case 'digitalocean':
273
			case 'digitalocean-v6':
274
			case 'gandi-livedns':
275
			case 'gandi-livedns-v6':
276
			case 'linode':
277
			case 'linode-v6':
278
			case 'onecom':
279
			case 'onecom-v6':
280
			case 'yandex':
281
			case 'yandex-v6':
282
				if (!$dnsPass) $this->_error(4);
283
				if (!$dnsHost) $this->_error(5);
284
				if (!$dnsDomain) $this->_error(5);
285
				if (!$dnsTTL) $this->_error(9);
286
				break;
287
			case 'freedns':
288
			case 'freedns-v6':
289
			case 'freedns2':
290
			case 'freedns2-v6':
291
				if (!$dnsHost) $this->_error(5);
292
				break;
293
			case 'gratisdns':
294
			case 'hover':
295
			case 'name.com':
296
			case 'name.com-v6':
297
				if (!$dnsUser) $this->_error(3);
298
				if (!$dnsPass) $this->_error(4);
299
				if (!$dnsHost) $this->_error(5);
300
				if (!$dnsDomain) $this->_error(5);
301
				break;
302
			case 'namecheap':
303
				if (!$dnsPass) $this->_error(4);
304
				if (!$dnsHost) $this->_error(5);
305
				if (!$dnsDomain) $this->_error(5);
306
				break;
307
			case 'route53':
308
			case 'route53-v6':
309
				if (!$dnsZoneID) $this->_error(8);
310
				if (!$dnsTTL) $this->_error(9);
311
				break;
312
			default:
313
				if (!$dnsUser) $this->_error(3);
314
				if (!$dnsPass) $this->_error(4);
315
				if (!$dnsHost) $this->_error(5);
316
			}
317

    
318
			switch ($dnsService) {
319
				case 'azurev6':
320
				case 'cloudflare-v6':
321
				case 'custom-v6':
322
				case 'desec-v6':
323
				case 'digitalocean-v6':
324
				case 'dnsimple-v6':
325
				case 'domeneshop-v6':
326
				case 'dreamhost-v6':
327
				case 'duiadns-v6':
328
				case 'dynv6-v6':
329
				case 'easydns-v6':
330
				case 'freedns-v6':
331
				case 'freedns2-v6':
332
				case 'gandi-livedns-v6':
333
				case 'godaddy-v6':
334
				case 'he-net-v6':
335
				case 'linode-v6':
336
				case 'mythicbeasts-v6':
337
				case 'name.com-v6':
338
				case 'noip-free-v6':
339
				case 'noip-v6':
340
				case 'route53-v6':
341
				case 'spdyn-v6':
342
				case 'yandex-v6':
343
					$this->_useIPv6 = true;
344
					break;
345
				default:
346
					$this->_useIPv6 = false;
347
			}
348
			$this->_dnsService = strtolower($dnsService);
349
			$this->_dnsUser = $dnsUser;
350
			$this->_dnsPass = base64_decode($dnsPass);
351
			$this->_dnsHost = $dnsHost;
352
			$this->_dnsDomain = $dnsDomain;
353
			$this->_dnsServer = $dnsServer;
354
			$this->_dnsPort = $dnsPort;
355
			$this->_dnsWildcard = $dnsWildcard;
356
			$this->_dnsProxied = $dnsProxied;
357
			$this->_dnsMX = $dnsMX;
358
			$this->_dnsZoneID = $dnsZoneID;
359
			$this->_dnsTTL = $dnsTTL;
360
			$this->_if = get_failover_interface($dnsIf);
361
			$this->_checkIP();
362
			$this->_dnsUpdateURL = $dnsUpdateURL;
363
			$this->_dnsResultMatch = $dnsResultMatch;
364
			if (($this->_dnsService == 'custom') || ($this->_dnsService == 'custom-v6')) {
365
				$this->_dnsRequestIf = get_failover_interface($dnsRequestIf);
366
			} else {
367
				$this->_dnsRequestIf = $this->_if;
368
			}
369
			if (($dnsMaxCacheAge !== null) && ($dnsMaxCacheAge !== "")) {
370
				$this->_dnsMaxCacheAgeDays = (int)$dnsMaxCacheAge;
371
			} else {
372
				switch ($dnsService) {
373
					// exceptions to _dnsMaxCacheAgeDays in an alphabetical order
374
					case 'dyfi':
375
						$this->_dnsMaxCacheAgeDays = 6;
376
						break;
377
					default:
378
						$this->_dnsMaxCacheAgeDays = 25;
379
				}
380
			}
381
			if ($this->_dnsVerboseLog) {
382
				log_error(sprintf(gettext('Dynamic DNS (%1$s): running get_failover_interface for %2$s. found %3$s'), $this->_FQDN, $dnsRequestIf, $this->_dnsRequestIf));
383
			}
384
			$this->_dnsRequestIfIP = get_interface_ip($dnsRequestIf);
385
			$this->_dnsDummyUpdateDone = false;
386
			$this->_forceUpdateNeeded = $forceUpdate;
387

    
388
			// Ensure that we were able to lookup the IP
389
			if (!is_ipaddr($this->_dnsIP)) {
390
				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));
391
				unlock($dyndnslck);
392
				return;
393
			}
394

    
395
			$this->_debugID = rand(1000000, 9999999);
396

    
397
			if ($forceUpdate == false && $this->_detectChange() == false) {
398
				$this->_error(10);
399
			} else {
400
				switch ($this->_dnsService) {
401
					case 'all-inkl':
402
					case 'azure':
403
					case 'azurev6':
404
					case 'citynetwork':
405
					case 'cloudflare':
406
					case 'cloudflare-v6':
407
					case 'cloudns':
408
					case 'custom':
409
					case 'custom-v6':
410
					case 'desec':
411
					case 'desec-v6':
412
					case 'dhs':
413
					case 'digitalocean':
414
					case 'digitalocean-v6':
415
					case 'dnsexit':
416
					case 'dnsimple':
417
					case 'dnsimple-v6':
418
					case 'dnsmadeeasy':
419
					case 'dnsomatic':
420
					case 'domeneshop':
421
					case 'domeneshop-v6':
422
					case 'duiadns':
423
					case 'duiadns-v6':
424
					case 'dyfi':
425
					case 'dyndns':
426
					case 'dyndns-custom':
427
					case 'dyndns-static':
428
					case 'dyns':
429
					case 'dynv6':
430
					case 'dynv6-v6':
431
					case 'easydns':
432
					case 'easydns-v6':
433
					case 'eurodns':
434
					case 'freedns':
435
					case 'freedns-v6':
436
					case 'freedns2':
437
					case 'freedns2-v6':
438
					case 'gandi-livedns':
439
					case 'gandi-livedns-v6':
440
					case 'glesys':
441
					case 'godaddy':
442
					case 'godaddy-v6':
443
					case 'googledomains':
444
					case 'gratisdns':
445
					case 'he-net':
446
					case 'he-net-tunnelbroker':
447
					case 'he-net-v6':
448
					case 'hn':
449
					case 'hover':
450
					case 'linode':
451
					case 'linode-v6':
452
					case 'loopia':
453
					case 'mythicbeasts':
454
					case 'mythicbeasts-v6':
455
					case 'name.com':
456
					case 'name.com-v6':
457
					case 'namecheap':
458
					case 'nicru':
459
					case 'nicru-v6':
460
					case 'noip':
461
					case 'noip-free':
462
					case 'noip-free-v6':
463
					case 'noip-v6':
464
					case 'ods':
465
					case 'opendns':
466
					case 'ovh-dynhost':
467
					case 'route53':
468
					case 'route53-v6':
469
					case 'selfhost':
470
					case 'spdyn':
471
					case 'spdyn-v6':
472
					case 'staticcling':
473
					case 'strato':
474
					case 'onecom':
475
					case 'onecom-v6':
476
					case 'yandex':
477
					case 'yandex-v6':
478
					case 'zoneedit':
479
						$this->_update();
480
						if ($this->_dnsDummyUpdateDone == true) {
481
							// If a dummy update was needed, then sleep a while and do the update again to put the proper address back.
482
							// Some providers (e.g. No-IP free accounts) need to have at least 1 address change every month.
483
							// If the address has not changed recently, or the user did "Force Update", then the code does
484
							// a dummy address change for providers like this.
485
							sleep(10);
486
							$this->_update();
487
						}
488
						break;
489
					case 'dreamhost':
490
					case 'dreamhost-v6':
491
						$this->_lookup_current();
492
						if (isset($this->status)) {
493
							return;
494
						}
495
						foreach ($this->_existingRecords as $record) {
496
							$this->_remove($record['existing_val']);
497
							$this->_update();
498
						}
499
						break;
500
					default:
501
						$this->_error(6);
502
						break;
503
				}
504
			}
505

    
506
			unlock($dyndnslck);
507
		}
508

    
509
		/*
510
		 * Private Function (added 12 July 05) [beta]
511
		 *   Send Update To Selected Service.
512
		 */
513
		function _update() {
514

    
515
			if ($this->_dnsVerboseLog) {
516
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
517
			}
518

    
519
			if (strstr($this->_dnsRequestIf, "_vip")) {
520
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
521
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
522
			} else {
523
				$realparentif = $this->_dnsRequestIf;
524
			}
525

    
526
			$ch = curl_init();
527

    
528
			if ($this->_useIPv6 == false) {
529
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
530
			}
531

    
532
			if ($this->_dnsService != 'ods') {
533
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
534
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
535
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
536
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
537
			}
538

    
539
			switch ($this->_dnsService) {
540
				// The special custom provider
541
				case 'custom':
542
				case 'custom-v6':
543
					if (strstr($this->dnsUpdateURL, "%IP%")) {$needsIP = TRUE;} else {$needsIP = FALSE;}
544
					if ($this->_dnsUser != '') {
545
						if ($this->_curlIpresolveV4) {
546
							curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
547
						}
548
						if ($this->_curlSslVerifypeer) {
549
							/* required for a local CA
550
							 * see https://redmine.pfsense.org/issues/12589 */
551
							curl_setopt($ch, CURLOPT_CAPATH, "/etc/ssl/certs/");
552
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
553
						} else {
554
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
555
						}
556
						curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
557
					}
558
					$server = str_replace("%IP%", $this->_dnsIP, $this->_dnsUpdateURL);
559
					if ($this->_dnsVerboseLog) {
560
						log_error(sprintf(gettext("Sending request to: %s"), $server));
561
					}
562
					curl_setopt($ch, CURLOPT_URL, $server);
563
					break;
564
				// Providers in an alphabetical order. Add code for new providers below in a correct position.
565
				case 'dyfi':
566
					// see specification at https://www.dy.fi/page/specification
567
					$needsIP = FALSE;
568
					$server = 'https://www.dy.fi/nic/update';
569
					curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
570
					curl_setopt($ch, CURLOPT_URL, "{$server}?hostname={$this->_dnsHost}");
571
					break;
572
				case 'glesys':
573
					$needsIP = TRUE;
574
					$server = 'https://api.glesys.com/domain/updaterecord/format/json';
575
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
576
					$post_data['recordid'] = $this->_FQDN;
577
					$post_data['data'] = $this->_dnsIP;
578
					curl_setopt($ch, CURLOPT_URL, $server);
579
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
580
					break;
581
				//
582
				// Not yet ordered providers.
583
				// TODO: When editing a provider, move it above in a correct position.
584
				//
585
				case 'dyndns':
586
				case 'dyndns-static':
587
				case 'dyndns-custom':
588
					$needsIP = FALSE;
589
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
590
						$this->_dnsWildcard = "ON";
591
					}
592
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
593
					$server = "https://members.dyndns.org/nic/update";
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 . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
602
					break;
603
				case 'dhs':
604
					// DHS is disabled in the GUI because the following doesn't work.
605
					$needsIP = TRUE;
606
					$post_data['hostscmd'] = 'edit';
607
					$post_data['hostscmdstage'] = '2';
608
					$post_data['type'] = '4';
609
					$post_data['updatetype'] = 'Online';
610
					$post_data['mx'] = $this->_dnsMX;
611
					$post_data['mx2'] = '';
612
					$post_data['txt'] = '';
613
					$post_data['offline_url'] = '';
614
					$post_data['cloak'] = 'Y';
615
					$post_data['cloak_title'] = '';
616
					$post_data['ip'] = $this->_dnsIP;
617
					$post_data['domain'] = 'dyn.dhs.org';
618
					$post_data['hostname'] = $this->_dnsHost;
619
					$post_data['submit'] = 'Update';
620
					$server = "https://members.dhs.org/nic/hosts";
621
					$port = "";
622
					if ($this->_dnsServer) {
623
						$server = $this->_dnsServer;
624
					}
625
					if ($this->_dnsPort) {
626
						$port = ":" . $this->_dnsPort;
627
					}
628
					curl_setopt($ch, CURLOPT_URL, $server . $port);
629
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
630
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
631
					break;
632
				case 'noip-v6':
633
				case 'noip-free-v6':
634
					$needsIP = TRUE;
635
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
636
					$server = "https://dynupdate.no-ip.com/nic/update";
637
					$port = "";
638
					if ($this->_dnsServer) {
639
						$server = $this->_dnsServer;
640
					}
641
					if ($this->_dnsPort) {
642
						$port = ":" . $this->_dnsPort;
643
					}
644
					if (($this->_dnsService == "noip-free-v6") &&
645
					    ($this->_forceUpdateNeeded == true) &&
646
					    ($this->_dnsDummyUpdateDone == false)) {
647
						// Update the IP to a dummy value to force No-IP free accounts to see a change.
648
						$iptoset = "fd00:d::1";
649
						$this->_dnsDummyUpdateDone = true;
650
						$log_message = 'Dynamic DNS %1$s (%2$s): ';
651
						$log_message .= 'Processing dummy update on No-IP free account. ';
652
						$log_message .= 'IP temporarily set to %3$s';
653
						log_error(sprintf(gettext($log_message), $this->_dnsService, $this->_dnsHost, $iptoset));
654
					} else {
655
						$iptoset = $this->_dnsIP;
656
					}
657
					curl_setopt($ch, CURLOPT_URL, $url_data);
658
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myipv6=' . $iptoset);
659
					break;
660
				case 'noip':
661
				case 'noip-free':
662
					$needsIP = TRUE;
663
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
664
					$server = "https://dynupdate.no-ip.com/nic/update";
665
					$port = "";
666
					if ($this->_dnsServer) {
667
						$server = $this->_dnsServer;
668
					}
669
					if ($this->_dnsPort) {
670
						$port = ":" . $this->_dnsPort;
671
					}
672
					if (($this->_dnsService == "noip-free") &&
673
					    ($this->_forceUpdateNeeded == true) &&
674
					    ($this->_dnsDummyUpdateDone == false)) {
675
						// Update the IP to a dummy value to force No-IP free accounts to see a change.
676
						$iptoset = "192.168.1.1";
677
						$this->_dnsDummyUpdateDone = true;
678
						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));
679
					} else {
680
						$iptoset = $this->_dnsIP;
681
					}
682
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $iptoset);
683
					break;
684
				case 'easydns':
685
					$needsIP = TRUE;
686
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
687
					$server = "https://members.easydns.com/dyn/dyndns.php";
688
					$port = "";
689
					if ($this->_dnsServer) {
690
						$server = $this->_dnsServer;
691
					}
692
					if ($this->_dnsPort) {
693
						$port = ":" . $this->_dnsPort;
694
					}
695
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=' . $this->_dnsBackMX);
696
					break;
697
				case 'easydns-v6':
698
					$needsIP = TRUE;
699
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
700
					$server = "https://members.easydns.com/dyn/dyndns.php";
701
					$port = "";
702
					if ($this->_dnsServer) {
703
						$server = $this->_dnsServer;
704
					}
705
					if ($this->_dnsPort) {
706
						$port = ":" . $this->_dnsPort;
707
					}
708
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=' . $this->_dnsBackMX);
709
					break;
710
				case 'hn':
711
					$needsIP = TRUE;
712
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
713
					$server = "http://dup.hn.org/vanity/update";
714
					$port = "";
715
					if ($this->_dnsServer) {
716
						$server = $this->_dnsServer;
717
					}
718
					if ($this->_dnsPort) {
719
						$port = ":" . $this->_dnsPort;
720
					}
721
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?ver=1&IP=' . $this->_dnsIP);
722
					break;
723
				case 'zoneedit':
724
					$needsIP = FALSE;
725
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
726

    
727
					$server = "https://dynamic.zoneedit.com/auth/dynamic.html";
728
					$port = "";
729
					if ($this->_dnsServer) {
730
						$server = $this->_dnsServer;
731
					}
732
					if ($this->_dnsPort) {
733
						$port = ":" . $this->_dnsPort;
734
					}
735
					curl_setopt($ch, CURLOPT_URL, "{$server}{$port}?host=" . $this->_dnsHost . '&dnsto=' . $this->_dnsIP);
736
					break;
737
				case 'dyns':
738
					$needsIP = FALSE;
739
					$server = "http://www.dyns.net/postscript011.php";
740
					$port = "";
741
					if ($this->_dnsServer) {
742
						$server = $this->_dnsServer;
743
					}
744
					if ($this->_dnsPort) {
745
						$port = ":" . $this->_dnsPort;
746
					}
747
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost);
748
					break;
749
				case 'ods':
750
					$needsIP = FALSE;
751
					$misc_errno = 0;
752
					$misc_error = "";
753
					$server = "ods.org";
754
					$port = "";
755
					if ($this->_dnsServer) {
756
						$server = $this->_dnsServer;
757
					}
758
					if ($this->_dnsPort) {
759
						$port = ":" . $this->_dnsPort;
760
					}
761
					$this->con['socket'] = fsockopen("{$server}{$port}", "7070", $misc_errno, $misc_error, 30);
762
					/* Check that we have connected */
763
					if (!$this->con['socket']) {
764
						print "error! could not connect.";
765
						break;
766
					}
767
					/* Here is the loop. Read the incoming data (from the socket connection) */
768
					while (!feof($this->con['socket'])) {
769
						$this->con['buffer']['all'] = trim(fgets($this->con['socket'], 4096));
770
						$code = substr($this->con['buffer']['all'], 0, 3);
771
						sleep(1);
772
						switch ($code) {
773
							case 100:
774
								fputs($this->con['socket'], "LOGIN " . $this->_dnsUser . " " . $this->_dnsPass . "\n");
775
								break;
776
							case 225:
777
								fputs($this->con['socket'], "DELRR " . $this->_dnsHost . " A\n");
778
								break;
779
							case 901:
780
								fputs($this->con['socket'], "ADDRR " . $this->_dnsHost . " A " . $this->_dnsIP . "\n");
781
								break;
782
							case 795:
783
								fputs($this->con['socket'], "QUIT\n");
784
								break;
785
						}
786
					}
787
					$this->_checkStatus(null, null, $code, null);
788
					break;
789
				case 'freedns':
790
				case 'freedns-v6':
791
					$needIP = TRUE;
792
					curl_setopt($ch, CURLOPT_URL, 'https://freedns.afraid.org/dynamic/update.php?' . $this->_dnsPass . '&address=' . $this->_dnsIP);
793
					break;
794
				case 'freedns2':
795
					$needIP = TRUE;
796
					curl_setopt($ch, CURLOPT_URL, 'https://sync.afraid.org/u/' . $this->_dnsPass . '/?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
797
					break;
798
				case 'freedns2-v6':
799
					$needIP = TRUE;
800
					curl_setopt($ch, CURLOPT_URL, 'https://v6.sync.afraid.org/u/' . $this->_dnsPass . '/?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
801
					break;
802
				case 'dnsexit':
803
					$needsIP = TRUE;
804
					curl_setopt($ch, CURLOPT_URL, 'https://update.dnsexit.com/RemoteUpdate.sv?login=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
805
					break;
806
				case 'loopia':
807
					$needsIP = TRUE;
808
					if(isset($this->_dnsWildcard) && $this->_dnsWildcard == TRUE) {
809
						$this->_dnsWildcard = "ON";
810
					} else {
811
						$this->_dnsWildcard = "OFF";
812
					}
813
					curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
814
					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");
815
					break;
816
				case 'opendns':
817
					$needsIP = FALSE;
818
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
819
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
820
					$server = "https://updates.opendns.com/nic/update?hostname=" . $this->_dnsHost;
821
					$port = "";
822
					if ($this->_dnsServer) {
823
						$server = $this->_dnsServer;
824
					}
825
					if ($this->_dnsPort) {
826
						$port = ":" . $this->_dnsPort;
827
					}
828
					curl_setopt($ch, CURLOPT_URL, $server . $port);
829
					break;
830

    
831
				case 'staticcling':
832
					$needsIP = FALSE;
833
					curl_setopt($ch, CURLOPT_URL, 'https://www.staticcling.org/update.html?login=' . $this->_dnsUser . '&pass=' . $this->_dnsPass);
834
					break;
835
				case 'dnsomatic':
836
					/* Example syntax
837
						https://username:password@updates.dnsomatic.com/nic/update?hostname=yourhostname&myip=ipaddress&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG
838
					*/
839
					$needsIP = FALSE;
840
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
841
						$this->_dnsWildcard = "ON";
842
					}
843
					/*
844
					Reference: https://www.dnsomatic.com/wiki/api
845
						DNS-O-Matic usernames are 3-25 characters.
846
						DNS-O-Matic passwords are 6-20 characters.
847
						All ASCII letters and numbers accepted.
848
						Dots, dashes, and underscores allowed, but not at the beginning or end of the string.
849
					Required: "rawurlencode" http://www.php.net/manual/en/function.rawurlencode.php
850
						Encodes the given string according to RFC 3986.
851
					*/
852
					$server = "https://" . rawurlencode($this->_dnsUser) . ":" . rawurlencode($this->_dnsPass) . "@updates.dnsomatic.com/nic/update?hostname=";
853
					if ($this->_dnsServer) {
854
						$server = $this->_dnsServer;
855
					}
856
					if ($this->_dnsPort) {
857
						$port = ":" . $this->_dnsPort;
858
					}
859
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NOCHG');
860
					break;
861
				case 'domeneshop':
862
				case 'domeneshop-v6':
863
					/* Example:
864
						https://{token}:{secret}@api.domeneshop.no/v0/dyndns/update?hostname=example.com&myip=127.0.0.1
865
					*/
866
					$needsIP = FALSE;
867
					$server = "https://{$this->_dnsUser}:{$this->_dnsPass}@api.domeneshop.no/v0/dyndns/update?hostname={$this->_dnsHost}&myip={$this->_dnsIP}";
868
					curl_setopt($ch, CURLOPT_URL, $server);
869
					break;
870
				case 'mythicbeasts':
871
				case 'mythicbeasts-v6':
872
					/* Example:
873
						https://{login}:{password}@https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1/A -d data=1.2.3.4
874
					*/
875
					$needsIP = FALSE;
876
					if ($this->_useIPv6) {
877
						$record = "AAAA";
878
					} else {
879
						$record = "A";
880
					}
881
					$post_data['data'] = $this->_dnsIP;
882
					$server = "https://api.mythic-beasts.com/dns/v2/zones/{$this->_dnsDomain}/records/{$this->_dnsHost}/{$record}";
883
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
884
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
885
					curl_setopt($ch, CURLOPT_URL, $server);
886
					break;
887
				case 'name.com':
888
				case 'name.com-v6':
889
					// API documentation: https://www.name.com/api-docs/ & https://www.name.com/api-docs/DNS
890
					$namedotcom_api = "https://api.name.com/v4/domains/{$this->_dnsDomain}/records";
891
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
892
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
893
					// Check if a record already exists for this host.
894
					curl_setopt($ch, CURLOPT_URL, "{$namedotcom_api}?perPage=1000");
895
					$response = json_decode(curl_exec($ch), true);
896
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
897
					if ($http_code != "200") {
898
						log_error(gettext("Error message: ") . $response);
899
						return false;
900
					}
901
					if (!is_array($response["records"])) {
902
						log_error(gettext("Unexpected response: ") . $response);
903
						return false;
904
					}
905
					foreach($response["records"] as $record) {
906
						if (($record["domainName"] == $this->_dnsDomain) &&
907
							($record["host"] == $this->_dnsHost) &&
908
							($record["type"] == $record_type)) {
909
								$record_id = $record["id"];
910
								$existing_ttl = $record["ttl"];
911
								break;
912
						}
913
					}
914
					if (!$record_id && $response["nextPage"])
915
					{
916
						log_error(gettext("Too many (>1000) Name.com DNS records. Paging not supported."));
917
						return false;
918
					}
919
					// Either update an existing record or create a new one.
920
					$post_data['host'] = $this->_dnsHost;
921
					$post_data['type'] = $record_type;
922
					$post_data['answer'] = $this->_dnsIP;
923
					$post_data['ttl'] = max($this->_dnsTTL ?: $existing_ttl, 300);
924
					curl_setopt($ch, CURLOPT_URL, "{$namedotcom_api}/{$record_id}");
925
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
926
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
927
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $record_id ? "PUT" : "POST");
928
					break;
929
				case 'namecheap':
930
					/* Example:
931
						https://dynamicdns.park-your-domain.com/update?host=[host_name]&domain=[domain.com]&password=[domain_password]&ip=[your_ip]
932
					*/
933
					$needsIP = FALSE;
934
					$dnspass = trim($this->_dnsPass);
935
					$server = "https://dynamicdns.park-your-domain.com/update?host={$this->_dnsHost}&domain={$this->_dnsDomain}&password={$dnspass}&ip={$this->_dnsIP}";
936
					curl_setopt($ch, CURLOPT_URL, $server);
937
					break;
938
				case 'nicru':
939
				case 'nicru-v6':
940
					/* see https://www.nic.ru/help/dynamic-dns-for-developers_5810.html */
941
					$needsIP = FALSE;
942
					if (is_ipaddrv6($this->_dnsIP)) {
943
						$iptype = "ipv6";
944
					} else {
945
						$iptype = "myip";
946
					}
947
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
948
					$server = "https://api.nic.ru/dyndns/update?hostname={$this->_dnsHost}&{$iptype}={$this->_dnsIP}";
949
					curl_setopt($ch, CURLOPT_URL, $server);
950
					break;
951
				case 'yandex':
952
				case 'yandex-v6':
953
					// https://yandex.com/dev/connect/directory/api/concepts/domains/dns-records-via-pdd.html
954

    
955
					if (is_ipaddrv4($this->_dnsIP)) {
956
						$type = 'A';
957
					} else {
958
						$type = 'AAAA';
959
					}
960

    
961
					// get record_id
962
					curl_setopt($ch, CURLOPT_URL, "https://pddimp.yandex.ru/api2/admin/dns/list?domain={$this->_dnsDomain}");
963
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('PddToken: ' . $this->_dnsPass));
964
					$output = json_decode(curl_exec($ch), true);
965
					if (is_array($output["records"])) {
966
						foreach($output["records"] as $record) {
967
							if (($record["domain"] == $this->_dnsDomain) &&
968
							    ($record["subdomain"] == $this->_dnsHost) &&
969
							    ($record["type"] == $type)) {
970
								$record_id = $record["record_id"];
971
							}
972
						}
973
					}
974

    
975
					if ($record_id) {
976
						$action = 'edit';
977
						$post_data['record_id'] = $record_id;
978
					} else {
979
						$action = 'add';
980
					}
981

    
982
					$post_data['domain'] = $this->_dnsDomain;
983
					$post_data['subdomain'] = $this->_dnsHost;
984
					$post_data['content'] = $this->_dnsIP;
985
					$post_data['type'] = $type;
986
					if ($this->_dnsTTL) {
987
						$post_data['ttl'] = $this->_dnsTTL;
988
					}
989

    
990
					curl_setopt($ch, CURLOPT_URL, 'https://pddimp.yandex.ru/api2/admin/dns/' . $action);
991
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('PddToken: ' . $this->_dnsPass));
992
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
993
					break;
994
				case 'duiadns':
995
				case 'duiadns-v6':
996
					$needsIP = FALSE;
997
					$server = "https://ipv4.duiadns.net/dyndns.duia?";
998
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
999
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1000
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1001
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1002
					break;
1003
				case 'he-net':
1004
				case 'he-net-v6':
1005
					$needsIP = FALSE;
1006
					$server = "https://dyn.dns.he.net/nic/update?";
1007
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1008
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1009
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&password=' . $this->_dnsPass . '&myip=' . $this->_dnsIP);
1010
					break;
1011
				case 'he-net-tunnelbroker':
1012
					$needsIP = FALSE;
1013
					$server = "https://ipv4.tunnelbroker.net/nic/update?";
1014
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1015
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1016
					break;
1017
				case 'selfhost':
1018
					$needsIP = FALSE;
1019
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
1020
						$this->_dnsWildcard = "ON";
1021
					}
1022
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1023
					$server = "https://carol.selfhost.de/nic/update";
1024
					$port = "";
1025
					if ($this->_dnsServer) {
1026
						$server = $this->_dnsServer;
1027
					}
1028
					if ($this->_dnsPort) {
1029
						$port = ":" . $this->_dnsPort;
1030
					}
1031
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
1032
					break;
1033
				case 'strato':
1034
					$needsIP = FALSE;
1035
					$server = 'https://dyndns.strato.com/nic/update';
1036
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1037
					curl_setopt($ch, CURLOPT_URL, $server . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1038
					break;
1039
				case 'route53':
1040
					require_once("r53.class");
1041
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
1042
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
1043
					$xmlreq = $r53->getRequestBody($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
1044
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
1045
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
1046
					if($this->_dnsVerboseLog){
1047
						log_error(sprintf("Sending request to: %s", $apiurl));
1048
						foreach($httphead as $hv){
1049
							log_error(sprintf("Header: %s", $hv));
1050
						}
1051
						log_error(sprintf("XMLPOST: %s", $xmlreq));
1052
					}
1053
					curl_setopt($ch, CURLOPT_URL, $apiurl);
1054
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
1055
					break;
1056
				case 'route53-v6':
1057
					require_once("r53.class");
1058
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
1059
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
1060
					$xmlreq = $r53->getRequestBodyV6($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
1061
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
1062
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
1063
					if($this->_dnsVerboseLog){
1064
						log_error(sprintf("Sending request to: %s", $apiurl));
1065
						foreach($httphead as $hv){
1066
							log_error(sprintf("Header: %s", $hv));
1067
						}
1068
						log_error(sprintf("XMLPOST: %s", $xmlreq));
1069
					}
1070
					curl_setopt($ch, CURLOPT_URL, $apiurl);
1071
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
1072
					break;
1073
				case 'cloudflare-v6':
1074
				case 'cloudflare':
1075
					$this->_FQDN = ltrim($this->_FQDN, '@.');
1076
					$isv6 = ($this->_dnsService === 'cloudflare-v6');
1077
					$recordType = $isv6 ? "AAAA" : "A";
1078
					$needsIP = TRUE;
1079
					$dnsServer ='api.cloudflare.com';
1080
					$dnsHost = str_replace(' ', '', $this->_dnsHost);
1081

    
1082
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1083

    
1084
					if ((!$this->_dnsUser) || (strpos($this->_dnsUser, '@') !== false)) {
1085
						if (strpos($this->_dnsUser, '@') !== false) {
1086
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1087
										'X-Auth-Email: ' . $this->_dnsUser,
1088
										'X-Auth-Key: ' . $this->_dnsPass,
1089
										'Content-Type: application/json'
1090
										));
1091
						} else {
1092
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1093
										'Authorization: Bearer ' . $this->_dnsPass,
1094
										'Content-Type: application/json'
1095
										));
1096
						}
1097

    
1098
						// Get zone ID
1099
						$getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$this->_dnsDomain}";
1100
						curl_setopt($ch, CURLOPT_URL, $getZoneId);
1101
						$output = json_decode(curl_exec($ch));
1102
						$zone = $output->result[0]->id;
1103
					} else {
1104
						curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1105
							'Authorization: Bearer ' . $this->_dnsPass,
1106
							'Content-Type: application/json'
1107
						));
1108

    
1109
						$zone = $this->_dnsUser;
1110
					}
1111

    
1112
					if ($zone) { // If zone ID was found get host ID
1113
						$getHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records?name={$this->_FQDN}&type={$recordType}";
1114
						curl_setopt($ch, CURLOPT_URL, $getHostId);
1115
						$output = json_decode(curl_exec($ch));
1116
						$host = $output->result[0]->id;
1117
						if ($host) { // If host ID was found update host
1118
							$hostData = array(
1119
								"content" => "{$this->_dnsIP}",
1120
								"type" => "{$recordType}",
1121
								"proxied" => $this->_dnsProxied,
1122
								"name" => "{$this->_dnsHost}",
1123
								"ttl" => empty($this->_dnsTTL) ? 1 : (int) $this->_dnsTTL
1124
							);
1125
							$data_json = json_encode($hostData);
1126
							$updateHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records/{$host}";
1127
							curl_setopt($ch, CURLOPT_URL, $updateHostId);
1128
							curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1129
							curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
1130
						}
1131
					}
1132
					break;
1133
				case 'eurodns':
1134
					$needsIP = TRUE;
1135
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1136
					$server = "https://update.eurodyndns.org/update/";
1137
					$port = "";
1138
					if ($this->_dnsPort) {
1139
						$port = ":" . $this->_dnsPort;
1140
					}
1141
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1142
					break;
1143
				case 'gratisdns':
1144
					$needsIP = TRUE;
1145
					$server = "https://ssl.gratisdns.dk/ddns.phtml";
1146
					curl_setopt($ch, CURLOPT_URL, $server . '?u=' . $this->_dnsUser . '&p=' . $this->_dnsPass . '&h=' . $this->_dnsHost . '&d=' . $this->_dnsDomain . '&i=' . $this->_dnsIP);
1147
					break;
1148
				case 'ovh-dynhost':
1149
					$needsIP = FALSE;
1150
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
1151
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1152
					$server = "https://www.ovh.com/nic/update";
1153
					$port = "";
1154
					if ($this->_dnsServer) {
1155
						$server = $this->_dnsServer;
1156
					}
1157
					if ($this->_dnsPort) {
1158
						$port = ":" . $this->_dnsPort;
1159
					}
1160
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
1161
					break;
1162
				case 'citynetwork':
1163
					$needsIP = TRUE;
1164
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1165
					$server = 'https://dyndns.citynetwork.se/nic/update';
1166
					$port = "";
1167
					if ($this->_dnsServer) {
1168
						$server = $this->_dnsServer;
1169
					}
1170
					if ($this->_dnsPort) {
1171
						$port = ":" . $this->_dnsPort;
1172
					}
1173
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1174
					break;
1175
				case 'dnsimple':
1176
				case 'dnsimple-v6':
1177
					/* Uses DNSimple's v2 REST API
1178
					   Requires the Account ID as the username (found in the URL when pull up the domain)
1179
					   And an API Token for the password (generated in the User Settings -> API tokens area of the website)
1180
					   Piggybacks on Route 53's ZoneID field for the DNSimple record ID to update
1181
					   The DNS record MUST exist before it can update since it performs a PATCH operation
1182
					   Data sent as JSON over HTTPS */
1183
					$needsIP = TRUE;
1184
					$server = 'https://api.dnsimple.com/v2/';
1185
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
1186
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-Type: application/json', 'Authorization: Bearer ' . $this->_dnsPass));
1187
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsUser . '/zones/' . $this->_dnsHost . '/records/' . $this->_dnsZoneID);
1188
					curl_setopt($ch, CURLOPT_POSTFIELDS, '{"content":"' . $this->_dnsIP . '","ttl":"' . $this->_dnsTTL . '"}');
1189
					break;
1190
				case 'godaddy':
1191
				case 'godaddy-v6':
1192
					/* Uses GoDaddy's REST API
1193
					   Requires username and Account API sso-key passed in header
1194
					   Data sent as JSON */
1195
					$needsIP = TRUE;
1196
					$server = 'https://api.godaddy.com/v1/domains/';
1197
					$recordType = $this->_useIPv6 ? "AAAA" : "A";
1198
					$url = $server . $this->_dnsDomain . '/records/' . $recordType . '/' . $this->_dnsHost;
1199
					$jsondata = '[{"data":"' . $this->_dnsIP . '","ttl":' . $this->_dnsTTL . '}]';
1200
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1201
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1202
						'Accept: application/json',
1203
						'Content-Type: application/json',
1204
						'Authorization: sso-key ' . $this->_dnsUser . ':' . $this->_dnsPass
1205
					));
1206
					curl_setopt($ch, CURLOPT_URL, $url);
1207
					curl_setopt($ch, CURLOPT_POSTFIELDS, $jsondata);
1208
					break;
1209
				case 'googledomains':
1210
					$needsIP = FALSE;
1211
					$post_data['username:password'] = $this->_dnsUser . ':' . $this->_dnsPass;
1212
					$post_data['hostname'] = $this->_dnsHost;
1213
					$post_data['myip'] = $this->_dnsIP;
1214
					$post_data['offline'] = 'no';
1215
					$server = "https://domains.google.com/nic/update";
1216
					$port = "";
1217
					curl_setopt($ch, CURLOPT_URL, 'https://domains.google.com/nic/update');
1218
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1219
					curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
1220
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1221
					curl_setopt($ch, CURLOPT_POST, 1);
1222
					break;
1223
				case 'dnsmadeeasy':
1224
					$needsIP = TRUE;
1225
					$server = "https://cp.dnsmadeeasy.com/servlet/updateip";
1226
					$username = empty($this->_dnsUser) ? "" : "&username={$this->dnsUser}";
1227
					curl_setopt($ch, CURLOPT_URL, $server . '?password=' . $this->_dnsPass . '&id=' . $this->_dnsHost . '&ip=' . $this->_dnsIP . $username);
1228
					break;
1229
				case 'spdyn':
1230
				case 'spdyn-v6':
1231
					$needsIP = FALSE;
1232
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1233
					$server = "https://update.spdyn.de/nic/update";
1234
					$port = "";
1235
					if ($this->_dnsServer) {
1236
						$server = $this->_dnsServer;
1237
					}
1238
					if ($this->_dnsPort) {
1239
						$port = ":" . $this->_dnsPort;
1240
					}
1241
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1242
					break;
1243
				case 'all-inkl':
1244
					$needsIP = FALSE;
1245
					$server = 'https://dyndns.kasserver.com/';
1246
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1247
					curl_setopt($ch, CURLOPT_URL, $server . 'myip=' . $this->_dnsIP);
1248
					break;
1249
				case 'hover':
1250
					$needsIP = FALSE;
1251
					$port = "";
1252
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1253
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1254

    
1255
					//step 1: login to API
1256
					$post_data['username'] = $this->_dnsUser;
1257
					$post_data['password'] = $this->_dnsPass;
1258
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1259
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
1260
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1261
					$output = curl_exec($ch);
1262

    
1263
					//extract the cookies
1264
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1265
					if( count($cookies[1]) > 0 ){
1266
						$cookie_data = implode("; ",$cookies[1]);
1267
					}
1268

    
1269
					//step 2: find the id of the A record
1270
					$post_data = null;
1271
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1272
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1273
					curl_setopt($ch, CURLOPT_HEADER, 0);
1274
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
1275
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1276

    
1277
					$output = curl_exec($ch);
1278
					$pregHost = preg_quote($this->_dnsHost);
1279
					$pregDomain = preg_quote($this->_dnsDomain);
1280
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$pregHost}\",\"type\":\"A\".*?\$/", $output, $hostID);
1281
					$hostID = $hostID[1];
1282
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{[^\}]*?\"name\":\"{$pregHost}\",\"type\":\"A\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
1283
					$hostIP = $hostIP[1];
1284
					unset($pregHost);
1285
					unset($pregDomain);
1286

    
1287
					//step 3: update the IP
1288
					if ($hostID) {
1289
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1290
						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1291
						curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1292
						$post_data['content'] = $this->_dnsIP;
1293
						curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1294
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1295
						curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns/{$hostID}");
1296
						log_error("HostID:{$hostID}, OldIP:{$hostIP}");
1297
					}
1298
					break;
1299
				case 'onecom':
1300
				case 'onecom-v6':
1301
					/* see https://redmine.pfsense.org/issues/11293
1302
					 * and https://redmine.pfsense.org/issues/12352 */
1303

    
1304
					curl_setopt($ch, CURLOPT_URL, "https://www.one.com/admin/");
1305
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1306
					curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
1307
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1308
					$output = curl_exec($ch);
1309
					$last_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
1310

    
1311
					// extract the cookies
1312
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1313
					if (count($cookies[1]) > 0) {
1314
						$cookie_data = implode("; ", $cookies[1]);
1315
					}
1316

    
1317
					// login in
1318
					$post_data['username'] = $this->_dnsUser;
1319
					$post_data['password'] = $this->_dnsPass;
1320
					$post_data['credentialId'] = '';
1321

    
1322
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1323
					curl_setopt($ch, CURLOPT_URL, $last_url);
1324
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1325
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1326
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1327
					$output = curl_exec($ch);
1328

    
1329
					// extract the cookies
1330
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1331
					if (count($cookies[1]) > 0) {
1332
						$cookie_data = implode("; ", $cookies[1]);
1333
					}
1334

    
1335
					// gets all DNS records of the domain.
1336
					$post_data = null;
1337
					$url = "https://www.one.com/admin/api/domains/" . $this->_dnsDomain + "/dns/custom_records";
1338
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1339
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1340
					curl_setopt($ch, CURLOPT_HEADER, 0);
1341
					curl_setopt($ch, CURLOPT_URL, $url);
1342
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1343
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1344
					$output = curl_exec($ch);
1345
					$result = json_decode($output, true);
1346
					$records = $result['result']['data'];
1347

    
1348
					// finds the record id of a record from it's subdomain
1349
					foreach ($records as $rec) {
1350
						if ($rec['attributes']['prefix'] == $this->_dnsHostname) {
1351
							$id = $rec['id'];
1352
							break;
1353
						}
1354
					}
1355
					if (!$id) {
1356
						log_error("Could not find one.com hostname record id");
1357
						return false;
1358
					}
1359

    
1360
					// changes the IP Address of a TYPE A record. Default TTL=3800
1361
					$tosend = array();
1362
					$tosend['type'] = 'dns_service_records';
1363
					$tosend['id'] = $id;
1364
					if (is_ipaddrv4($this->_dnsIP)) {
1365
						$rectype = 'A';
1366
					} else {
1367
						$rectype = 'AAAA';
1368
					}
1369
					$tosend['attributes'] = array(
1370
						'type' => $rectype,
1371
						'prefix' => $this->_dnsHostname,
1372
						'content' => $this->_dnsIP,
1373
						'ttl' => $this->_dnsTTL
1374
					);
1375
					$url = "https://www.one.com/admin/api/domains/" . $this->_dnsDomain .
1376
						 "/dns/custom_records/" . $id;
1377
					curl_setopt($ch, CURLOPT_URL, $url);
1378
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
1379
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($tosend));
1380
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
1381
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1382
					break;
1383
				case 'dreamhost':
1384
				case 'dreamhost-v6':
1385
					$needsIP = TRUE;
1386
					$isv6 = ($this->_dnsService === 'dreamhost-v6');
1387
					$server = 'https://api.dreamhost.com/';
1388
					$post_data['key'] = $this->_dnsPass;
1389
					$post_data['unique_id'] = uniqid($this->_dnsHost);
1390
					$post_data['cmd'] = 'dns-add_record';
1391
					$post_data['format'] = 'json';
1392
					$post_data['value'] = $this->_dnsIP;
1393
					$post_data['record'] = $this->_dnsHost;
1394
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1395
					$post_data['comment'] = "Updated by pfSense:$this->_dnsUser on " . date('c');
1396
					$port = "";
1397
					if ($this->_dnsServer) {
1398
						$server = $this->_dnsServer;
1399
					}
1400
					if ($this->_dnsPort) {
1401
						$port = ":" . $this->_dnsPort;
1402
					}
1403
					curl_setopt($ch, CURLOPT_URL, $server . $port);
1404
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1405
					break;
1406
				case 'digitalocean':
1407
				case 'digitalocean-v6':
1408
					// Get record ID
1409
					$server = 'https://api.digitalocean.com/v2/domains/';
1410
					$isv6 = ($this->_dnsService === 'digitalocean-v6');
1411
					$url = $server . $this->_dnsDomain . '/records';
1412
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$this->_dnsPass}"));
1413
					curl_setopt($ch, CURLOPT_URL, $url);
1414
					$output = json_decode(curl_exec($ch));
1415
					if (!is_array($output->domain_records)) {
1416
						$output->domain_records = array();
1417
					}
1418

    
1419
					// DO's API lists 20 NS records per page, so additional pages needs to be downloaded
1420
					// https://redmine.pfsense.org/issues/10952
1421
					$_domain_records = $output->domain_records;
1422
					$_count = count($_domain_records);
1423
					$_total = 0;
1424
					if (property_exists($output, 'meta')) {
1425
						$meta = $output->meta;
1426
						if (property_exists($meta, 'total')) {
1427
							$_total = $meta->total;
1428
						}
1429
					}
1430
					$_next = '...';
1431
					$_last = '';
1432
					while ($_next != $_last) {
1433
						$_next = '';
1434
						if (property_exists($output, 'links')) {
1435
							$_links = $output->links;
1436
							if (property_exists($_links, 'pages')) {
1437
								$_pages = $_links->pages;
1438
								if (property_exists($_pages, 'next')) {
1439
									$_next = $_pages->next;
1440
								}
1441
								if (property_exists($_pages, 'last')) {
1442
									$_last = $_pages->last;
1443
								}
1444
								if ($_next != '') {
1445
									echo "getting $_next\n";
1446
									curl_setopt($ch, CURLOPT_URL, $_next);
1447
									$output = json_decode(curl_exec($ch));
1448
									if (!is_array($output->domain_records)) {
1449
										$output->domain_records = array();
1450
									}
1451
									$_domain_records = array_merge($_domain_records,$output->domain_records);
1452
								}
1453
							}
1454
						}
1455
					}
1456
					$_count = count($_domain_records);
1457

    
1458
					foreach($_domain_records as $dnsRecord) {
1459
						// NS records are named @ in DO's API, so check type as well
1460
						// https://redmine.pfsense.org/issues/9171
1461
						if ($this->_dnsHost == $dnsRecord->name && $dnsRecord->type == ($isv6 ? 'AAAA' : 'A')) {
1462
							$recordID = $dnsRecord->id;
1463
							break;
1464
						}
1465
					}
1466

    
1467
					// Create/update record
1468
					if ($recordID == null) {
1469
						$url = $server . $this->_dnsDomain . '/records';
1470
					} else {
1471
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1472
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1473
					}
1474
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1475
					$post_data['ttl'] = $this->_dnsTTL;
1476
					$post_data['name'] = $this->_dnsHost;
1477
					$post_data['data'] = $this->_dnsIP;
1478
					curl_setopt($ch, CURLOPT_URL, $url);
1479
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1480
					break;
1481
				case 'cloudns':
1482
					/* Uses ClouDNS REST API
1483
					   Requires auth-id or sub-auth-id or sub-auth-user */
1484
					// Step 1: Find the Record ID
1485
					$url = 'https://api.cloudns.net/dns/records.json';
1486
					$post_data['auth-id'] = $this->_dnsUser;
1487
					$post_data['auth-password'] = $this->_dnsPass;
1488
					$post_data['domain-name'] = $this->_dnsDomain;
1489
					$post_data['host'] = $this->_dnsHost;
1490
					$post_data['type'] = 'a';
1491
					curl_setopt($ch, CURLOPT_URL, $url);
1492
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1493
					$output = json_decode(curl_exec($ch));
1494
					$recordID = key(get_object_vars($output));
1495

    
1496
					// Step 2: Set the record
1497
					$needsIP = TRUE;
1498
					$url = 'https://api.cloudns.net/dns/mod-record.json';
1499
					$post_data = array();
1500
					$post_data['auth-id'] = $this->_dnsUser;
1501
					$post_data['auth-password'] = $this->_dnsPass;
1502
					$post_data['domain-name'] = $this->_dnsDomain;
1503
					$post_data['record-id'] = $recordID;
1504
					$post_data['host'] = $this->_dnsHost;
1505
					$post_data['record'] = $this->_dnsIP;
1506
					$post_data['ttl'] = $this->_dnsTTL;
1507
					curl_setopt($ch, CURLOPT_URL, $url);
1508
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1509
					break;
1510
				case 'azurev6':
1511
				case 'azure':
1512
					$hostname = "{$this->_dnsHost}";
1513
					$resourceid = trim($this->_dnsZoneID);
1514
					$app_id = $this->_dnsUser;
1515
					$client_secret = $this->_dnsPass;
1516
					$newip = $this->_dnsIP;
1517
					$newttl = $this->_dnsTTL;
1518
						// ensure resourceid starts with / and has no trailing /
1519
					$resourceid = '/' . trim($resourceid, '/');
1520
						// extract subscription id from resource id
1521
					preg_match('/\\/subscriptions\\/(?<sid>[^\\/]*)/', $resourceid, $result);
1522
					$subscriptionid = isset($result['sid']) ? $result['sid'] : '';
1523
					if (isset($result['sid'])) {
1524
						$subscriptionid = $result['sid'];
1525
					} else {
1526
						log_error("Azure subscription id not found in resource id");
1527
						return false;
1528
					}
1529
						// find tenant id from subscription id
1530
					curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01");
1531
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1532
					curl_setopt($ch, CURLOPT_HEADER, 1);
1533
					curl_setopt($ch, CURLOPT_NOBODY, 1);
1534
					$output = curl_exec($ch);
1535
					$pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?<tid>[^"]*)/i';
1536
					preg_match($pattern, $output, $result);
1537
					if (isset($result['tid'])) {
1538
						$tenantid = $result['tid'];
1539
					} else {
1540
						log_error("Tenant ID not found");
1541
						return false;
1542
					}
1543
						// get an bearer token
1544
					curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token");
1545
					curl_setopt($ch, CURLOPT_POST, 1);
1546
					$body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret);
1547
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1548
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1549
					$server_output = curl_exec($ch);
1550
					$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1551
					preg_match("/\"access_token\":\"(?<tok>[^\"]*)\"/", $server_output, $result);
1552
					if (isset($result['tok'])) {
1553
						$bearertoken = $result['tok'];
1554
					} else {
1555
						log_error("no valid bearer token");
1556
						return false;
1557
					}
1558
						// Update the DNS record
1559
					if ($this->_useIPv6) {
1560
						$url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01";
1561
						$body = '{"properties":{"TTL":"' . $newttl . '", "AAAARecords":[{"ipv6Address":"' . $newip . '"}]}}';
1562
					} else {
1563
						$url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01";
1564
						$body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}';
1565
					}
1566
					$request_headers = array();
1567
					$request_headers[] = 'Accept: application/json';
1568
					$request_headers[] = 'Authorization: Bearer ' . $bearertoken;
1569
					$request_headers[] = 'Content-Type: application/json';
1570
					curl_setopt($ch, CURLOPT_URL, $url);
1571
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1572
					curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1573
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1574
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1575
					curl_setopt($ch, CURLOPT_HEADER, 1);
1576
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1577
					break;
1578
				case 'linode':
1579
				case 'linode-v6':
1580
					$linode_api = "https://api.linode.com/v4";
1581
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1582
					$record_name = $this->_dnsHost == "@" ? "" : $this->_dnsHost;
1583
					$err_prefix = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): (" . gettext("Error") . ")";
1584
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1585
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1586
						'Accept: application/json',
1587
						'Content-Type: application/json',
1588
						'Authorization: Bearer ' . $this->_dnsPass
1589
					));
1590

    
1591
					// get domain id
1592
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains");
1593
					$domains_output = curl_exec($ch);
1594
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1595
					if ( $http_code == 401 && preg_match('/invalid oauth token/i', $domains_output) ) {
1596
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authentication Failed"));
1597
						return false;
1598
					} else if ( $http_code == 401 ) {
1599
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authorization Failed"));
1600
						return false;
1601
					} else if ( $http_code != 200 ) {
1602
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1603
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1604
						return false;
1605
					}
1606

    
1607
					$domains_result = json_decode($domains_output, TRUE);
1608
					foreach($domains_result["data"] as $domains_entry) {
1609
						if ($domains_entry["domain"] == $this->_dnsDomain) {
1610
							$domain_id = $domains_entry["id"];
1611
						}
1612
					}
1613
					if ( ! $domain_id ) {
1614
						log_error("{$err_prefix} " . gettext("no domain ID for domain") . ": '{$this->_dnsDomain}'");
1615
						return false;
1616
					}
1617
					if ($this->_dnsVerboseLog) {
1618
						log_error("_update(): " . sprintf(gettext("found domain id: %s"), $domain_id));
1619
					}
1620

    
1621
					// get existing record if present
1622
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1623
					$records_output = curl_exec($ch);
1624
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1625
					if ( $http_code != 200 )
1626
					{
1627
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1628
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1629
						return false;
1630
					}
1631

    
1632
					$records_result = json_decode($records_output, TRUE);
1633
					foreach($records_result["data"] as $records_entry) {
1634
						if ( $records_entry["type"] == $record_type && $records_entry["name"] == $record_name ) {
1635
							// not adding support for pagination at this time, hope you have < 100 records!
1636
							$record = $records_entry;
1637
						}
1638
					}
1639
					if ($this->_dnsVerboseLog) {
1640
						log_error("_update(): " . ( $record ? sprintf(gettext("found existing record id: %s"), $record["id"]) : gettext("no matching record found") ));
1641
					}
1642

    
1643
					if (is_array($record)) {
1644
						// update existing record
1645
						$record["target"] = $this->_dnsIP;
1646
						$record["ttl_sec"] = (int) $this->_dnsTTL; // linode may round this up, 0 = zone default
1647
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1648
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1649
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records/" . $record["id"]);
1650
					} else {
1651
						// create a new record
1652
						$record = array(
1653
							"type"    => $record_type,
1654
							"name"    => $record_name,
1655
							"target"  => $this->_dnsIP,
1656
							"ttl_sec" => (int) $this->_dnsTTL, // linode may round this up, 0 = zone default
1657
						);
1658
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1659
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
1660
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1661
					}
1662
					break;
1663
				case 'dynv6':
1664
					$needIP = TRUE;
1665
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv4=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1666
					break;
1667
				case 'dynv6-v6':
1668
					$needIP = TRUE;
1669
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv6=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1670
					break;
1671
				case 'gandi-livedns':
1672
				case 'gandi-livedns-v6':
1673
					// https://api.gandi.net/docs/livedns/#v5-livedns-domains-fqdn-records-rrset_name-rrset_type
1674
					// PUT overrides the existing record or creates if none exists
1675
					$gandi_api = 'https://api.gandi.net/v5/livedns/domains/';
1676
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1677
					$url = "{$gandi_api}{$this->_dnsDomain}/records/{$this->_dnsHost}/{$record_type}";
1678

    
1679
					$headers = array(
1680
						'Authorization: Apikey ' . $this->_dnsPass,
1681
						'Content-Type: application/json'
1682
					);
1683
					$body = array(
1684
						"rrset_ttl"  => $this->_dnsTTL,
1685
						"rrset_values" => array($this->_dnsIP),
1686
					);
1687

    
1688
					curl_setopt($ch, CURLOPT_URL, $url);
1689
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1690
					curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
1691
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
1692
					break;
1693
				case 'desec':
1694
					curl_setopt($ch, CURLOPT_URL, 'https://update.dedyn.io/?hostname=' . $this->_dnsHost);
1695
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Token ' . $this->_dnsPass));
1696
					break;
1697
				case 'desec-v6':
1698
					curl_setopt($ch, CURLOPT_URL, 'https://update6.dedyn.io/?hostname=' . $this->_dnsHost);
1699
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Token ' . $this->_dnsPass));
1700
					break;
1701
				default:
1702
					break;
1703
			}
1704
			if ($this->_dnsService != 'ods') {
1705
				curl_setopt($ch, CURLOPT_HEADER, 1);
1706
				if ($this->_curlProxy == true) {
1707
					set_curlproxy($ch);
1708
				}
1709
				$response = curl_exec($ch);
1710
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1711
				$header = substr($response, 0, $header_size);
1712
				$data = substr($response, $header_size);
1713
				if ($this->_dnsVerboseLog) {
1714
					foreach (explode(PHP_EOL, $header) as $hv) {
1715
						log_error(sprintf("Response Header: %s", rtrim($hv)));
1716
				  	}
1717
					log_error(sprintf("Response Data: %s", $data));
1718
				}
1719
				$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1720
				$curl_error = @curl_error($ch);
1721
				@curl_close($ch);
1722
				$this->_checkStatus($http_code, $curl_error, $data, $header);
1723
			}
1724
		}
1725

    
1726
		/**
1727
		 * Private Function (added 23 Feb 17)
1728
		 *   Send Removal To Selected Service.
1729
		 *
1730
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1731
		 *   is to remove the existing record and add a new record.
1732
		 *
1733
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1734
		 */
1735
		function _remove($existing_ip = NULL) {
1736
			$remove_allowed = false;
1737
			if ($this->_dnsVerboseLog) {
1738
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1739
			}
1740

    
1741
			if (strstr($this->_dnsRequestIf, "_vip")) {
1742
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1743
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1744
			} else {
1745
				$realparentif = $this->_dnsRequestIf;
1746
			}
1747

    
1748
			$ch = curl_init();
1749

    
1750
			if ($this->_useIPv6 == false) {
1751
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1752
			}
1753

    
1754
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1755
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1756
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1757
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1758

    
1759
			switch ($this->_dnsService) {
1760
			case 'dreamhost':
1761
			case 'dreamhost-v6':
1762
				$server = 'https://api.dreamhost.com/';
1763
				$post_data['key'] = $this->_dnsPass;
1764
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1765
				$post_data['cmd'] = 'dns-remove_record';
1766
				$post_data['format'] = 'json';
1767
				$post_data['value'] = $existing_ip;
1768
				$post_data['record'] = $this->_dnsHost;
1769
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1770
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1771
				$port = "";
1772
				if ($this->_dnsServer) {
1773
					$server = $this->_dnsServer;
1774
				}
1775
				if ($this->_dnsPort) {
1776
					$port = ":" . $this->_dnsPort;
1777
				}
1778
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1779
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1780
				$remove_allowed = true;
1781
				break;
1782
			default:
1783
				break;
1784
			}
1785
			if ($remove_allowed) {
1786
				curl_setopt($ch, CURLOPT_HEADER, 1);
1787
				$response = curl_exec($ch);
1788
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1789
				$header = substr($response, 0, $header_size);
1790
				$data = substr($response, $header_size);
1791
				$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1792
				$curl_error = @curl_error($ch);
1793
				@curl_close($ch);
1794
				$this->_checkStatus($http_code, $curl_error, $data, $header);
1795
			}
1796
		}
1797

    
1798
		/**
1799
		 * Private Function (added 23 Feb 17)
1800
		 * Retrieves current DNS records from an external API source.
1801
		 *
1802
		 * Some services cannot perform new operations without the caller
1803
		 * providing existing record information.
1804
		 */
1805
		function _lookup_current() {
1806
			$lookup_allowed = false;
1807
			if ($this->_dnsVerboseLog) {
1808
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1809
			}
1810

    
1811
			if (strstr($this->_dnsRequestIf, "_vip")) {
1812
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1813
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1814
			} else {
1815
				$realparentif = $this->_dnsRequestIf;
1816
			}
1817

    
1818
			$ch = curl_init();
1819

    
1820
			if ($this->_useIPv6 == false) {
1821
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1822
			}
1823

    
1824
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1825
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1826
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1827
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1828

    
1829
			switch ($this->_dnsService) {
1830
			case 'dreamhost':
1831
			case 'dreamhost-v6':
1832
				$server = 'https://api.dreamhost.com/';
1833
				$post_data['key'] = $this->_dnsPass;
1834
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1835
				$post_data['cmd'] = 'dns-list_records';
1836
				$post_data['format'] = 'json';
1837
				$port = "";
1838
				if ($this->_dnsServer) {
1839
					$server = $this->_dnsServer;
1840
				}
1841
				if ($this->_dnsPort) {
1842
					$port = ":" . $this->_dnsPort;
1843
				}
1844
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1845
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1846
				$lookup_allowed = true;
1847
				break;
1848
			default:
1849
				break;
1850
			}
1851
			if ($lookup_allowed) {
1852
				curl_setopt($ch, CURLOPT_HEADER, 1);
1853
				$response = curl_exec($ch);
1854
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1855
				$header = substr($response, 0, $header_size);
1856
				$data = substr($response, $header_size);
1857
				$this->_checkLookupStatus($ch, $data, $header);
1858
				@curl_close($ch);
1859
			}
1860
		}
1861

    
1862
		/*
1863
		 * Private Function (added 23 Feb 17)
1864
		 *   Retrieve Lookup Status from the provided data and/or header
1865
		 */
1866
		function _checkLookupStatus($ch, $data, $header) {
1867
			if ($this->_dnsVerboseLog) {
1868
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1869
			}
1870
			$success_str = "(" . gettext("Success") . ") ";
1871
			$error_str = "(" . gettext("Error") . ") ";
1872
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1873

    
1874
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1875
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1876
				log_error($status);
1877
				$this->status = $status;
1878
				return;
1879
			}
1880
			switch ($this->_dnsService) {
1881
			case 'dreamhost':
1882
			case 'dreamhost-v6':
1883
				$result = json_decode($data,true);
1884
				if($result["result"] != "success") {
1885
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1886
					$this->_debug($data);
1887
					return;
1888
				} else {
1889
					foreach($result["data"] as $key => $row) {
1890
						if($row["record"] == $this->_dnsHost &&
1891
								(($row["type"] == "A" && !$this->_useIPv6)
1892
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1893
								)) {
1894
							if($row["editable"] == 0) {
1895
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1896
								continue;
1897
							}
1898
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1899
						}
1900
					}
1901
				}
1902
				if (!is_array($this->_existingRecords)){
1903
					if ($this->_dnsVerboseLog) {
1904
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1905
					}
1906
				}
1907
				break;
1908
			default:
1909
				break;
1910
			}
1911
		}
1912

    
1913
		/*
1914
		 * Private Function (added 12 July 2005) [beta]
1915
		 *   Retrieve Update Status
1916
		 */
1917
		function _checkStatus($http_code, $curl_error, $data, $header) {
1918
			if ($this->_dnsVerboseLog) {
1919
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1920
			}
1921
			$successful_update = false;
1922
			$success_str = "(" . gettext("Success") . ") ";
1923
			$error_str = "(" . gettext("Error") . ") ";
1924
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1925

    
1926
			if ($this->_dnsService != 'ods' && !empty($curl_error)) {
1927
				$status = gettext("Curl error occurred:") . " {$curl_error}";
1928
				log_error($status);
1929
				$this->status = $status;
1930
				return;
1931
			}
1932
			switch ($this->_dnsService) {
1933
				// The special custom provider
1934
				case 'custom':
1935
				case 'custom-v6':
1936
					$successful_update = false;
1937
					if ($this->_dnsResultMatch == "") {
1938
						$successful_update = true;
1939
					} else {
1940
						$this->_dnsResultMatch = str_replace("%IP%", $this->_dnsIP, $this->_dnsResultMatch);
1941
						$matches = preg_split("/(?<!\\\\)\\|/", $this->_dnsResultMatch);
1942
						foreach ($matches as $match) {
1943
							$match= str_replace("\\|", "|", $match);
1944
							if (strcmp($match, trim($data, "\t\n\r")) == 0) {
1945
								$successful_update = true;
1946
							}
1947
						}
1948
						unset ($matches);
1949
					}
1950
					if ($successful_update == true) {
1951
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1952
					} else {
1953
						$status = $status_intro . $error_str . gettext("Result did not match.") . " [" . $data . "]";
1954
					}
1955
					break;
1956
				// Providers in an alphabetical order. Add code for new providers below in a correct position.
1957
				case 'dyfi':
1958
					// see specification at https://www.dy.fi/page/specification
1959
					if (preg_match('/badauth/i', $data)) {
1960
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1961
					} else if (preg_match('/nohost/i', $data)) {
1962
						$status = $status_intro . $error_str . gettext("No such host");
1963
					} else if (preg_match('/notfqdn/i', $data)) {
1964
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1965
					} else if (preg_match('/badip/i', $data)) {
1966
						$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.");
1967
					} else if (preg_match('/nochg/i', $data)) {
1968
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1969
						$successful_update = true;
1970
					} else if (preg_match('/good/i', $data)) {
1971
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1972
						$successful_update = true;
1973
					} else if (preg_match('/dnserr/i', $data)) {
1974
						$status = $status_intro . $error_str . gettext("Server side error.");
1975
					} else if (preg_match('/abuse/i', $data)) {
1976
						$status = $status_intro . $error_str . gettext("Updating too frequently, considered abuse.");
1977
					} else {
1978
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1979
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1980
						$this->_debug($data);
1981
					}
1982
					break;
1983
				case 'glesys':
1984
					$status_intro = "GleSYS ({$this->_dnsHost}): ";
1985
					if (preg_match("/OK/i", $data)) {
1986
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1987
						$successful_update = true;
1988
					} else {
1989
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1990
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1991
						$this->_debug($data);
1992
					}
1993
					break;
1994
				//
1995
				// Not yet ordered providers.
1996
				// TODO: When editing a provider, move it above in a correct position.
1997
				//
1998
				case 'dnsomatic':
1999
					$status_intro = "DNS-O-Matic ({$this->_dnsHost}): ";
2000
					if (preg_match('/badauth/i', $data)) {
2001
						$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.");
2002
					} else if (preg_match('/notfqdn /i', $data)) {
2003
						$status = $status_intro . gettext("The hostname specified is not a fully-qualified domain name. If no hostnames included, notfqdn will be returned once.");
2004
					} else if (preg_match('/nohost/i', $data)) {
2005
						$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.");
2006
					} else if (preg_match('/numhost/i', $data)) {
2007
						$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.");
2008
					} else if (preg_match('/abuse/i', $data)) {
2009
						$status = $status_intro . gettext("The hostname is blocked for update abuse.");
2010
					} else if (preg_match('/good/i', $data)) {
2011
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2012
						$successful_update = true;
2013
					} else if (preg_match('/dnserr/i', $data)) {
2014
						$status = $status_intro . gettext("DNS error encountered. Stop updating for 30 minutes.");
2015
					} else {
2016
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2017
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2018
						$this->_debug($data);
2019
					}
2020
					break;
2021
				case 'domeneshop':
2022
				case 'domeneshop-v6':
2023
					/* Responds with HTTP 204 on success.
2024
					 * see https://api.domeneshop.no/docs/index.html#tag/ddns/paths/~1dyndns~1update/get */
2025

    
2026
					if ($http_code == "204") {
2027
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2028
						$successful_update = true;
2029
					} else {
2030
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $http_code . ")";
2031
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2032
						$this->_debug($data);
2033
					}
2034
					break;
2035
				case 'onecom':
2036
				case 'onecom-v6':
2037

    
2038
					if (($http_code == "200") || ($http_code == "204")) {
2039
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2040
						$successful_update = true;
2041
					} else {
2042
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $http_code . ")";
2043
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2044
						$this->_debug($data);
2045
					}
2046
					break;
2047
				case 'citynetwork':
2048
					if (preg_match('/notfqdn/i', $data)) {
2049
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2050
					} else if (preg_match('/nohost/i', $data)) {
2051
						$status = $status_intro . $error_str . gettext("No such host");
2052
					} else if (preg_match('/nochg/i', $data)) {
2053
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2054
						$successful_update = true;
2055
					} else if (preg_match('/good/i', $data)) {
2056
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2057
						$successful_update = true;
2058
					} else if (preg_match('/badauth/i', $data)) {
2059
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2060
					} else {
2061
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2062
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2063
						$this->_debug($data);
2064
					}
2065
					break;
2066
				case 'ovh-dynhost':
2067
				case 'dyndns':
2068
					if (preg_match('/notfqdn/i', $data)) {
2069
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2070
					} else if (preg_match('/nochg/i', $data)) {
2071
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2072
						$successful_update = true;
2073
					} else if (preg_match('/good/i', $data)) {
2074
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2075
						$successful_update = true;
2076
					} else if (preg_match('/noauth/i', $data)) {
2077
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2078
					} else {
2079
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2080
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2081
						$this->_debug($data);
2082
					}
2083
					break;
2084
				case 'dyndns-static':
2085
					if (preg_match('/notfqdn/i', $data)) {
2086
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2087
					} else if (preg_match('/nochg/i', $data)) {
2088
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2089
						$successful_update = true;
2090
					} else if (preg_match('/good/i', $data)) {
2091
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2092
						$successful_update = true;
2093
					} else if (preg_match('/noauth/i', $data)) {
2094
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2095
					} else {
2096
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2097
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2098
						$this->_debug($data);
2099
					}
2100
					break;
2101
				case 'dyndns-custom':
2102
					if (preg_match('/notfqdn/i', $data)) {
2103
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2104
					} else if (preg_match('/nochg/i', $data)) {
2105
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2106
						$successful_update = true;
2107
					} else if (preg_match('/good/i', $data)) {
2108
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2109
						$successful_update = true;
2110
					} else if (preg_match('/noauth/i', $data)) {
2111
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2112
					} else {
2113
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2114
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2115
						$this->_debug($data);
2116
					}
2117
					break;
2118
				case 'dhs':
2119
					break;
2120
				case 'noip':
2121
				case 'noip-free':
2122
					if (preg_match('/good/i', $data)) {
2123
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2124
						$successful_update = true;
2125
					} else if (preg_match('/nochg/i', $data)) {
2126
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2127
						$successful_update = true;
2128
					} else if (preg_match('/nohost/i', $data)) {
2129
						$status = $status_intro . $error_str . gettext("Hostname supplied does not exist under specified account");
2130
					} else if (preg_match('/badauth/i', $data)) {
2131
						$status = $status_intro . $error_str . gettext("Invalid username password combination");
2132
					} else if (preg_match('/badagent/i', $data)) {
2133
						$status = $status_intro . $error_str . gettext("Client disabled");
2134
					} else if (preg_match('/\!donator/i', $data)) {
2135
						$status = $status_intro . $error_str . gettext("Feature is not available");
2136
					} else if (preg_match('/abuse/i', $data)) {
2137
						$status = $status_intro . $error_str . gettext("Username is blocked due to abuse");
2138
					} else if (preg_match('/911/i', $data)) {
2139
						$status = $status_intro . $error_str . gettext("Fatal error");
2140
					} else {
2141
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2142
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2143
						$this->_debug($data);
2144
					}
2145
					break;
2146
				case 'noip-v6':
2147
				case 'noip-free-v6':
2148
					list($ip, $code) = explode(":", $data);
2149
					switch ($code) {
2150
						case 0:
2151
							$status = $status_intro . $success_str . gettext("IP address is current, no update performed.");
2152
							$successful_update = true;
2153
							break;
2154
						case 1:
2155
							$status = $status_intro . $success_str . gettext("DNS hostname update successful.");
2156
							$successful_update = true;
2157
							break;
2158
						case 2:
2159
							$status = $status_intro . $error_str . gettext("Hostname supplied does not exist.");
2160
							break;
2161
						case 3:
2162
							$status = $status_intro . $error_str . gettext("Invalid Username.");
2163
							break;
2164
						case 4:
2165
							$status = $status_intro . $error_str . gettext("Invalid Password.");
2166
							break;
2167
						case 5:
2168
							$status = $status_intro . $error_str . gettext("Too many updates sent.");
2169
							break;
2170
						case 6:
2171
							$status = $status_intro . $error_str . gettext("Account disabled due to violation of No-IP terms of service.");
2172
							break;
2173
						case 7:
2174
							$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.");
2175
							break;
2176
						case 8:
2177
							$status = $status_intro . $error_str . gettext("Disabled / Locked Hostname.");
2178
							break;
2179
						case 9:
2180
							$status = $status_intro . $error_str . gettext("Host updated is configured as a web redirect and no update was performed.");
2181
							break;
2182
						case 10:
2183
							$status = $status_intro . $error_str . gettext("Group supplied does not exist.");
2184
							break;
2185
						case 11:
2186
							$status = $status_intro . $success_str . gettext("DNS group update is successful.");
2187
							$successful_update = true;
2188
							break;
2189
						case 12:
2190
							$status = $status_intro . $success_str . gettext("DNS group is current, no update performed.");
2191
							$successful_update = true;
2192
							break;
2193
						case 13:
2194
							$status = $status_intro . $error_str . gettext("Update client support not available for supplied hostname or group.");
2195
							break;
2196
						case 14:
2197
							$status = $status_intro . $error_str . gettext("Hostname supplied does not have offline settings configured.");
2198
							break;
2199
						case 99:
2200
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
2201
							break;
2202
						case 100:
2203
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
2204
							break;
2205
						default:
2206
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2207
							$this->_debug(gettext("Unknown Response:") . " " . $data);
2208
							break;
2209
					}
2210
					break;
2211
				case 'easydns':
2212
					if (preg_match('/NOACCESS/i', $data)) {
2213
						$status = $status_intro . $error_str . gettext("Authentication Failed: Username and/or Password was Incorrect.");
2214
					} else if (preg_match('/NOSERVICE/i', $data)) {
2215
						$status = $status_intro . $error_str . gettext("No Service: Dynamic DNS Service has been disabled for this domain.");
2216
					} else if (preg_match('/ILLEGAL INPUT/i', $data)) {
2217
						$status = $status_intro . $error_str . gettext("Illegal Input: Self-Explanatory");
2218
					} else if (preg_match('/TOOSOON/i', $data)) {
2219
						$status = $status_intro . $error_str . gettext("Too Soon: Not Enough Time Has Elapsed Since Last Update");
2220
					} else if (preg_match('/NOERROR/i', $data)) {
2221
						$status = $status_intro . $success_str . gettext("IP Updated Successfully!");
2222
						$successful_update = true;
2223
					} else {
2224
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2225
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2226
						$this->_debug($data);
2227
					}
2228
					break;
2229
				case 'easydns-v6':
2230
					if (preg_match('/NOACCESS/i', $data)) {
2231
						$status = $status_intro . $error_str . gettext("Authentication Failed: Username and/or Password was Incorrect.");
2232
					} else if (preg_match('/NOSERVICE/i', $data)) {
2233
						$status = $status_intro . $error_str . gettext("No Service: Dynamic DNS Service has been disabled for this domain.");
2234
					} else if (preg_match('/ILLEGAL INPUT/i', $data)) {
2235
						$status = $status_intro . $error_str . gettext("Illegal Input: Self-Explanatory");
2236
					} else if (preg_match('/TOOSOON/i', $data)) {
2237
						$status = $status_intro . $error_str . gettext("Too Soon: Not Enough Time Has Elapsed Since Last Update");
2238
					} else if (preg_match('/NOERROR/i', $data)) {
2239
						$status = $status_intro . $success_str . gettext("IP Updated Successfully!");
2240
						$successful_update = true;
2241
					} else {
2242
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2243
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2244
						$this->_debug($data);
2245
					}
2246
					break;
2247
				case 'hn':
2248
					/* FIXME: add checks */
2249
					break;
2250
				case 'zoneedit':
2251
					if (preg_match('/799/i', $data)) {
2252
						$status = $status_intro . "(" . gettext("Error 799") . ") " . gettext("Update Failed!");
2253
					} else if (preg_match('/700/i', $data)) {
2254
						$status = $status_intro . "(" . gettext("Error 700") . ") " . gettext("Update Failed!");
2255
					} else if (preg_match('/200/i', $data)) {
2256
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2257
						$successful_update = true;
2258
					} else if (preg_match('/201/i', $data)) {
2259
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2260
						$successful_update = true;
2261
					} else {
2262
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2263
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2264
						$this->_debug($data);
2265
					}
2266
					break;
2267
				case 'dyns':
2268
					if (preg_match("/400/i", $data)) {
2269
						$status = $status_intro . $error_str . gettext("Bad Request - The URL was malformed. Required parameters were not provided.");
2270
					} else if (preg_match('/402/i', $data)) {
2271
						$status = $status_intro . $error_str . gettext("Update Too Soon - Attempted to update too quickly since last change.");
2272
					} else if (preg_match('/403/i', $data)) {
2273
						$status = $status_intro . $error_str . gettext("Database Error - There was a server-sided database error.");
2274
					} else if (preg_match('/405/i', $data)) {
2275
						$status = $status_intro . $error_str . sprintf(gettext('Hostname Error - The hostname (%1$s) doesn\'t belong to user (%2$s).'), $this->_dnsHost, $this->_dnsUser);
2276
					} else if (preg_match('/200/i', $data)) {
2277
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2278
						$successful_update = true;
2279
					} else {
2280
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2281
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2282
						$this->_debug($data);
2283
					}
2284
					break;
2285
				case 'ods':
2286
					if (preg_match("/299/i", $data)) {
2287
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2288
						$successful_update = true;
2289
					} else {
2290
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2291
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2292
						$this->_debug($data);
2293
					}
2294
					break;
2295
				case 'freedns':
2296
				case 'freedns-v6':
2297
					if (preg_match("/has not changed./i", $data)) {
2298
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2299
						$successful_update = true;
2300
					} else if (preg_match("/Updated/i", $data)) {
2301
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2302
						$successful_update = true;
2303
					} else {
2304
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2305
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2306
						$this->_debug($data);
2307
					}
2308
					break;
2309
				case 'freedns2':
2310
				case 'freedns2-v6':
2311
					if (preg_match("/No IP change detected/i", $data)) {
2312
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2313
						$successful_update = true;
2314
					} else if (preg_match("/Updated/i", $data)) {
2315
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2316
						$successful_update = true;
2317
					} else {
2318
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2319
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2320
						$this->_debug($data);
2321
					}
2322
					break;
2323
				case 'dnsexit':
2324
					if (preg_match("/is the same/i", $data)) {
2325
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2326
						$successful_update = true;
2327
					} else if (preg_match("/Success/i", $data)) {
2328
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2329
						$successful_update = true;
2330
					} else {
2331
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2332
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2333
						$this->_debug($data);
2334
					}
2335
					break;
2336
				case 'loopia':
2337
					if (preg_match("/nochg/i", $data)) {
2338
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2339
						$successful_update = true;
2340
					} else if (preg_match("/good/i", $data)) {
2341
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2342
						$successful_update = true;
2343
					} else if (preg_match('/badauth/i', $data)) {
2344
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2345
					} else {
2346
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2347
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2348
						$this->_debug($data);
2349
					}
2350
					break;
2351
				case 'opendns':
2352
					if (preg_match('/badauth/i', $data)) {
2353
						$status = $status_intro . $error_str . gettext("Not a valid username or password!");
2354
					} else if (preg_match('/nohost/i', $data)) {
2355
						$status = $status_intro . $error_str . gettext("Hostname specified does not exist.");
2356
						$successful_update = true;
2357
					} else if (preg_match('/good/i', $data)) {
2358
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2359
						$successful_update = true;
2360
					} else if (preg_match('/yours/i', $data)) {
2361
						$status = $status_intro . $error_str . gettext("Hostname specified exists, but not under the username specified.");
2362
					} else if (preg_match('/abuse/i', $data)) {
2363
						$status = $status_intro . $error_str . gettext("Updating too frequently, considered abuse.");
2364
					} else {
2365
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2366
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2367
						$this->_debug($data);
2368
					}
2369
					break;
2370
				case 'staticcling':
2371
					if (preg_match("/invalid ip/i", $data)) {
2372
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
2373
					} else if (preg_match('/required info missing/i', $data)) {
2374
						$status = $status_intro . $error_str . gettext("Bad Request - Required parameters were not provided.");
2375
					} else if (preg_match('/invalid characters/i', $data)) {
2376
						$status = $status_intro . $error_str . gettext("Bad Request - Illegal characters in either the username or the password.");
2377
					} else if (preg_match('/bad password/i', $data)) {
2378
						$status = $status_intro . $error_str . gettext("Invalid password.");
2379
					} else if (preg_match('/account locked/i', $data)) {
2380
						$status = $status_intro . $error_str . gettext("This account has been administratively locked.");
2381
					} else if (preg_match('/update too frequent/i', $data)) {
2382
						$status = $status_intro . $error_str . gettext("Updating too frequently.");
2383
					} else if (preg_match('/DB error/i', $data)) {
2384
						$status = $status_intro . $error_str . gettext("Server side error.");
2385
					} else if (preg_match('/success/i', $data)) {
2386
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2387
						$successful_update = true;
2388
					} else {
2389
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2390
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2391
						$this->_debug($data);
2392
					}
2393
					break;
2394
				case 'mythicbeasts':
2395
				case 'mythicbeasts-v6':
2396
					/* https://www.mythic-beasts.com/support/api/dnsv2/tutorial */
2397
					$result = json_decode($data, true);
2398
					if ($result['message'] == '1 records added') {
2399
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2400
						$successful_update = true;
2401
					} else {
2402
						log_error($status_intro . " ( " . gettext("Error message: ") . $result['error'] . " )");
2403
					}
2404
					break;
2405
				case 'name.com':
2406
				case 'name.com-v6':
2407
					if ($http_code == "200") {
2408
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2409
						$successful_update = true;
2410
					} else {
2411
						$status = $status_intro . "(" . gettext("Error, HTTP response code") . ": " . $http_code . ")";
2412
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2413
					}
2414
					break;
2415
				case 'namecheap':
2416
					$tmp = str_replace("^M", "", $data);
2417
					$ncresponse = @xml2array($tmp);
2418
					/* If XML parsing failed, it may be due to mismatched encoding on the response, drop the xml line. */
2419
					if (empty($ncresponse)) {
2420
						$ncresponse = @xml2array(substr($tmp, strpos($tmp, "\n") + 1));
2421
					}
2422
					if (preg_match("/internal server error/i", $data)) {
2423
						$status = $status_intro . $error_str . gettext("Server side error.");
2424
					} else if (preg_match("/request is badly formed/i", $data)) {
2425
						$status = $status_intro . $error_str . gettext("Badly Formed Request (check the settings).");
2426
					} else if ($ncresponse['interface-response']['ErrCount'] === "0") {
2427
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2428
						$successful_update = true;
2429
					} else if (is_numeric($ncresponse['interface-response']['ErrCount']) && ($ncresponse['interface-response']['ErrCount'] > 0)) {
2430
						$status = $status_intro . $error_str . implode(", ", $ncresponse["interface-response"]["errors"]);
2431
					} else {
2432
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2433
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2434
						$this->_debug($data);
2435
					}
2436
					break;
2437
				case 'nicru':
2438
				case 'nicru-v6':
2439
					$status_intro = "NIC.RU ({$this->_dnsHost}): ";
2440
					if (preg_match('/badauth/i', $data)) {
2441
						$status = $status_intro . gettext("The NIC.RU username or password specified are incorrect.");
2442
					} else if (preg_match('/notfqdn /i', $data)) {
2443
						$status = $status_intro . gettext("The hostname specified is not a fully-qualified domain name.");
2444
					} else if (preg_match('/nohost/i', $data)) {
2445
						$status = $status_intro . gettext("The hostname passed could not be matched to any records configured.");
2446
					} else if (preg_match('/good/i', $data)) {
2447
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2448
						$successful_update = true;
2449
					} else if (preg_match('/dnserr/i', $data)) {
2450
						$status = $status_intro . gettext("DNS error encountered. Stop updating for 30 minutes.");
2451
					} else {
2452
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2453
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2454
						$this->_debug($data);
2455
					}
2456
					break;
2457
				case 'duiadns':
2458
				case 'duiadns-v6':
2459
					if (preg_match("/error/i", $data)) {
2460
						$status = $status_intro . $error_str . gettext("Server side error.");
2461
					} else if (preg_match('/nohost/i', $data)) {
2462
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
2463
					} else if (preg_match('/badauth/i', $data)) {
2464
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
2465
					} else if (preg_match('/good/i', $data)) {
2466
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2467
						$successful_update = true;
2468
					} else if (preg_match('/nochg/i', $data)) {
2469
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
2470
						$successful_update = true;
2471
					} else {
2472
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2473
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2474
						$this->_debug($data);
2475
					}
2476
					break;
2477
				case 'he-net':
2478
				case 'he-net-v6':
2479
				case 'he-net-tunnelbroker':
2480
					if (preg_match("/badip/i", $data)) {
2481
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
2482
					} else if (preg_match('/nohost/i', $data)) {
2483
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
2484
					} else if (preg_match('/badauth/i', $data)) {
2485
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
2486
					} else if (preg_match('/good/i', $data)) {
2487
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2488
						$successful_update = true;
2489
					} else if (preg_match('/nochg/i', $data)) {
2490
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
2491
						$successful_update = true;
2492
					} else {
2493
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2494
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2495
						$this->_debug($data);
2496
					}
2497
					break;
2498
				case 'selfhost':
2499
					if (preg_match('/notfqdn/i', $data)) {
2500
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2501
					} else if (preg_match('/nochg/i', $data)) {
2502
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
2503
						$successful_update = true;
2504
					} else if (preg_match('/good/i', $data)) {
2505
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2506
						$successful_update = true;
2507
					} else if (preg_match('/noauth/i', $data)) {
2508
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2509
					} else {
2510
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2511
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2512
						$this->_debug($data);
2513
					}
2514
					break;
2515
				case 'strato':
2516
					if (preg_match('/good\s' . $this->_dnsIP . '/i', $data)) {
2517
						$status = $status_intro . $success_str . gettext("IP address updated successfully") . " (" . $this->_dnsIP . ")";
2518
						$successful_update = true;
2519
					} else if (preg_match('/good/i', $data)) {
2520
						$status = $status_intro . $error_str . gettext("IP result did not match");
2521
					} else if (preg_match('/nochg/i', $data)) {
2522
						$status = $status_intro . $success_str . gettext("IP was not changed");
2523
						$successful_update = true;
2524
					} else if (preg_match("/badauth/i", $data)) {
2525
						$status = $status_intro . $error_str . gettext("invalid username or password");
2526
					} else if (preg_match('/nohost/i', $data)) {
2527
						$status = $status_intro . $error_str . gettext("invalid hostname");
2528
					} else if (preg_match('/abuse/i', $data)) {
2529
						$status = $status_intro . $error_str . gettext("too many requests - please wait");
2530
					} else {
2531
						$status = $status_intro . "(" . gettext("unknown response") . ")";
2532
						log_error($status_intro . gettext("PAYLOAD:") . " " . $header . $data);
2533
						$this->_debug($data);
2534
						$this->_debug($header);
2535
					}
2536
					break;
2537
				case 'route53':
2538
				case 'route53-v6':
2539
					if(preg_match('/ErrorResponse/', $data)){
2540
						$status = $status_intro . $error_str . gettext("Route53 API call failed");
2541
						log_error(sprintf("error message: %s", $data));
2542
						$status_update = false;
2543
					} else {
2544
						$status = $status_intro . $success_str . gettext("IP address changed successfully");
2545
						$successful_update = true;
2546
					}
2547
					break;
2548
				case 'cloudflare-v6':
2549
				case 'cloudflare':
2550
					$output = json_decode($data);
2551
					if ($output->result->content === $this->_dnsIP) {
2552
						$status = $status_intro . $success_str . sprintf(gettext('%1$s updated to %2$s'), $this->_dnsHost, $this->_dnsIP);
2553
						$successful_update = true;
2554
					} elseif ($output->errors[0]->code === 9103) {
2555
						$status = $status_intro . $error_str . gettext("Invalid Credentials! Don't forget to use API Key for password field with Cloudflare.");
2556
					} elseif (($output->success) && (!$output->result[0]->id)) {
2557
						$status = $status_intro . $error_str . gettext("Zone or Host ID was not found, check the hostname.");
2558
					} else {
2559
						$status = $status_intro . gettext("UNKNOWN ERROR") . " - " . $output->errors[0]->message;
2560
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2561
					}
2562
					break;
2563
				case 'yandex-v6':
2564
				case 'yandex':
2565
					$result = json_decode($data, true);
2566
					if ($result['success'] == 'ok') {
2567
						$successful_update = true;
2568
					} elseif ($result['error'] == 'bad_domain') {
2569
						log_error($status_intro . $error_str .  gettext("The domain name was not specified or does not conform to the RFC"));
2570
					} elseif ($result['error'] == 'unknown') {
2571
						log_error($status_intro . $error_str .  gettext("A temporary failure or API error occurred"));
2572
					} elseif ($result['error'] == 'prohibited') {
2573
						log_error($status_intro . $error_str .  gettext("A forbidden domain name"));
2574
					} elseif ($result['error'] == 'bad_token') {
2575
						log_error($status_intro . $error_str .  gettext("Invalid PDD token"));
2576
					} elseif ($result['error'] == 'no_auth') {
2577
						log_error($status_intro . $error_str .  gettext("The PddToken header was omitted"));
2578
					} elseif ($result['error'] == 'not_allowed') {
2579
						log_error($status_intro . $error_str .  gettext("This operation is not allowed for this user"));
2580
					} elseif ($result['error'] == 'blocked') {
2581
						log_error($status_intro . $error_str .  gettext("Blocked domain"));
2582
					} elseif ($result['error'] == 'occupied') {
2583
						log_error($status_intro . $error_str .  gettext("The domain name is in use by another user"));
2584
					} elseif ($result['error'] == 'domain_limit_reached') {
2585
						log_error($status_intro . $error_str .  gettext("Exceeded the acceptable number of connected domains (50)"));
2586
					} elseif ($result['error'] == 'no_reply') {
2587
						log_error($status_intro . $error_str .  gettext("Yandex.Mail for Domain cannot connect to the server source for the import"));
2588
					} else {
2589
						log_error($status_intro . $error_str .  gettext("PAYLOAD:") . " " . $data);
2590
					}
2591
					break;
2592
				case 'eurodns':
2593
					if (preg_match('/notfqdn/i', $data)) {
2594
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2595
					} else if (preg_match('/nochg/i', $data)) {
2596
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2597
						$successful_update = true;
2598
					} else if (preg_match('/good/i', $data)) {
2599
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2600
						$successful_update = true;
2601
					} else if (preg_match('/badauth/i', $data)) {
2602
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2603
					} else {
2604
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2605
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2606
						$this->_debug($data);
2607
					}
2608
					break;
2609
				case 'gratisdns':
2610
					if (preg_match('/Forkerte værdier/i', $data)) {
2611
						$status = $status_intro . $error_str . gettext("Wrong values - Update could not be completed.");
2612
					} else if (preg_match('/Bruger login: Bruger eksistere ikke/i', $data)) {
2613
						$status = $status_intro . $error_str . gettext("Unknown username - User does not exist.");
2614
					} else if (preg_match('/Bruger login: 1Fejl i kodeord/i', $data)) {
2615
						$status = $status_intro . $error_str . gettext("Wrong password - Remember password is case sensitive.");
2616
					} else if (preg_match('/Domæne kan IKKE administreres af bruger/i', $data)) {
2617
						$status = $status_intro . $error_str . gettext("User unable to administer the selected domain.");
2618
					} else if (preg_match('/OK/i', $data)) {
2619
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2620
						$successful_update = true;
2621
					} else {
2622
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2623
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2624
						$this->_debug($data);
2625
					}
2626
					break;
2627
				case 'digitalocean':
2628
				case 'digitalocean-v6':
2629
					// Creating new records returns an HTTP 201, updating existing records get 200
2630
					// https://redmine.pfsense.org/issues/9171
2631
					if (preg_match("/HTTP\/2\s20[0,1]/i", $header)) {
2632
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2633
						$successful_update = true;
2634
					} else {
2635
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2636
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2637
						$this->_debug($data);
2638
					}
2639
					break;
2640
				case 'dnsimple':
2641
				case 'dnsimple-v6':
2642
					/* Responds with HTTP 200 on success.
2643
					   Responds with HTTP 4xx on error.
2644
					   Returns JSON data as body */
2645
;
2646
					if ($http_code == "200") {
2647
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2648
						$successful_update = true;
2649
					} else if (preg_match("/4\d\d/i", $http_code)) {
2650
						$arrbody = json_decode($data, true);
2651
						$message = $arrbody['message'] . ".";
2652
						if (isset($arrbody['errors']['content'])) {
2653
							foreach ($arrbody['errors']['content'] as $key => $content) {
2654
								$message .= " " . $content . ".";
2655
							}
2656
						}
2657
						$status = $status_intro . $error_str . $message;
2658
					} else {
2659
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $http_code . ")";
2660
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2661
						$this->_debug($data);
2662
					}
2663
					break;
2664
				case 'godaddy':
2665
				case 'godaddy-v6':
2666
					/* Responds with HTTP 200 on success.
2667
					   Responds with HTTP 4xx or  on error.
2668
					   Returns JSON data as body */
2669
;
2670
					if (preg_match("/\s200\sOK/i", $header)) {
2671
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2672
						$successful_update = true;
2673
					} else if (preg_match("/\s4\d\d\s/i", $header)) {
2674
						$arrbody = json_decode($data, true);
2675
						$message = $arrbody['message'] . ".";
2676
						if (isset($arrbody['fields'])) {
2677
							foreach ($arrbody['fields'] as $error) {
2678
								$message .= " " . $error['path'] . ": " . $error['message'] . ".";
2679
							}
2680
						}
2681
						$status = $status_intro . $error_str . $message;
2682
					} else {
2683
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2684
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2685
						$this->_debug($data);
2686
					}
2687
					break;
2688
				case 'googledomains':
2689
					if (preg_match('/notfqdn/i', $data)) {
2690
						$status = $status_intro . $error_str . gettext("Not A FQDN");
2691
					} else if (preg_match('/nochg/i', $data)) {
2692
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2693
						$successful_update = true;
2694
					} else if (preg_match('/good/i', $data)) {
2695
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2696
						$successful_update = true;
2697
					} else if (preg_match('/badauth/i', $data)) {
2698
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2699
					} else if (preg_match('/nohost/i', $data)) {
2700
						$status = $status_intro . $error_str . gettext("Hostname does not exist or DynDNS not enabled");
2701
					} else if (preg_match('/badagent/i', $data)) {
2702
						$status = $status_intro . $error_str . gettext("Bad request");
2703
					} else if (preg_match('/abuse/i', $data)) {
2704
						$status = $status_intro . $error_str . gettext("Dynamic DNS access has been blocked!");
2705
					} else if (preg_match('/911/i', $data)) {
2706
						$status = $status_intro . $error_str . gettext("Error on Google's end, retry in 5 minutes");
2707
					} else {
2708
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2709
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2710
						$this->_debug($data);
2711
					}
2712
					break;
2713
				case 'dnsmadeeasy':
2714
					switch ($data) {
2715
						case 'success':
2716
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2717
							$successful_update = true;
2718
							break;
2719
						case 'error-auth':
2720
							$status = $status_intro . $error_str . gettext("Invalid username or password");
2721
							break;
2722
						case 'error-auth-suspend':
2723
							$status = $status_intro . $error_str . gettext("Account suspended");
2724
							break;
2725
						case 'error-auth-voided':
2726
							$status = $status_intro . $error_str . gettext("Account revoked");
2727
							break;
2728
						case 'error-record-invalid':
2729
							$status = $status_intro . $error_str . gettext("Record does not exist in the system. Unable to update record");
2730
							break;
2731
						case 'error-record-auth':
2732
							$status = $status_intro . $error_str . gettext("User does not have access to this record");
2733
							break;
2734
						case 'error-record-ip-same':
2735
							$status = $status_intro . $success_str . gettext("No Change In IP Address");
2736
							$successful_update = true;
2737
							break;
2738
						case 'error-system':
2739
							$status = $status_intro . $error_str . gettext("General system error recognized by the system");
2740
							break;
2741
						case 'error':
2742
							$status = $status_intro . $error_str . gettext("General system error unrecognized by the system");
2743
							break;
2744
						default:
2745
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2746
							log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2747
							$this->_debug($data);
2748
							break;
2749
					}
2750
					break;
2751
				case 'spdyn':
2752
				case 'spdyn-v6':
2753
					if (preg_match('/notfqdn/i', $data)) {
2754
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2755
					} else if (preg_match('/nohost/i', $data)) {
2756
						$status = $status_intro . $error_str . gettext("No such host");
2757
					} else if (preg_match('/nochg/i', $data)) {
2758
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2759
						$successful_update = true;
2760
					} else if (preg_match('/good/i', $data)) {
2761
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2762
						$successful_update = true;
2763
					} else if (preg_match('/badauth/i', $data)) {
2764
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2765
					} else {
2766
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2767
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2768
						$this->_debug($data);
2769
					}
2770
					break;
2771
				case 'all-inkl':
2772
 					if (preg_match('/good\s' . $this->_dnsIP . '/i', $data)) {
2773
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2774
 							$successful_update = true;
2775
 					} else if (preg_match('/good/i', $data)) {
2776
						$status = $status_intro . $error_str . gettext("Result did not match.");
2777
					} else if (preg_match("/\s401\sUnauthorized/i", $header)) {
2778
						$status = $status_intro . $error_str . gettext("Invalid username or password");
2779
					}
2780
					else {
2781
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2782
							log_error($status_intro . gettext("PAYLOAD:") . " " . $header . $data);
2783
 							$this->_debug($data);
2784
 							$this->_debug($header);
2785
 					}
2786
 					break;
2787
				case 'hover':
2788
					if (preg_match('/succeeded":true/i', $data)) {
2789
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2790
						$successful_update = true;
2791
					} else {
2792
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2793
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2794
						$this->_debug($data);
2795
					}
2796
					break;
2797
				case 'cloudns':
2798
					$result = json_decode($data, true);
2799
					if ($result['status'] == 'Success') {
2800
						$successful_update = true;
2801
					} else {
2802
						log_error($result['status'] . "(" . $result['statusDescription'] . ")");
2803
					}
2804
					break;
2805
				case 'dreamhost':
2806
				case 'dreamhost-v6':
2807
					$result = json_decode($data,true);
2808
					if ($this->_dnsVerboseLog) {
2809
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2810
					}
2811
					switch ($result['data']) {
2812
					case 'success':
2813
					case 'record_added':
2814
					case 'record_removed':
2815
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2816
						$successful_update = true;
2817
						break;
2818
					case 'no_record':
2819
					case 'no_such_record ':
2820
						$status = $status_intro . $error_str . gettext("No record exists.");
2821
						break;
2822
					case 'no_type':
2823
					case 'no_such_type ':
2824
						$status = $status_intro . $error_str . gettext("No type exists.");
2825
						break;
2826
					case 'no_value':
2827
					case 'no_such_value ':
2828
						$status = $status_intro . $error_str . gettext("No value exists.");
2829
						break;
2830
					case 'no_such_zone':
2831
						$status = $status_intro . $error_str . gettext("No such zone exists.");
2832
						break;
2833
					case 'invalid_record':
2834
						$status = $status_intro . $error_str . gettext("The specified record is invalid.");
2835
						break;
2836
					case 'invalid_type':
2837
						$status = $status_intro . $error_str . gettext("The specified type is invalid.");
2838
						break;
2839
					case 'invalid_value':
2840
						$status = $status_intro . $error_str . gettext("The specified value is invalid.");
2841
						break;
2842
					case 'not_editable ':
2843
						$status = $status_intro . $error_str . gettext("Record is not editable.");
2844
						break;
2845
					case 'record_already_exists_not_editable':
2846
						$status = $status_intro . $error_str . gettext("Record exists but is not editable.");
2847
						break;
2848
					case 'record_already_exists_remove_first':
2849
						$status = $status_intro . $error_str . gettext("Record exists and must be removed before adding.");
2850
						break;
2851
					case 'internal_error_updating_zone':
2852
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2853
						break;
2854
					case 'internal_error_could_not_load_zone':
2855
						$status = $status_intro . $error_str . gettext("A remote server error occurred loading the zone.");
2856
						break;
2857
					case 'internal_error_could_not_update_zone':
2858
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2859
						break;
2860
					case 'internal_error_could_not_add_record':
2861
						$status = $status_intro . $error_str . gettext("A remote server error occurred adding a new record.");
2862
						break;
2863
					case 'internal_error_could_not_destroy_record ':
2864
						$status = $status_intro . $error_str . gettext("A remote server error occurred removing an existing record.");
2865
						break;
2866
					default:
2867
						break;
2868
					}
2869
				case 'azure':
2870
				case 'azurev6':
2871
					if ($http_code == 401) {
2872
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2873
					} else if ($http_code == 201) {
2874
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2875
						$successful_update = true;
2876
					} else if ($http_code == 200) {
2877
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2878
						$successful_update = true;
2879
					} else {
2880
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2881
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2882
						$this->_debug($data);
2883
					}
2884
					break;
2885
				case 'linode':
2886
				case 'linode-v6':
2887
					$status_intro = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): ";
2888
					$result = json_decode($data,true);
2889
					if ($this->_dnsVerboseLog) {
2890
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2891
					}
2892
					if ($http_code == 200 && isset($result["id"]) && ! isset($result["errors"])) {
2893
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2894
						$successful_update = true;
2895
					} else if ( $http_code == 401 && preg_match('/not authorized to use/i', $data) ) {
2896
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2897
					} else {
2898
						$status = $status_intro . $error_str .
2899
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" );
2900
					}
2901
					break;
2902
				case 'gandi-livedns':
2903
				case 'gandi-livedns-v6':
2904
					if ($http_code == 401) {
2905
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2906
					} else if ($http_code == 200 || $http_code == 201) {
2907
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2908
						$successful_update = true;
2909
					} else {
2910
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2911
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2912
						$this->_debug($data);
2913
					}
2914
					break;
2915
				case 'desec':
2916
				case 'desec-v6':
2917
					switch ($http_code) {
2918
						case '200':
2919
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2920
							$successful_update = true;
2921
							break;
2922
						case '401':
2923
							$status = $status_intro . $error_str . gettext("User Authorization Failed");
2924
							break;
2925
						default:
2926
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2927
							log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2928
							$this->_debug($data);
2929
							break;
2930
					}
2931
					break;
2932
				default:
2933
					break;
2934
			}
2935

    
2936
			if ($successful_update == true) {
2937
				/* Write WAN IP to cache file */
2938
				$wan_ip = $this->_checkIP();
2939
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2940
					$currentTime = time();
2941
					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));
2942
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2943
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2944
				} else {
2945
					@unlink($this->_cacheFile);
2946
				}
2947
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2948
					$currentTime = time();
2949
					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));
2950
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2951
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2952
				} else {
2953
					@unlink($this->_cacheFile_v6);
2954
				}
2955
			}
2956
			$this->status = $status;
2957
			log_error($status);
2958
		}
2959

    
2960
		/*
2961
		 * Private Function (added 12 July 05) [beta]
2962
		 *   Return Error, Set Last Error, and Die.
2963
		 */
2964
		function _error($errorNumber = '1') {
2965
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2966
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2967
			switch ($errorNumber) {
2968
				case 0:
2969
					break;
2970
				case 2:
2971
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2972
					break;
2973
				case 3:
2974
					$error = $err_str . gettext('No Username Provided.');
2975
					break;
2976
				case 4:
2977
					$error = $err_str . gettext('No Password Provided.');
2978
					break;
2979
				case 5:
2980
					$error = $err_str . gettext('No Hostname Provided.');
2981
					break;
2982
				case 6:
2983
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2984
					break;
2985
				case 7:
2986
					$error = $err_str . gettext('No Update URL Provided.');
2987
					break;
2988
				case 8:
2989
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2990
					break;
2991
				case 9:
2992
					$status = $err_str_r53 . gettext("Invalid TTL");
2993
					break;
2994
				case 10:
2995
					$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);
2996
					break;
2997
				default:
2998
					$error = $err_str . gettext('Unknown Response.');
2999
					/* FIXME: $data isn't in scope here */
3000
					/* $this->_debug($data); */
3001
					break;
3002
			}
3003
			$this->lastError = $error;
3004
			log_error($error);
3005
		}
3006

    
3007
		/*
3008
		 * Private Function (added 12 July 05) [beta]
3009
		 *   - Detect whether or not IP needs to be updated.
3010
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
3011
		 *      | work with other systems. pfSense base is FreeBSD.
3012
		 */
3013
		function _detectChange() {
3014
			if ($this->_dnsVerboseLog) {
3015
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
3016
			}
3017

    
3018
			$currentTime = time();
3019

    
3020
			$wan_ip = $this->_checkIP();
3021
			if ($wan_ip == 0) {
3022
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
3023
				return false;
3024
			}
3025
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
3026

    
3027
			if ($this->_useIPv6 == true) {
3028
				if (file_exists($this->_cacheFile_v6)) {
3029
					$contents = file_get_contents($this->_cacheFile_v6);
3030
					list($cacheIP, $cacheTime) = explode('|', $contents);
3031
					$this->_debug($cacheIP . '/' . $cacheTime);
3032
					$initial = false;
3033
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
3034
				} else {
3035
					$cacheIP = '::';
3036
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
3037
					$cacheTime = $currentTime;
3038
					$initial = true;
3039
					$log_error .= gettext("No Cached IPv6 found.");
3040
				}
3041
			} else {
3042
				if (file_exists($this->_cacheFile)) {
3043
					$contents = file_get_contents($this->_cacheFile);
3044
					list($cacheIP, $cacheTime) = explode('|', $contents);
3045
					$this->_debug($cacheIP . '/' . $cacheTime);
3046
					$initial = false;
3047
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
3048
				} else {
3049
					$cacheIP = '0.0.0.0';
3050
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
3051
					$cacheTime = $currentTime;
3052
					$initial = true;
3053
					$log_error .= gettext("No Cached IP found.");
3054
				}
3055
			}
3056
			if ($this->_dnsVerboseLog) {
3057
				log_error($log_error);
3058
			}
3059

    
3060
			// Convert seconds = days * hr/day * min/hr * sec/min
3061
			// We subtract 1 hour, so that when this code is executed few seconds
3062
			// before _dnsMaxCacheAgeDays has passed, then we still consider that
3063
			// a required number of days has passed.
3064
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60 - 60 * 60;
3065

    
3066
			$needs_updating = FALSE;
3067
			/* lets determine if the item needs updating */
3068
			if ($cacheIP != $wan_ip) {
3069
				$needs_updating = true;
3070
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
3071
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
3072
			}
3073
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
3074
				$needs_updating = true;
3075
				$this->_forceUpdateNeeded = true;
3076
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
3077
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
3078
			}
3079
			if ($initial == true) {
3080
				$needs_updating = true;
3081
				$update_reason .= gettext("Initial update.");
3082
			}
3083

    
3084
			/*   finally if we need updating then store the
3085
			 *   new cache value and return true
3086
			 */
3087
			if ($needs_updating == true) {
3088
				if ($this->_dnsVerboseLog) {
3089
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
3090
				}
3091
				return true;
3092
			}
3093

    
3094
			return false;
3095
		}
3096

    
3097
		/*
3098
		 * Private Function (added 16 July 05) [beta]
3099
		 *   - Writes debug information to a file.
3100
		 *   - This function is only called when a unknown response
3101
		 *   - status is returned from a DynDNS service provider.
3102
		 */
3103
		function _debug($data) {
3104
			global $g;
3105

    
3106
			if (!$g['debug']) {
3107
				return;
3108
			}
3109
			$string = date('m-d-y h:i:s') . ' - (' . $this->_debugID . ') - [' . $this->_dnsService . '] - ' . $data . "\n";
3110
			$file = fopen($this->_debugFile, 'a');
3111
			fwrite($file, $string);
3112
			fclose($file);
3113
		}
3114
		function _checkIP() {
3115
			if ($this->_dnsVerboseLog) {
3116
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
3117
			}
3118

    
3119
			if ($this->_useIPv6 == true) {
3120
				$ip_address = get_interface_ipv6($this->_if);
3121
				if (!is_ipaddrv6($ip_address)) {
3122
					return 0;
3123
				}
3124
			} else {
3125
				$ip_address = dyndnsCheckIP($this->_if);
3126
				if (!is_ipaddr($ip_address)) {
3127
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
3128
					return 0;
3129
				} elseif (is_private_ip($ip_address)) {
3130
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): Public IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
3131
					return 0;
3132
				}
3133
			}
3134
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
3135
				if ($this->_dnsVerboseLog) {
3136
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
3137
				}
3138
			} else {
3139
				if ($this->_dnsVerboseLog) {
3140
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
3141
				}
3142
			}
3143
			$this->_dnsIP = $ip_address;
3144

    
3145
			return $ip_address;
3146
		}
3147

    
3148
	}
3149

    
3150
?>
(16-16/62)