Project

General

Profile

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

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

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

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

    
504
			unlock($dyndnslck);
505
		}
506

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

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

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

    
524
			$ch = curl_init();
525

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

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

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

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

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

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

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

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

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

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

    
1080
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1081

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

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

    
1107
						$zone = $this->_dnsUser;
1108
					}
1109

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

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

    
1258
					//extract the cookies
1259
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1260
					if( count($cookies[1]) > 0 ){
1261
						$cookie_data = implode("; ",$cookies[1]);
1262
					}
1263

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

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

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

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

    
1306
					// extract the cookies
1307
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1308
					if (count($cookies[1]) > 0) {
1309
						$cookie_data = implode("; ", $cookies[1]);
1310
					}
1311

    
1312
					// login in
1313
					$post_data['username'] = $this->_dnsUser;
1314
					$post_data['password'] = $this->_dnsPass;
1315
					$post_data['credentialId'] = '';
1316

    
1317
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1318
					curl_setopt($ch, CURLOPT_URL, $last_url);
1319
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1320
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1321
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1322
					$output = curl_exec($ch);
1323

    
1324
					// extract the cookies
1325
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1326
					if (count($cookies[1]) > 0) {
1327
						$cookie_data = implode("; ", $cookies[1]);
1328
					}
1329

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

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

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

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

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

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

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

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

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

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

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

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

    
1674
					$headers = array(
1675
						'Authorization: Apikey ' . $this->_dnsPass,
1676
						'Content-Type: application/json'
1677
					);
1678
					$body = array(
1679
						"rrset_ttl"  => $this->_dnsTTL,
1680
						"rrset_values" => array($this->_dnsIP),
1681
					);
1682

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

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

    
1734
			if (strstr($this->_dnsRequestIf, "_vip")) {
1735
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1736
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1737
			} else {
1738
				$realparentif = $this->_dnsRequestIf;
1739
			}
1740

    
1741
			$ch = curl_init();
1742

    
1743
			if ($this->_useIPv6 == false) {
1744
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1745
			}
1746

    
1747
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1748
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1749
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1750
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1751

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

    
1789
		/**
1790
		 * Private Function (added 23 Feb 17)
1791
		 * Retrieves current DNS records from an external API source.
1792
		 *
1793
		 * Some services cannot perform new operations without the caller
1794
		 * providing existing record information.
1795
		 */
1796
		function _lookup_current() {
1797
			$lookup_allowed = false;
1798
			if ($this->_dnsVerboseLog) {
1799
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1800
			}
1801

    
1802
			if (strstr($this->_dnsRequestIf, "_vip")) {
1803
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1804
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1805
			} else {
1806
				$realparentif = $this->_dnsRequestIf;
1807
			}
1808

    
1809
			$ch = curl_init();
1810

    
1811
			if ($this->_useIPv6 == false) {
1812
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1813
			}
1814

    
1815
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1816
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1817
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1818
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1819

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

    
1853
		/*
1854
		 * Private Function (added 23 Feb 17)
1855
		 *   Retrieve Lookup Status from the provided data and/or header
1856
		 */
1857
		function _checkLookupStatus($ch, $data, $header) {
1858
			if ($this->_dnsVerboseLog) {
1859
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1860
			}
1861
			$success_str = "(" . gettext("Success") . ") ";
1862
			$error_str = "(" . gettext("Error") . ") ";
1863
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1864

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

    
1904
		/*
1905
		 * Private Function (added 12 July 2005) [beta]
1906
		 *   Retrieve Update Status
1907
		 */
1908
		function _checkStatus($ch, $data, $header) {
1909
			if ($this->_dnsVerboseLog) {
1910
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1911
			}
1912
			$successful_update = false;
1913
			$success_str = "(" . gettext("Success") . ") ";
1914
			$error_str = "(" . gettext("Error") . ") ";
1915
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1916

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

    
2017
                                        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2018

    
2019
					if ($code == "204") {
2020
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2021
						$successful_update = true;
2022
					} else {
2023
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $code . ")";
2024
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2025
						$this->_debug($data);
2026
					}
2027
					break;
2028
				case 'onecom':
2029
				case 'onecom-v6':
2030
                                        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2031

    
2032
					if (($code == "200") || ($code == "204")) {
2033
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2034
						$successful_update = true;
2035
					} else {
2036
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $code . ")";
2037
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2038
						$this->_debug($data);
2039
					}
2040
					break;
2041
				case 'citynetwork':
2042
					if (preg_match('/notfqdn/i', $data)) {
2043
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2044
					} else if (preg_match('/nohost/i', $data)) {
2045
						$status = $status_intro . $error_str . gettext("No such host");
2046
					} else if (preg_match('/nochg/i', $data)) {
2047
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2048
						$successful_update = true;
2049
					} else if (preg_match('/good/i', $data)) {
2050
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2051
						$successful_update = true;
2052
					} else if (preg_match('/badauth/i', $data)) {
2053
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2054
					} else {
2055
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2056
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2057
						$this->_debug($data);
2058
					}
2059
					break;
2060
				case 'ovh-dynhost':
2061
				case 'dyndns':
2062
					if (preg_match('/notfqdn/i', $data)) {
2063
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2064
					} else if (preg_match('/nochg/i', $data)) {
2065
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2066
						$successful_update = true;
2067
					} else if (preg_match('/good/i', $data)) {
2068
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2069
						$successful_update = true;
2070
					} else if (preg_match('/noauth/i', $data)) {
2071
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2072
					} else {
2073
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2074
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2075
						$this->_debug($data);
2076
					}
2077
					break;
2078
				case 'dyndns-static':
2079
					if (preg_match('/notfqdn/i', $data)) {
2080
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2081
					} else if (preg_match('/nochg/i', $data)) {
2082
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2083
						$successful_update = true;
2084
					} else if (preg_match('/good/i', $data)) {
2085
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2086
						$successful_update = true;
2087
					} else if (preg_match('/noauth/i', $data)) {
2088
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2089
					} else {
2090
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2091
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2092
						$this->_debug($data);
2093
					}
2094
					break;
2095
				case 'dyndns-custom':
2096
					if (preg_match('/notfqdn/i', $data)) {
2097
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2098
					} else if (preg_match('/nochg/i', $data)) {
2099
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2100
						$successful_update = true;
2101
					} else if (preg_match('/good/i', $data)) {
2102
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2103
						$successful_update = true;
2104
					} else if (preg_match('/noauth/i', $data)) {
2105
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2106
					} else {
2107
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2108
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2109
						$this->_debug($data);
2110
					}
2111
					break;
2112
				case 'dhs':
2113
					break;
2114
				case 'noip':
2115
				case 'noip-free':
2116
					if (preg_match('/good/i', $data)) {
2117
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2118
						$successful_update = true;
2119
					} else if (preg_match('/nochg/i', $data)) {
2120
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2121
						$successful_update = true;
2122
					} else if (preg_match('/nohost/i', $data)) {
2123
						$status = $status_intro . $error_str . gettext("Hostname supplied does not exist under specified account");
2124
					} else if (preg_match('/badauth/i', $data)) {
2125
						$status = $status_intro . $error_str . gettext("Invalid username password combination");
2126
					} else if (preg_match('/badagent/i', $data)) {
2127
						$status = $status_intro . $error_str . gettext("Client disabled");
2128
					} else if (preg_match('/\!donator/i', $data)) {
2129
						$status = $status_intro . $error_str . gettext("Feature is not available");
2130
					} else if (preg_match('/abuse/i', $data)) {
2131
						$status = $status_intro . $error_str . gettext("Username is blocked due to abuse");
2132
					} else if (preg_match('/911/i', $data)) {
2133
						$status = $status_intro . $error_str . gettext("Fatal error");
2134
					} else {
2135
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2136
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2137
						$this->_debug($data);
2138
					}
2139
					break;
2140
				case 'noip-v6':
2141
				case 'noip-free-v6':
2142
					list($ip, $code) = explode(":", $data);
2143
					switch ($code) {
2144
						case 0:
2145
							$status = $status_intro . $success_str . gettext("IP address is current, no update performed.");
2146
							$successful_update = true;
2147
							break;
2148
						case 1:
2149
							$status = $status_intro . $success_str . gettext("DNS hostname update successful.");
2150
							$successful_update = true;
2151
							break;
2152
						case 2:
2153
							$status = $status_intro . $error_str . gettext("Hostname supplied does not exist.");
2154
							break;
2155
						case 3:
2156
							$status = $status_intro . $error_str . gettext("Invalid Username.");
2157
							break;
2158
						case 4:
2159
							$status = $status_intro . $error_str . gettext("Invalid Password.");
2160
							break;
2161
						case 5:
2162
							$status = $status_intro . $error_str . gettext("Too many updates sent.");
2163
							break;
2164
						case 6:
2165
							$status = $status_intro . $error_str . gettext("Account disabled due to violation of No-IP terms of service.");
2166
							break;
2167
						case 7:
2168
							$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.");
2169
							break;
2170
						case 8:
2171
							$status = $status_intro . $error_str . gettext("Disabled / Locked Hostname.");
2172
							break;
2173
						case 9:
2174
							$status = $status_intro . $error_str . gettext("Host updated is configured as a web redirect and no update was performed.");
2175
							break;
2176
						case 10:
2177
							$status = $status_intro . $error_str . gettext("Group supplied does not exist.");
2178
							break;
2179
						case 11:
2180
							$status = $status_intro . $success_str . gettext("DNS group update is successful.");
2181
							$successful_update = true;
2182
							break;
2183
						case 12:
2184
							$status = $status_intro . $success_str . gettext("DNS group is current, no update performed.");
2185
							$successful_update = true;
2186
							break;
2187
						case 13:
2188
							$status = $status_intro . $error_str . gettext("Update client support not available for supplied hostname or group.");
2189
							break;
2190
						case 14:
2191
							$status = $status_intro . $error_str . gettext("Hostname supplied does not have offline settings configured.");
2192
							break;
2193
						case 99:
2194
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
2195
							break;
2196
						case 100:
2197
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
2198
							break;
2199
						default:
2200
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2201
							$this->_debug(gettext("Unknown Response:") . " " . $data);
2202
							break;
2203
					}
2204
					break;
2205
				case 'easydns':
2206
					if (preg_match('/NOACCESS/i', $data)) {
2207
						$status = $status_intro . $error_str . gettext("Authentication Failed: Username and/or Password was Incorrect.");
2208
					} else if (preg_match('/NOSERVICE/i', $data)) {
2209
						$status = $status_intro . $error_str . gettext("No Service: Dynamic DNS Service has been disabled for this domain.");
2210
					} else if (preg_match('/ILLEGAL INPUT/i', $data)) {
2211
						$status = $status_intro . $error_str . gettext("Illegal Input: Self-Explanatory");
2212
					} else if (preg_match('/TOOSOON/i', $data)) {
2213
						$status = $status_intro . $error_str . gettext("Too Soon: Not Enough Time Has Elapsed Since Last Update");
2214
					} else if (preg_match('/NOERROR/i', $data)) {
2215
						$status = $status_intro . $success_str . gettext("IP Updated Successfully!");
2216
						$successful_update = true;
2217
					} else {
2218
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2219
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2220
						$this->_debug($data);
2221
					}
2222
					break;
2223
				case 'easydns-v6':
2224
					if (preg_match('/NOACCESS/i', $data)) {
2225
						$status = $status_intro . $error_str . gettext("Authentication Failed: Username and/or Password was Incorrect.");
2226
					} else if (preg_match('/NOSERVICE/i', $data)) {
2227
						$status = $status_intro . $error_str . gettext("No Service: Dynamic DNS Service has been disabled for this domain.");
2228
					} else if (preg_match('/ILLEGAL INPUT/i', $data)) {
2229
						$status = $status_intro . $error_str . gettext("Illegal Input: Self-Explanatory");
2230
					} else if (preg_match('/TOOSOON/i', $data)) {
2231
						$status = $status_intro . $error_str . gettext("Too Soon: Not Enough Time Has Elapsed Since Last Update");
2232
					} else if (preg_match('/NOERROR/i', $data)) {
2233
						$status = $status_intro . $success_str . gettext("IP Updated Successfully!");
2234
						$successful_update = true;
2235
					} else {
2236
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2237
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2238
						$this->_debug($data);
2239
					}
2240
					break;
2241
				case 'hn':
2242
					/* FIXME: add checks */
2243
					break;
2244
				case 'zoneedit':
2245
					if (preg_match('/799/i', $data)) {
2246
						$status = $status_intro . "(" . gettext("Error 799") . ") " . gettext("Update Failed!");
2247
					} else if (preg_match('/700/i', $data)) {
2248
						$status = $status_intro . "(" . gettext("Error 700") . ") " . gettext("Update Failed!");
2249
					} else if (preg_match('/200/i', $data)) {
2250
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2251
						$successful_update = true;
2252
					} else if (preg_match('/201/i', $data)) {
2253
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2254
						$successful_update = true;
2255
					} else {
2256
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2257
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2258
						$this->_debug($data);
2259
					}
2260
					break;
2261
				case 'dyns':
2262
					if (preg_match("/400/i", $data)) {
2263
						$status = $status_intro . $error_str . gettext("Bad Request - The URL was malformed. Required parameters were not provided.");
2264
					} else if (preg_match('/402/i', $data)) {
2265
						$status = $status_intro . $error_str . gettext("Update Too Soon - Attempted to update too quickly since last change.");
2266
					} else if (preg_match('/403/i', $data)) {
2267
						$status = $status_intro . $error_str . gettext("Database Error - There was a server-sided database error.");
2268
					} else if (preg_match('/405/i', $data)) {
2269
						$status = $status_intro . $error_str . sprintf(gettext('Hostname Error - The hostname (%1$s) doesn\'t belong to user (%2$s).'), $this->_dnsHost, $this->_dnsUser);
2270
					} else if (preg_match('/200/i', $data)) {
2271
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2272
						$successful_update = true;
2273
					} else {
2274
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2275
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2276
						$this->_debug($data);
2277
					}
2278
					break;
2279
				case 'ods':
2280
					if (preg_match("/299/i", $data)) {
2281
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2282
						$successful_update = true;
2283
					} else {
2284
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2285
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2286
						$this->_debug($data);
2287
					}
2288
					break;
2289
				case 'freedns':
2290
				case 'freedns-v6':
2291
					if (preg_match("/has not changed./i", $data)) {
2292
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2293
						$successful_update = true;
2294
					} else if (preg_match("/Updated/i", $data)) {
2295
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2296
						$successful_update = true;
2297
					} else {
2298
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2299
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2300
						$this->_debug($data);
2301
					}
2302
					break;
2303
				case 'freedns2':
2304
				case 'freedns2-v6':
2305
					if (preg_match("/No IP change detected/i", $data)) {
2306
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2307
						$successful_update = true;
2308
					} else if (preg_match("/Updated/i", $data)) {
2309
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2310
						$successful_update = true;
2311
					} else {
2312
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2313
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2314
						$this->_debug($data);
2315
					}
2316
					break;
2317
				case 'dnsexit':
2318
					if (preg_match("/is the same/i", $data)) {
2319
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2320
						$successful_update = true;
2321
					} else if (preg_match("/Success/i", $data)) {
2322
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2323
						$successful_update = true;
2324
					} else {
2325
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2326
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2327
						$this->_debug($data);
2328
					}
2329
					break;
2330
				case 'loopia':
2331
					if (preg_match("/nochg/i", $data)) {
2332
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2333
						$successful_update = true;
2334
					} else if (preg_match("/good/i", $data)) {
2335
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2336
						$successful_update = true;
2337
					} else if (preg_match('/badauth/i', $data)) {
2338
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2339
					} else {
2340
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2341
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2342
						$this->_debug($data);
2343
					}
2344
					break;
2345
				case 'opendns':
2346
					if (preg_match('/badauth/i', $data)) {
2347
						$status = $status_intro . $error_str . gettext("Not a valid username or password!");
2348
					} else if (preg_match('/nohost/i', $data)) {
2349
						$status = $status_intro . $error_str . gettext("Hostname specified does not exist.");
2350
						$successful_update = true;
2351
					} else if (preg_match('/good/i', $data)) {
2352
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2353
						$successful_update = true;
2354
					} else if (preg_match('/yours/i', $data)) {
2355
						$status = $status_intro . $error_str . gettext("Hostname specified exists, but not under the username specified.");
2356
					} else if (preg_match('/abuse/i', $data)) {
2357
						$status = $status_intro . $error_str . gettext("Updating too frequently, considered abuse.");
2358
					} else {
2359
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2360
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2361
						$this->_debug($data);
2362
					}
2363
					break;
2364
				case 'staticcling':
2365
					if (preg_match("/invalid ip/i", $data)) {
2366
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
2367
					} else if (preg_match('/required info missing/i', $data)) {
2368
						$status = $status_intro . $error_str . gettext("Bad Request - Required parameters were not provided.");
2369
					} else if (preg_match('/invalid characters/i', $data)) {
2370
						$status = $status_intro . $error_str . gettext("Bad Request - Illegal characters in either the username or the password.");
2371
					} else if (preg_match('/bad password/i', $data)) {
2372
						$status = $status_intro . $error_str . gettext("Invalid password.");
2373
					} else if (preg_match('/account locked/i', $data)) {
2374
						$status = $status_intro . $error_str . gettext("This account has been administratively locked.");
2375
					} else if (preg_match('/update too frequent/i', $data)) {
2376
						$status = $status_intro . $error_str . gettext("Updating too frequently.");
2377
					} else if (preg_match('/DB error/i', $data)) {
2378
						$status = $status_intro . $error_str . gettext("Server side error.");
2379
					} else if (preg_match('/success/i', $data)) {
2380
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2381
						$successful_update = true;
2382
					} else {
2383
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2384
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2385
						$this->_debug($data);
2386
					}
2387
					break;
2388
				case 'mythicbeasts':
2389
				case 'mythicbeasts-v6':
2390
					/* https://www.mythic-beasts.com/support/api/dnsv2/tutorial */
2391
					$result = json_decode($data, true);
2392
					if ($result['message'] == '1 records added') {
2393
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2394
						$successful_update = true;
2395
					} else {
2396
						log_error($status_intro . " ( " . gettext("Error message: ") . $result['error'] . " )");
2397
					}
2398
					break;
2399
				case 'name.com':
2400
				case 'name.com-v6':
2401
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2402
					if ($http_code == "200") {
2403
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2404
						$successful_update = true;
2405
					} else {
2406
						$status = $status_intro . "(" . gettext("Error, HTTP response code") . ": " . $http_code . ")";
2407
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2408
					}
2409
					break;
2410
				case 'namecheap':
2411
					$tmp = str_replace("^M", "", $data);
2412
					$ncresponse = @xml2array($tmp);
2413
					if (preg_match("/internal server error/i", $data)) {
2414
						$status = $status_intro . $error_str . gettext("Server side error.");
2415
					} else if (preg_match("/request is badly formed/i", $data)) {
2416
						$status = $status_intro . $error_str . gettext("Badly Formed Request (check the settings).");
2417
					} else if ($ncresponse['interface-response']['ErrCount'] === "0") {
2418
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2419
						$successful_update = true;
2420
					} else if (is_numeric($ncresponse['interface-response']['ErrCount']) && ($ncresponse['interface-response']['ErrCount'] > 0)) {
2421
						$status = $status_intro . $error_str . implode(", ", $ncresponse["interface-response"]["errors"]);
2422
					} else {
2423
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2424
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2425
						$this->_debug($data);
2426
					}
2427
					break;
2428
				case 'nicru':
2429
				case 'nicru-v6':
2430
					$status_intro = "NIC.RU ({$this->_dnsHost}): ";
2431
					if (preg_match('/badauth/i', $data)) {
2432
						$status = $status_intro . gettext("The NIC.RU username or password specified are incorrect.");
2433
					} else if (preg_match('/notfqdn /i', $data)) {
2434
						$status = $status_intro . gettext("The hostname specified is not a fully-qualified domain name.");
2435
					} else if (preg_match('/nohost/i', $data)) {
2436
						$status = $status_intro . gettext("The hostname passed could not be matched to any records configured.");
2437
					} else if (preg_match('/good/i', $data)) {
2438
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2439
						$successful_update = true;
2440
					} else if (preg_match('/dnserr/i', $data)) {
2441
						$status = $status_intro . gettext("DNS error encountered. Stop updating for 30 minutes.");
2442
					} else {
2443
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2444
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
2445
						$this->_debug($data);
2446
					}
2447
					break;
2448
				case 'duiadns':
2449
				case 'duiadns-v6':
2450
					if (preg_match("/error/i", $data)) {
2451
						$status = $status_intro . $error_str . gettext("Server side error.");
2452
					} else if (preg_match('/nohost/i', $data)) {
2453
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
2454
					} else if (preg_match('/badauth/i', $data)) {
2455
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
2456
					} else if (preg_match('/good/i', $data)) {
2457
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2458
						$successful_update = true;
2459
					} else if (preg_match('/nochg/i', $data)) {
2460
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
2461
						$successful_update = true;
2462
					} else {
2463
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2464
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2465
						$this->_debug($data);
2466
					}
2467
					break;
2468
				case 'he-net':
2469
				case 'he-net-v6':
2470
				case 'he-net-tunnelbroker':
2471
					if (preg_match("/badip/i", $data)) {
2472
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
2473
					} else if (preg_match('/nohost/i', $data)) {
2474
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
2475
					} else if (preg_match('/badauth/i', $data)) {
2476
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
2477
					} else if (preg_match('/good/i', $data)) {
2478
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2479
						$successful_update = true;
2480
					} else if (preg_match('/nochg/i', $data)) {
2481
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
2482
						$successful_update = true;
2483
					} else {
2484
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2485
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2486
						$this->_debug($data);
2487
					}
2488
					break;
2489
				case 'selfhost':
2490
					if (preg_match('/notfqdn/i', $data)) {
2491
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2492
					} else if (preg_match('/nochg/i', $data)) {
2493
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
2494
						$successful_update = true;
2495
					} else if (preg_match('/good/i', $data)) {
2496
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2497
						$successful_update = true;
2498
					} else if (preg_match('/noauth/i', $data)) {
2499
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2500
					} else {
2501
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2502
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2503
						$this->_debug($data);
2504
					}
2505
					break;
2506
				case 'strato':
2507
					if (preg_match('/good\s' . $this->_dnsIP . '/i', $data)) {
2508
						$status = $status_intro . $success_str . gettext("IP address updated successfully") . " (" . $this->_dnsIP . ")";
2509
						$successful_update = true;
2510
					} else if (preg_match('/good/i', $data)) {
2511
						$status = $status_intro . $error_str . gettext("IP result did not match");
2512
					} else if (preg_match('/nochg/i', $data)) {
2513
						$status = $status_intro . $success_str . gettext("IP was not changed");
2514
						$successful_update = true;
2515
					} else if (preg_match("/badauth/i", $data)) {
2516
						$status = $status_intro . $error_str . gettext("invalid username or password");
2517
					} else if (preg_match('/nohost/i', $data)) {
2518
						$status = $status_intro . $error_str . gettext("invalid hostname");
2519
					} else if (preg_match('/abuse/i', $data)) {
2520
						$status = $status_intro . $error_str . gettext("too many requests - please wait");
2521
					} else {
2522
						$status = $status_intro . "(" . gettext("unknown response") . ")";
2523
						log_error($status_intro . gettext("PAYLOAD:") . " " . $header . $data);
2524
						$this->_debug($data);
2525
						$this->_debug($header);
2526
					}
2527
					break;
2528
				case 'route53':
2529
				case 'route53-v6':
2530
					if(preg_match('/ErrorResponse/', $data)){
2531
						$status = $status_intro . $error_str . gettext("Route53 API call failed");
2532
						log_error(sprintf("error message: %s", $data));
2533
						$status_update = false;
2534
					} else {
2535
						$status = $status_intro . $success_str . gettext("IP address changed successfully");
2536
						$successful_update = true;
2537
					}
2538
					break;
2539
				case 'cloudflare-v6':
2540
				case 'cloudflare':
2541
					$output = json_decode($data);
2542
					if ($output->result->content === $this->_dnsIP) {
2543
						$status = $status_intro . $success_str . sprintf(gettext('%1$s updated to %2$s'), $this->_dnsHost, $this->_dnsIP);
2544
						$successful_update = true;
2545
					} elseif ($output->errors[0]->code === 9103) {
2546
						$status = $status_intro . $error_str . gettext("Invalid Credentials! Don't forget to use API Key for password field with Cloudflare.");
2547
					} elseif (($output->success) && (!$output->result[0]->id)) {
2548
						$status = $status_intro . $error_str . gettext("Zone or Host ID was not found, check the hostname.");
2549
					} else {
2550
						$status = $status_intro . gettext("UNKNOWN ERROR") . " - " . $output->errors[0]->message;
2551
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2552
					}
2553
					break;
2554
				case 'yandex-v6':
2555
				case 'yandex':
2556
					$result = json_decode($data, true);
2557
					if ($result['success'] == 'ok') {
2558
						$successful_update = true;
2559
					} elseif ($result['error'] == 'bad_domain') {
2560
						log_error($status_intro . $error_str .  gettext("The domain name was not specified or does not conform to the RFC"));
2561
					} elseif ($result['error'] == 'unknown') {
2562
						log_error($status_intro . $error_str .  gettext("A temporary failure or API error occurred"));
2563
					} elseif ($result['error'] == 'prohibited') {
2564
						log_error($status_intro . $error_str .  gettext("A forbidden domain name"));
2565
					} elseif ($result['error'] == 'bad_token') {
2566
						log_error($status_intro . $error_str .  gettext("Invalid PDD token"));
2567
					} elseif ($result['error'] == 'no_auth') {
2568
						log_error($status_intro . $error_str .  gettext("The PddToken header was omitted"));
2569
					} elseif ($result['error'] == 'not_allowed') {
2570
						log_error($status_intro . $error_str .  gettext("This operation is not allowed for this user"));
2571
					} elseif ($result['error'] == 'blocked') {
2572
						log_error($status_intro . $error_str .  gettext("Blocked domain"));
2573
					} elseif ($result['error'] == 'occupied') {
2574
						log_error($status_intro . $error_str .  gettext("The domain name is in use by another user"));
2575
					} elseif ($result['error'] == 'domain_limit_reached') {
2576
						log_error($status_intro . $error_str .  gettext("Exceeded the acceptable number of connected domains (50)"));
2577
					} elseif ($result['error'] == 'no_reply') {
2578
						log_error($status_intro . $error_str .  gettext("Yandex.Mail for Domain cannot connect to the server source for the import"));
2579
					} else {
2580
						log_error($status_intro . $error_str .  gettext("PAYLOAD:") . " " . $data);
2581
					}
2582
					break;
2583
				case 'eurodns':
2584
					if (preg_match('/notfqdn/i', $data)) {
2585
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2586
					} else if (preg_match('/nochg/i', $data)) {
2587
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2588
						$successful_update = true;
2589
					} else if (preg_match('/good/i', $data)) {
2590
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2591
						$successful_update = true;
2592
					} else if (preg_match('/badauth/i', $data)) {
2593
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2594
					} else {
2595
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2596
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2597
						$this->_debug($data);
2598
					}
2599
					break;
2600
				case 'gratisdns':
2601
					if (preg_match('/Forkerte værdier/i', $data)) {
2602
						$status = $status_intro . $error_str . gettext("Wrong values - Update could not be completed.");
2603
					} else if (preg_match('/Bruger login: Bruger eksistere ikke/i', $data)) {
2604
						$status = $status_intro . $error_str . gettext("Unknown username - User does not exist.");
2605
					} else if (preg_match('/Bruger login: 1Fejl i kodeord/i', $data)) {
2606
						$status = $status_intro . $error_str . gettext("Wrong password - Remember password is case sensitive.");
2607
					} else if (preg_match('/Domæne kan IKKE administreres af bruger/i', $data)) {
2608
						$status = $status_intro . $error_str . gettext("User unable to administer the selected domain.");
2609
					} else if (preg_match('/OK/i', $data)) {
2610
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2611
						$successful_update = true;
2612
					} else {
2613
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2614
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2615
						$this->_debug($data);
2616
					}
2617
					break;
2618
				case 'digitalocean':
2619
				case 'digitalocean-v6':
2620
					// Creating new records returns an HTTP 201, updating existing records get 200
2621
					// https://redmine.pfsense.org/issues/9171
2622
					if (preg_match("/HTTP\/2\s20[0,1]/i", $header)) {
2623
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2624
						$successful_update = true;
2625
					} else {
2626
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2627
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2628
						$this->_debug($data);
2629
					}
2630
					break;
2631
				case 'dnsimple':
2632
					/* Responds with HTTP 200 on success.
2633
					   Responds with HTTP 4xx on error.
2634
					   Returns JSON data as body */
2635
;
2636
                                        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2637

    
2638
					if ($code == "200") {
2639
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2640
						$successful_update = true;
2641
					} else if (preg_match("/4\d\d/i", $code)) {
2642
						$arrbody = json_decode($data, true);
2643
						$message = $arrbody['message'] . ".";
2644
						if (isset($arrbody['errors']['content'])) {
2645
							foreach ($arrbody['errors']['content'] as $key => $content) {
2646
								$message .= " " . $content . ".";
2647
							}
2648
						}
2649
						$status = $status_intro . $error_str . $message;
2650
					} else {
2651
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $code . ")";
2652
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2653
						$this->_debug($data);
2654
					}
2655
					break;
2656
				case 'godaddy':
2657
				case 'godaddy-v6':
2658
					/* Responds with HTTP 200 on success.
2659
					   Responds with HTTP 4xx or  on error.
2660
					   Returns JSON data as body */
2661
;
2662
					if (preg_match("/\s200\sOK/i", $header)) {
2663
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2664
						$successful_update = true;
2665
					} else if (preg_match("/\s4\d\d\s/i", $header)) {
2666
						$arrbody = json_decode($data, true);
2667
						$message = $arrbody['message'] . ".";
2668
						if (isset($arrbody['fields'])) {
2669
							foreach ($arrbody['fields'] as $error) {
2670
								$message .= " " . $error['path'] . ": " . $error['message'] . ".";
2671
							}
2672
						}
2673
						$status = $status_intro . $error_str . $message;
2674
					} else {
2675
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2676
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2677
						$this->_debug($data);
2678
					}
2679
					break;
2680
				case 'googledomains':
2681
					if (preg_match('/notfqdn/i', $data)) {
2682
						$status = $status_intro . $error_str . gettext("Not A FQDN");
2683
					} else if (preg_match('/nochg/i', $data)) {
2684
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2685
						$successful_update = true;
2686
					} else if (preg_match('/good/i', $data)) {
2687
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2688
						$successful_update = true;
2689
					} else if (preg_match('/badauth/i', $data)) {
2690
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2691
					} else if (preg_match('/nohost/i', $data)) {
2692
						$status = $status_intro . $error_str . gettext("Hostname does not exist or DynDNS not enabled");
2693
					} else if (preg_match('/badagent/i', $data)) {
2694
						$status = $status_intro . $error_str . gettext("Bad request");
2695
					} else if (preg_match('/abuse/i', $data)) {
2696
						$status = $status_intro . $error_str . gettext("Dynamic DNS access has been blocked!");
2697
					} else if (preg_match('/911/i', $data)) {
2698
						$status = $status_intro . $error_str . gettext("Error on Google's end, retry in 5 minutes");
2699
					} else {
2700
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2701
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2702
						$this->_debug($data);
2703
					}
2704
					break;
2705
				case 'dnsmadeeasy':
2706
					switch ($data) {
2707
						case 'success':
2708
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2709
							$successful_update = true;
2710
							break;
2711
						case 'error-auth':
2712
							$status = $status_intro . $error_str . gettext("Invalid username or password");
2713
							break;
2714
						case 'error-auth-suspend':
2715
							$status = $status_intro . $error_str . gettext("Account suspended");
2716
							break;
2717
						case 'error-auth-voided':
2718
							$status = $status_intro . $error_str . gettext("Account revoked");
2719
							break;
2720
						case 'error-record-invalid':
2721
							$status = $status_intro . $error_str . gettext("Record does not exist in the system. Unable to update record");
2722
							break;
2723
						case 'error-record-auth':
2724
							$status = $status_intro . $error_str . gettext("User does not have access to this record");
2725
							break;
2726
						case 'error-record-ip-same':
2727
							$status = $status_intro . $success_str . gettext("No Change In IP Address");
2728
							$successful_update = true;
2729
							break;
2730
						case 'error-system':
2731
							$status = $status_intro . $error_str . gettext("General system error recognized by the system");
2732
							break;
2733
						case 'error':
2734
							$status = $status_intro . $error_str . gettext("General system error unrecognized by the system");
2735
							break;
2736
						default:
2737
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2738
							log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2739
							$this->_debug($data);
2740
							break;
2741
					}
2742
					break;
2743
				case 'spdyn':
2744
				case 'spdyn-v6':
2745
					if (preg_match('/notfqdn/i', $data)) {
2746
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2747
					} else if (preg_match('/nohost/i', $data)) {
2748
						$status = $status_intro . $error_str . gettext("No such host");
2749
					} else if (preg_match('/nochg/i', $data)) {
2750
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2751
						$successful_update = true;
2752
					} else if (preg_match('/good/i', $data)) {
2753
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2754
						$successful_update = true;
2755
					} else if (preg_match('/badauth/i', $data)) {
2756
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2757
					} else {
2758
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2759
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2760
						$this->_debug($data);
2761
					}
2762
					break;
2763
				case 'all-inkl':
2764
 					if (preg_match('/good\s' . $this->_dnsIP . '/i', $data)) {
2765
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2766
 							$successful_update = true;
2767
 					} else if (preg_match('/good/i', $data)) {
2768
						$status = $status_intro . $error_str . gettext("Result did not match.");
2769
					} else if (preg_match("/\s401\sUnauthorized/i", $header)) {
2770
						$status = $status_intro . $error_str . gettext("Invalid username or password");
2771
					}
2772
					else {
2773
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2774
							log_error($status_intro . gettext("PAYLOAD:") . " " . $header . $data);
2775
 							$this->_debug($data);
2776
 							$this->_debug($header);
2777
 					}
2778
 					break;
2779
				case 'hover':
2780
					if (preg_match('/succeeded":true/i', $data)) {
2781
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2782
						$successful_update = true;
2783
					} else {
2784
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2785
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2786
						$this->_debug($data);
2787
					}
2788
					break;
2789
				case 'cloudns':
2790
					$result = json_decode($data, true);
2791
					if ($result['status'] == 'Success') {
2792
						$successful_update = true;
2793
					} else {
2794
						log_error($result['status'] . "(" . $result['statusDescription'] . ")");
2795
					}
2796
					break;
2797
				case 'dreamhost':
2798
				case 'dreamhost-v6':
2799
					$result = json_decode($data,true);
2800
					if ($this->_dnsVerboseLog) {
2801
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2802
					}
2803
					switch ($result['data']) {
2804
					case 'success':
2805
					case 'record_added':
2806
					case 'record_removed':
2807
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2808
						$successful_update = true;
2809
						break;
2810
					case 'no_record':
2811
					case 'no_such_record ':
2812
						$status = $status_intro . $error_str . gettext("No record exists.");
2813
						break;
2814
					case 'no_type':
2815
					case 'no_such_type ':
2816
						$status = $status_intro . $error_str . gettext("No type exists.");
2817
						break;
2818
					case 'no_value':
2819
					case 'no_such_value ':
2820
						$status = $status_intro . $error_str . gettext("No value exists.");
2821
						break;
2822
					case 'no_such_zone':
2823
						$status = $status_intro . $error_str . gettext("No such zone exists.");
2824
						break;
2825
					case 'invalid_record':
2826
						$status = $status_intro . $error_str . gettext("The specified record is invalid.");
2827
						break;
2828
					case 'invalid_type':
2829
						$status = $status_intro . $error_str . gettext("The specified type is invalid.");
2830
						break;
2831
					case 'invalid_value':
2832
						$status = $status_intro . $error_str . gettext("The specified value is invalid.");
2833
						break;
2834
					case 'not_editable ':
2835
						$status = $status_intro . $error_str . gettext("Record is not editable.");
2836
						break;
2837
					case 'record_already_exists_not_editable':
2838
						$status = $status_intro . $error_str . gettext("Record exists but is not editable.");
2839
						break;
2840
					case 'record_already_exists_remove_first':
2841
						$status = $status_intro . $error_str . gettext("Record exists and must be removed before adding.");
2842
						break;
2843
					case 'internal_error_updating_zone':
2844
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2845
						break;
2846
					case 'internal_error_could_not_load_zone':
2847
						$status = $status_intro . $error_str . gettext("A remote server error occurred loading the zone.");
2848
						break;
2849
					case 'internal_error_could_not_update_zone':
2850
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2851
						break;
2852
					case 'internal_error_could_not_add_record':
2853
						$status = $status_intro . $error_str . gettext("A remote server error occurred adding a new record.");
2854
						break;
2855
					case 'internal_error_could_not_destroy_record ':
2856
						$status = $status_intro . $error_str . gettext("A remote server error occurred removing an existing record.");
2857
						break;
2858
					default:
2859
						break;
2860
					}
2861
				case 'azure':
2862
				case 'azurev6':
2863
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2864
					if ($http_code == 401) {
2865
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2866
					} else if ($http_code == 201) {
2867
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2868
						$successful_update = true;
2869
					} else if ($http_code == 200) {
2870
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2871
						$successful_update = true;
2872
					} else {
2873
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2874
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2875
						$this->_debug($data);
2876
					}
2877
					break;
2878
				case 'linode':
2879
				case 'linode-v6':
2880
					$status_intro = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): ";
2881
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2882
					$result = json_decode($data,true);
2883
					if ($this->_dnsVerboseLog) {
2884
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2885
					}
2886
					if ($http_code == 200 && isset($result["id"]) && ! isset($result["errors"])) {
2887
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2888
						$successful_update = true;
2889
					} else if ( $http_code == 401 && preg_match('/not authorized to use/i', $data) ) {
2890
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2891
					} else {
2892
						$status = $status_intro . $error_str .
2893
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" );
2894
					}
2895
					break;
2896
				case 'gandi-livedns':
2897
				case 'gandi-livedns-v6':
2898
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2899
					if ($http_code == 401) {
2900
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2901
					} else if ($http_code == 200 || $http_code == 201) {
2902
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2903
						$successful_update = true;
2904
					} else {
2905
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2906
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2907
						$this->_debug($data);
2908
					}
2909
					break;
2910
				case 'desec':
2911
				case 'desec-v6':
2912
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2913
					switch ($http_code) {
2914
						case '200':
2915
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2916
							$successful_update = true;
2917
							break;
2918
						case '401':
2919
							$status = $status_intro . $error_str . gettext("User Authorization Failed");
2920
							break;
2921
						default:
2922
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2923
							log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2924
							$this->_debug($data);
2925
							break;
2926
					}
2927
					break;
2928
				default:
2929
					break;
2930
			}
2931

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

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

    
3003
		/*
3004
		 * Private Function (added 12 July 05) [beta]
3005
		 *   - Detect whether or not IP needs to be updated.
3006
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
3007
		 *      | work with other systems. pfSense base is FreeBSD.
3008
		 */
3009
		function _detectChange() {
3010
			global $debug;
3011

    
3012
			if ($debug) {
3013
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
3014
			}
3015

    
3016
			$currentTime = time();
3017

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

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

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

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

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

    
3092
			return false;
3093
		}
3094

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

    
3104
			if (!$g['debug']) {
3105
				return;
3106
			}
3107
			$string = date('m-d-y h:i:s') . ' - (' . $this->_debugID . ') - [' . $this->_dnsService . '] - ' . $data . "\n";
3108
			$file = fopen($this->_debugFile, 'a');
3109
			fwrite($file, $string);
3110
			fclose($file);
3111
		}
3112
		function _checkIP() {
3113
			global $debug;
3114

    
3115
			if ($debug) {
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
?>
(15-15/61)