Project

General

Profile

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

    
24
	/*
25
	 * PHP.updateDNS (pfSense version)
26
	 *
27
	 * +====================================================+
28
	 *  Services Supported:
29
	 *    - 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
	 *    - Namecheap (namecheap.com)
70
	 *    - No-IP (no-ip.com)
71
	 *    - OpenDNS (opendns.com)
72
	 *    - SelfHost (selfhost.de)
73
	 *    - SPDYN (spdyn.de)
74
	 *    - SPDYN IPv6 (spdyn.de)
75
	 *    - StaticCling (staticcling.org)
76
	 *    - Strato (www.strato.de)
77
	 *    - ZoneEdit (zoneedit.com)
78
	 * +----------------------------------------------------+
79
	 *  Requirements:
80
	 *    - PHP version 4.0.2 or higher with the CURL Library and the PCRE Library
81
	 * +----------------------------------------------------+
82
	 *  Public Functions
83
	 *    - updatedns()
84
	 *
85
	 *  Private Functions
86
	 *    - _update()
87
	 *    - _checkStatus()
88
	 *    - _error()
89
	 *    - _detectChange()
90
	 *    - _debug()
91
	 *    - _checkIP()
92
	 * +----------------------------------------------------+
93
	 *  All-Inkl        - Last Tested: 12 November 2016
94
	 *  Amazon Route 53 - Last Tested: 04 February 2017
95
	 *  Azure DNS       - Last Tested: 08 March 2018
96
	 *  City Network    - Last Tested: 13 November 2013
97
	 *  Cloudflare      - Last Tested: 05 September 2016
98
	 *  Cloudflare IPv6 - Last Tested: 17 July 2016
99
	 *  ClouDNS         - Last Tested: 22 August 2017
100
	 *  deSEC           - Last Tested: NEVER
101
	 *  deSEC IPv6      - Last Tested: NEVER
102
	 *  DHS             - Last Tested: 12 July 2005
103
	 *  DigitalOcean    - Not Yet Tested
104
	 *  DNS Made Easy   - Last Tested: 27 April 2015
105
	 *  DNS-O-Matic     - Last Tested: 9 September 2010
106
	 *  DNSexit         - Last Tested: 20 July 2008
107
	 *  DNSimple        - Last Tested: 09 February 2015
108
	 *  DreamHost       - Last Tested: 30 April 2017
109
	 *  DreamHost IPv6  - Not Yet Tested
110
	 *  DuiaDNS         - Last Tested: 25 November 2016
111
	 *  DuiaDNS IPv6    - Last Tested: 25 November 2016
112
	 *  DY.fi           - Last Tested: 22 April 2021
113
	 *  DynDNS Custom   - Last Tested: NEVER
114
	 *  DynDNS Dynamic  - Last Tested: 12 July 2005
115
	 *  DynDNS Static   - Last Tested: NEVER
116
	 *  Dyns            - Last Tested: NEVER
117
	 *  EasyDNS         - Last Tested: 20 July 2008
118
	 *  Eurodns         - Last Tested: 27 June 2013
119
	 *  FreeDNS         - Last Tested: 01 May 2016
120
	 *  FreeDNS IPv6    - Last Tested: 01 May 2016
121
	 *  FreeDNS IPv6 v2 - Last Tested: 01 June 2020
122
	 *  FreeDNS v2      - Last Tested: 01 June 2020
123
	 *  Gandi LiveDNS   - Not Yet Tested
124
	 *  GleSYS          - Last Tested: 3 February 2015
125
	 *  GoDaddy         - Last Tested: 22 November 2017
126
	 *  GoDaddy IPv6    - Last Tested: 22 November 2017
127
	 *  Google Domains  - Last Tested: 27 April 2015
128
	 *  GratisDNS       - Last Tested: 15 August 2012
129
	 *  HE.net          - Last Tested: 7 July 2013
130
	 *  HE.net IPv6     - Last Tested: 7 July 2013
131
	 *  HE.net Tunnel   - Last Tested: 28 June 2011
132
	 *  HN.org          - Last Tested: 12 July 2005
133
	 *  Hover           - Last Tested: 15 February 2017
134
	 *  Loopia          - Last Tested: 21 August 2019
135
	 *  Namecheap       - Last Tested: 31 August 2010
136
	 *  No-IP           - Last Tested: 20 July 2008
137
	 *  ODS             - Last Tested: 02 August 2005
138
	 *  OpenDNS         - Last Tested: 4 August 2008
139
	 *  OVH DynHOST     - Last Tested: NEVER
140
	 *  SelfHost        - Last Tested: 26 December 2011
141
	 *  SPDYN           - Last Tested: 02 July 2016
142
	 *  SPDYN IPv6      - Last Tested: 02 July 2016
143
	 *  StaticCling     - Last Tested: 27 April 2006
144
	 *  Strato          - Last Tested: 29 May 2021
145
	 *  ZoneEdit        - Last Tested: NEVER
146
	 * +====================================================+
147
	 *
148
	 * @author 	E.Kristensen
149
	 * @link    	http://www.idylldesigns.com/projects/phpdns/
150
	 * @version 	0.8
151
	 * @updated	13 October 05 at 21:02:42 GMT
152
	 *
153
	 * DNSexit/OpenDNS support and multiwan extension for pfSense by Ermal Luçi
154
	 * Custom DNS support by Matt Corallo
155
	 *
156
	 */
157

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

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

    
212
			global $config, $g, $dyndns_split_domain_types;
213
			if (in_array($dnsService, $dyndns_split_domain_types)) {
214
				$this->_FQDN = $dnsHost . "." . $dnsDomain;
215
			} else {
216
				$this->_FQDN = $dnsHost;
217
			}
218

    
219
			$this->_cacheFile = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.cache";
220
			$this->_cacheFile_v6 = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}_v6.cache";
221
			$this->_debugFile = "{$g['varetc_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.debug";
222

    
223
			$this->_curlIpresolveV4 = $curlIpresolveV4;
224
			$this->_curlSslVerifypeer = $curlSslVerifypeer;
225
			$this->_curlProxy = $curlProxy;
226
			$this->_dnsVerboseLog = $dnsVerboseLog;
227
			if ($this->_dnsVerboseLog) {
228
				log_error(gettext("Dynamic DNS: updatedns() starting"));
229
			}
230

    
231
			$dyndnslck = lock("DDNS" . $dnsID, LOCK_EX);
232

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

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

    
376
			// Ensure that we were able to lookup the IP
377
			if (!is_ipaddr($this->_dnsIP)) {
378
				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));
379
				unlock($dyndnslck);
380
				return;
381
			}
382

    
383
			$this->_debugID = rand(1000000, 9999999);
384

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

    
489
			unlock($dyndnslck);
490
		}
491

    
492
		/*
493
		 * Private Function (added 12 July 05) [beta]
494
		 *   Send Update To Selected Service.
495
		 */
496
		function _update() {
497

    
498
			if ($this->_dnsVerboseLog) {
499
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
500
			}
501

    
502
			if (strstr($this->_dnsRequestIf, "_vip")) {
503
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
504
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
505
			} else {
506
				$realparentif = $this->_dnsRequestIf;
507
			}
508

    
509
			$ch = curl_init();
510

    
511
			if ($this->_useIPv6 == false) {
512
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
513
			}
514

    
515
			if ($this->_dnsService != 'ods') {
516
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
517
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
518
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
519
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
520
			}
521

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

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

    
811
				case 'staticcling':
812
					$needsIP = FALSE;
813
					curl_setopt($ch, CURLOPT_URL, 'https://www.staticcling.org/update.html?login=' . $this->_dnsUser . '&pass=' . $this->_dnsPass);
814
					break;
815
				case 'dnsomatic':
816
					/* Example syntax
817
						https://username:password@updates.dnsomatic.com/nic/update?hostname=yourhostname&myip=ipaddress&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG
818
					*/
819
					$needsIP = FALSE;
820
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
821
						$this->_dnsWildcard = "ON";
822
					}
823
					/*
824
					Reference: https://www.dnsomatic.com/wiki/api
825
						DNS-O-Matic usernames are 3-25 characters.
826
						DNS-O-Matic passwords are 6-20 characters.
827
						All ASCII letters and numbers accepted.
828
						Dots, dashes, and underscores allowed, but not at the beginning or end of the string.
829
					Required: "rawurlencode" http://www.php.net/manual/en/function.rawurlencode.php
830
						Encodes the given string according to RFC 3986.
831
					*/
832
					$server = "https://" . rawurlencode($this->_dnsUser) . ":" . rawurlencode($this->_dnsPass) . "@updates.dnsomatic.com/nic/update?hostname=";
833
					if ($this->_dnsServer) {
834
						$server = $this->_dnsServer;
835
					}
836
					if ($this->_dnsPort) {
837
						$port = ":" . $this->_dnsPort;
838
					}
839
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NOCHG');
840
					break;
841
				case 'domeneshop':
842
				case 'domeneshop-v6':
843
					/* Example:
844
						https://{token}:{secret}@api.domeneshop.no/v0/dyndns/update?hostname=example.com&myip=127.0.0.1
845
					*/
846
					$needsIP = FALSE;
847
					$server = "https://{$this->_dnsUser}:{$this->_dnsPass}@api.domeneshop.no/v0/dyndns/update?hostname={$this->_dnsHost}&myip={$this->_dnsIP}";
848
					curl_setopt($ch, CURLOPT_URL, $server);
849
					break;
850
				case 'mythicbeasts':
851
				case 'mythicbeasts-v6':
852
					/* Example:
853
						https://{login}:{password}@https://api.mythic-beasts.com/dns/v2/zones/example.com/records/test1/A -d data=1.2.3.4
854
					*/
855
					$needsIP = FALSE;
856
					if ($this->_useIPv6) {
857
						$record = "AAAA";
858
					} else {
859
						$record = "A";
860
					}
861
					$post_data['data'] = $this->_dnsIP;
862
					$server = "https://api.mythic-beasts.com/dns/v2/zones/{$this->_dnsDomain}/records/{$this->_dnsHost}/{$record}";
863
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
864
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
865
					curl_setopt($ch, CURLOPT_URL, $server);
866
					break;
867
				case 'namecheap':
868
					/* Example:
869
						https://dynamicdns.park-your-domain.com/update?host=[host_name]&domain=[domain.com]&password=[domain_password]&ip=[your_ip]
870
					*/
871
					$needsIP = FALSE;
872
					$dnspass = trim($this->_dnsPass);
873
					$server = "https://dynamicdns.park-your-domain.com/update?host={$this->_dnsHost}&domain={$this->_dnsDomain}&password={$dnspass}&ip={$this->_dnsIP}";
874
					curl_setopt($ch, CURLOPT_URL, $server);
875
					break;
876
				case 'nicru':
877
				case 'nicru-v6':
878
					/* see https://www.nic.ru/help/dynamic-dns-for-developers_5810.html */
879
					$needsIP = FALSE;
880
					if (is_ipaddrv6($this->_dnsIP)) {
881
						$iptype = "ipv6";
882
					} else {
883
						$iptype = "myip";
884
					}
885
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
886
					$server = "https://api.nic.ru/dyndns/update?hostname={$this->_dnsHost}&{$iptype}={$this->_dnsIP}";
887
					curl_setopt($ch, CURLOPT_URL, $server);
888
					break;
889
				case 'yandex':
890
				case 'yandex-v6':
891
					// https://yandex.com/dev/connect/directory/api/concepts/domains/dns-records-via-pdd.html
892

    
893
					if (is_ipaddrv4($this->_dnsIP)) {
894
						$type = 'A';
895
					} else {
896
						$type = 'AAAA';
897
					}
898

    
899
					// get record_id
900
					curl_setopt($ch, CURLOPT_URL, "https://pddimp.yandex.ru/api2/admin/dns/list?domain={$this->_dnsDomain}");
901
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('PddToken: ' . $this->_dnsPass));
902
					$output = json_decode(curl_exec($ch), true);
903
					if (is_array($output["records"])) {
904
						foreach($output["records"] as $record) {
905
							if (($record["domain"] == $this->_dnsDomain) &&
906
							    ($record["subdomain"] == $this->_dnsHost) &&
907
							    ($record["type"] == $type)) {
908
								$record_id = $record["record_id"];
909
							}
910
						}
911
					}
912

    
913
					if ($record_id) {
914
						$action = 'edit';
915
						$post_data['record_id'] = $record_id;
916
					} else {
917
						$action = 'add';
918
					}
919

    
920
					$post_data['domain'] = $this->_dnsDomain;
921
					$post_data['subdomain'] = $this->_dnsHost;
922
					$post_data['content'] = $this->_dnsIP;
923
					$post_data['type'] = $type;
924
					if ($this->_dnsTTL) {
925
						$post_data['ttl'] = $this->_dnsTTL;
926
					}
927

    
928
					curl_setopt($ch, CURLOPT_URL, 'https://pddimp.yandex.ru/api2/admin/dns/' . $action);
929
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('PddToken: ' . $this->_dnsPass));
930
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
931
					break;
932
				case 'duiadns':
933
				case 'duiadns-v6':
934
					$needsIP = FALSE;
935
					$server = "https://ipv4.duiadns.net/dyndns.duia?";
936
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
937
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
938
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
939
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
940
					break;
941
				case 'he-net':
942
				case 'he-net-v6':
943
					$needsIP = FALSE;
944
					$server = "https://dyn.dns.he.net/nic/update?";
945
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
946
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
947
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&password=' . $this->_dnsPass . '&myip=' . $this->_dnsIP);
948
					break;
949
				case 'he-net-tunnelbroker':
950
					$needsIP = FALSE;
951
					$server = "https://ipv4.tunnelbroker.net/nic/update?";
952
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
953
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
954
					break;
955
				case 'selfhost':
956
					$needsIP = FALSE;
957
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
958
						$this->_dnsWildcard = "ON";
959
					}
960
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
961
					$server = "https://carol.selfhost.de/nic/update";
962
					$port = "";
963
					if ($this->_dnsServer) {
964
						$server = $this->_dnsServer;
965
					}
966
					if ($this->_dnsPort) {
967
						$port = ":" . $this->_dnsPort;
968
					}
969
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
970
					break;
971
				case 'strato':
972
					$needsIP = FALSE;
973
					$server = 'https://dyndns.strato.com/nic/update';
974
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
975
					curl_setopt($ch, CURLOPT_URL, $server . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
976
					break;
977
				case 'route53':
978
					require_once("r53.class");
979
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
980
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
981
					$xmlreq = $r53->getRequestBody($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
982
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
983
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
984
					if($this->_dnsVerboseLog){
985
						log_error(sprintf("Sending request to: %s", $apiurl));
986
						foreach($httphead as $hv){
987
							log_error(sprintf("Header: %s", $hv));
988
						}
989
						log_error(sprintf("XMLPOST: %s", $xmlreq));
990
					}
991
					curl_setopt($ch, CURLOPT_URL, $apiurl);
992
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
993
					break;
994
				case 'route53-v6':
995
					require_once("r53.class");
996
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
997
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
998
					$xmlreq = $r53->getRequestBodyV6($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
999
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
1000
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
1001
					if($this->_dnsVerboseLog){
1002
						log_error(sprintf("Sending request to: %s", $apiurl));
1003
						foreach($httphead as $hv){
1004
							log_error(sprintf("Header: %s", $hv));
1005
						}
1006
						log_error(sprintf("XMLPOST: %s", $xmlreq));
1007
					}
1008
					curl_setopt($ch, CURLOPT_URL, $apiurl);
1009
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
1010
					break;
1011
				case 'cloudflare-v6':
1012
				case 'cloudflare':
1013
					$this->_FQDN = ltrim($this->_FQDN, '@.');
1014
					$isv6 = ($this->_dnsService === 'cloudflare-v6');
1015
					$recordType = $isv6 ? "AAAA" : "A";
1016
					$needsIP = TRUE;
1017
					$dnsServer ='api.cloudflare.com';
1018
					$dnsHost = str_replace(' ', '', $this->_dnsHost);
1019

    
1020
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1021

    
1022
					if ((!$this->_dnsUser) || (strpos($this->_dnsUser, '@') !== false)) {
1023
						if (strpos($this->_dnsUser, '@') !== false) {
1024
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1025
										'X-Auth-Email: ' . $this->_dnsUser,
1026
										'X-Auth-Key: ' . $this->_dnsPass,
1027
										'Content-Type: application/json'
1028
										));
1029
						} else {
1030
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1031
										'Authorization: Bearer ' . $this->_dnsPass,
1032
										'Content-Type: application/json'
1033
										));
1034
						}
1035

    
1036
						// Get zone ID
1037
						$getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$this->_dnsDomain}";
1038
						curl_setopt($ch, CURLOPT_URL, $getZoneId);
1039
						$output = json_decode(curl_exec($ch));
1040
						$zone = $output->result[0]->id;
1041
					} else {
1042
						curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1043
							'Authorization: Bearer ' . $this->_dnsPass,
1044
							'Content-Type: application/json'
1045
						));
1046

    
1047
						$zone = $this->_dnsUser;
1048
					}
1049

    
1050
					if ($zone) { // If zone ID was found get host ID
1051
						$getHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records?name={$this->_FQDN}&type={$recordType}";
1052
						curl_setopt($ch, CURLOPT_URL, $getHostId);
1053
						$output = json_decode(curl_exec($ch));
1054
						$host = $output->result[0]->id;
1055
						if ($host) { // If host ID was found update host
1056
							$hostData = array(
1057
								"content" => "{$this->_dnsIP}",
1058
								"type" => "{$recordType}",
1059
								"proxied" => $this->_dnsProxied,
1060
								"name" => "{$this->_dnsHost}",
1061
								"ttl" => empty($this->_dnsTTL) ? 1 : (int) $this->_dnsTTL
1062
							);
1063
							$data_json = json_encode($hostData);
1064
							$updateHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records/{$host}";
1065
							curl_setopt($ch, CURLOPT_URL, $updateHostId);
1066
							curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1067
							curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
1068
						}
1069
					}
1070
					break;
1071
				case 'eurodns':
1072
					$needsIP = TRUE;
1073
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1074
					$server = "https://update.eurodyndns.org/update/";
1075
					$port = "";
1076
					if ($this->_dnsPort) {
1077
						$port = ":" . $this->_dnsPort;
1078
					}
1079
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1080
					break;
1081
				case 'gratisdns':
1082
					$needsIP = TRUE;
1083
					$server = "https://ssl.gratisdns.dk/ddns.phtml";
1084
					curl_setopt($ch, CURLOPT_URL, $server . '?u=' . $this->_dnsUser . '&p=' . $this->_dnsPass . '&h=' . $this->_dnsHost . '&d=' . $this->_dnsDomain . '&i=' . $this->_dnsIP);
1085
					break;
1086
				case 'ovh-dynhost':
1087
					$needsIP = FALSE;
1088
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
1089
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1090
					$server = "https://www.ovh.com/nic/update";
1091
					$port = "";
1092
					if ($this->_dnsServer) {
1093
						$server = $this->_dnsServer;
1094
					}
1095
					if ($this->_dnsPort) {
1096
						$port = ":" . $this->_dnsPort;
1097
					}
1098
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
1099
					break;
1100
				case 'citynetwork':
1101
					$needsIP = TRUE;
1102
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1103
					$server = 'https://dyndns.citynetwork.se/nic/update';
1104
					$port = "";
1105
					if ($this->_dnsServer) {
1106
						$server = $this->_dnsServer;
1107
					}
1108
					if ($this->_dnsPort) {
1109
						$port = ":" . $this->_dnsPort;
1110
					}
1111
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1112
					break;
1113
				case 'dnsimple':
1114
					/* Uses DNSimple's v2 REST API
1115
					   Requires the Account ID as the username (found in the URL when pull up the domain)
1116
					   And an API Token for the password (generated in the User Settings -> API tokens area of the website)
1117
					   Piggybacks on Route 53's ZoneID field for the DNSimple record ID to update
1118
					   The DNS record MUST exist before it can update since it performs a PATCH operation
1119
					   Data sent as JSON over HTTPS */
1120
					$needsIP = TRUE;
1121
					$server = 'https://api.dnsimple.com/v2/';
1122
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
1123
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-Type: application/json', 'Authorization: Bearer ' . $this->_dnsPass));
1124
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsUser . '/zones/' . $this->_dnsHost . '/records/' . $this->_dnsZoneID);
1125
					curl_setopt($ch, CURLOPT_POSTFIELDS, '{"content":"' . $this->_dnsIP . '","ttl":"' . $this->_dnsTTL . '"}');
1126
					break;
1127
				case 'godaddy':
1128
				case 'godaddy-v6':
1129
					/* Uses GoDaddy's REST API
1130
					   Requires username and Account API sso-key passed in header
1131
					   Data sent as JSON */
1132
					$needsIP = TRUE;
1133
					$server = 'https://api.godaddy.com/v1/domains/';
1134
					$recordType = $this->_useIPv6 ? "AAAA" : "A";
1135
					$url = $server . $this->_dnsDomain . '/records/' . $recordType . '/' . $this->_dnsHost;
1136
					$jsondata = '[{"data":"' . $this->_dnsIP . '","ttl":' . $this->_dnsTTL . '}]';
1137
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1138
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1139
						'Accept: application/json',
1140
						'Content-Type: application/json',
1141
						'Authorization: sso-key ' . $this->_dnsUser . ':' . $this->_dnsPass
1142
					));
1143
					curl_setopt($ch, CURLOPT_URL, $url);
1144
					curl_setopt($ch, CURLOPT_POSTFIELDS, $jsondata);
1145
					break;
1146
				case 'googledomains':
1147
					$needsIP = FALSE;
1148
					$post_data['username:password'] = $this->_dnsUser . ':' . $this->_dnsPass;
1149
					$post_data['hostname'] = $this->_dnsHost;
1150
					$post_data['myip'] = $this->_dnsIP;
1151
					$post_data['offline'] = 'no';
1152
					$server = "https://domains.google.com/nic/update";
1153
					$port = "";
1154
					curl_setopt($ch, CURLOPT_URL, 'https://domains.google.com/nic/update');
1155
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1156
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1157
					break;
1158
				case 'dnsmadeeasy':
1159
					$needsIP = TRUE;
1160
					$server = "https://cp.dnsmadeeasy.com/servlet/updateip";
1161
					$username = empty($this->_dnsUser) ? "" : "&username={$this->dnsUser}";
1162
					curl_setopt($ch, CURLOPT_URL, $server . '?password=' . $this->_dnsPass . '&id=' . $this->_dnsHost . '&ip=' . $this->_dnsIP . $username);
1163
					break;
1164
				case 'spdyn':
1165
				case 'spdyn-v6':
1166
					$needsIP = FALSE;
1167
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1168
					$server = "https://update.spdyn.de/nic/update";
1169
					$port = "";
1170
					if ($this->_dnsServer) {
1171
						$server = $this->_dnsServer;
1172
					}
1173
					if ($this->_dnsPort) {
1174
						$port = ":" . $this->_dnsPort;
1175
					}
1176
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
1177
					break;
1178
				case 'all-inkl':
1179
					$needsIP = FALSE;
1180
					$server = 'https://dyndns.kasserver.com/';
1181
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
1182
					curl_setopt($ch, CURLOPT_URL, $server . 'myip=' . $this->_dnsIP);
1183
					break;
1184
				case 'hover':
1185
					$needsIP = FALSE;
1186
					$port = "";
1187
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1188
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1189

    
1190
					//step 1: login to API
1191
					$post_data['username'] = $this->_dnsUser;
1192
					$post_data['password'] = $this->_dnsPass;
1193
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1194
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
1195
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1196
					$output = curl_exec($ch);
1197

    
1198
					//extract the cookies
1199
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1200
					if( count($cookies[1]) > 0 ){
1201
						$cookie_data = implode("; ",$cookies[1]);
1202
					}
1203

    
1204
					//step 2: find the id of the A record
1205
					$post_data = null;
1206
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1207
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1208
					curl_setopt($ch, CURLOPT_HEADER, 0);
1209
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
1210
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1211

    
1212
					$output = curl_exec($ch);
1213
					$pregHost = preg_quote($this->_dnsHost);
1214
					$pregDomain = preg_quote($this->_dnsDomain);
1215
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$pregHost}\",\"type\":\"A\".*?\$/", $output, $hostID);
1216
					$hostID = $hostID[1];
1217
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{[^\}]*?\"name\":\"{$pregHost}\",\"type\":\"A\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
1218
					$hostIP = $hostIP[1];
1219
					unset($pregHost);
1220
					unset($pregDomain);
1221

    
1222
					//step 3: update the IP
1223
					if ($hostID) {
1224
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1225
						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1226
						curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1227
						$post_data['content'] = $this->_dnsIP;
1228
						curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1229
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1230
						curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns/{$hostID}");
1231
						log_error("HostID:{$hostID}, OldIP:{$hostIP}");
1232
					}
1233
					break;
1234
				case 'onecom':
1235
				case 'onecom-v6':
1236
					// see https://redmine.pfsense.org/issues/11293
1237

    
1238
					// login in
1239
					$post_data['displayUsername'] = $this->_dnsUser;
1240
					$post_data['username'] = $this->_dnsUser;
1241
					$post_data['password1'] = $this->_dnsPass;
1242
					$post_data['loginDomain'] = True;
1243
					$post_data['loginTarget'] = '';
1244
					$post_data['targetDomain'] = '';
1245

    
1246
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1247
					curl_setopt($ch, CURLOPT_URL, "https://www.one.com/admin/login.do");
1248
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1249
					$output = curl_exec($ch);
1250

    
1251
					// extract the cookies
1252
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
1253
					if( count($cookies[1]) > 0 ){
1254
						$cookie_data = implode("; ",$cookies[1]);
1255
					}
1256

    
1257
					// gets all DNS records of the domain.
1258
					$post_data = null;
1259
					$url = "https://www.one.com/admin/api/domains/" . $this->_dnsDomain + "/dns/custom_records";
1260
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1261
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1262
					curl_setopt($ch, CURLOPT_HEADER, 0);
1263
					curl_setopt($ch, CURLOPT_URL, $url);
1264
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1265
					$output = curl_exec($ch);
1266
					$result = json_decode($output, true);
1267
					$records = $result['result']['data'];
1268

    
1269
					// finds the record id of a record from it's subdomain
1270
					foreach ($records as $rec) {
1271
						if ($rec['attributes']['prefix'] == $this->_dnsHostname) {
1272
							$id = $rec['id'];
1273
							break;
1274
						}
1275
					}
1276
					if (!$id) {
1277
						log_error("Could not find one.com hostname record id");
1278
						return false;
1279
					}
1280

    
1281
					// changes the IP Address of a TYPE A record. Default TTL=3800
1282
					$tosend = array();
1283
					$tosend['type'] = 'dns_service_records';
1284
					$tosend['id'] = $id;
1285
					if (is_ipaddrv4($this->_dnsIP)) {
1286
						$rectype = 'A';
1287
					} else {
1288
						$rectype = 'AAAA';
1289
					}
1290
					$tosend['attributes'] = array(
1291
						'type' => $rectype,
1292
						'prefix' => $this->_dnsHostname,
1293
						'content' => $this->_dnsIP,
1294
						'ttl' => $this->_dnsTTL
1295
					);
1296
					$url = "https://www.one.com/admin/api/domains/" . $this->_dnsDomain .
1297
						 "/dns/custom_records/" . $id;
1298
					curl_setopt($ch, CURLOPT_URL, $url);
1299
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
1300
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($tosend));
1301
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
1302
					break;
1303
				case 'dreamhost':
1304
				case 'dreamhost-v6':
1305
					$needsIP = TRUE;
1306
					$isv6 = ($this->_dnsService === 'dreamhost-v6');
1307
					$server = 'https://api.dreamhost.com/';
1308
					$post_data['key'] = $this->_dnsPass;
1309
					$post_data['unique_id'] = uniqid($this->_dnsHost);
1310
					$post_data['cmd'] = 'dns-add_record';
1311
					$post_data['format'] = 'json';
1312
					$post_data['value'] = $this->_dnsIP;
1313
					$post_data['record'] = $this->_dnsHost;
1314
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1315
					$post_data['comment'] = "Updated by pfSense:$this->_dnsUser on " . date('c');
1316
					$port = "";
1317
					if ($this->_dnsServer) {
1318
						$server = $this->_dnsServer;
1319
					}
1320
					if ($this->_dnsPort) {
1321
						$port = ":" . $this->_dnsPort;
1322
					}
1323
					curl_setopt($ch, CURLOPT_URL, $server . $port);
1324
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1325
					break;
1326
				case 'digitalocean':
1327
				case 'digitalocean-v6':
1328
					// Get record ID
1329
					$server = 'https://api.digitalocean.com/v2/domains/';
1330
					$isv6 = ($this->_dnsService === 'digitalocean-v6');
1331
					$url = $server . $this->_dnsDomain . '/records';
1332
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$this->_dnsPass}"));
1333
					curl_setopt($ch, CURLOPT_URL, $url);
1334
					$output = json_decode(curl_exec($ch));
1335
					if (!is_array($output->domain_records)) {
1336
						$output->domain_records = array();
1337
					}
1338

    
1339
					// DO's API lists 20 NS records per page, so additional pages needs to be downloaded
1340
					// https://redmine.pfsense.org/issues/10952
1341
					$_domain_records = $output->domain_records;
1342
					$_count = count($_domain_records);
1343
					$_total = 0;
1344
					if (property_exists($output, 'meta')) {
1345
						$meta = $output->meta;
1346
						if (property_exists($meta, 'total')) {
1347
							$_total = $meta->total;
1348
						}
1349
					}
1350
					$_next = '...';
1351
					$_last = '';
1352
					while ($_next != $_last) {
1353
						$_next = '';
1354
						if (property_exists($output, 'links')) {
1355
							$_links = $output->links;
1356
							if (property_exists($_links, 'pages')) {
1357
								$_pages = $_links->pages;
1358
								if (property_exists($_pages, 'next')) {
1359
									$_next = $_pages->next;
1360
								}
1361
								if (property_exists($_pages, 'last')) {
1362
									$_last = $_pages->last;
1363
								}
1364
								if ($_next != '') {
1365
									echo "getting $_next\n";
1366
									curl_setopt($ch, CURLOPT_URL, $_next);
1367
									$output = json_decode(curl_exec($ch));
1368
									if (!is_array($output->domain_records)) {
1369
										$output->domain_records = array();
1370
									}
1371
									$_domain_records = array_merge($_domain_records,$output->domain_records);
1372
								}
1373
							}
1374
						}
1375
					}
1376
					$_count = count($_domain_records);
1377

    
1378
					foreach($_domain_records as $dnsRecord) {
1379
						// NS records are named @ in DO's API, so check type as well
1380
						// https://redmine.pfsense.org/issues/9171
1381
						if ($this->_dnsHost == $dnsRecord->name && $dnsRecord->type == ($isv6 ? 'AAAA' : 'A')) {
1382
							$recordID = $dnsRecord->id;
1383
							break;
1384
						}
1385
					}
1386

    
1387
					// Create/update record
1388
					if ($recordID == null) {
1389
						$url = $server . $this->_dnsDomain . '/records';
1390
					} else {
1391
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1392
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1393
					}
1394
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1395
					$post_data['ttl'] = $this->_dnsTTL;
1396
					$post_data['name'] = $this->_dnsHost;
1397
					$post_data['data'] = $this->_dnsIP;
1398
					curl_setopt($ch, CURLOPT_URL, $url);
1399
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1400
					break;
1401
				case 'cloudns':
1402
					/* Uses ClouDNS REST API
1403
					   Requires auth-id or sub-auth-id or sub-auth-user */
1404
					// Step 1: Find the Record ID
1405
					$url = 'https://api.cloudns.net/dns/records.json';
1406
					$post_data['auth-id'] = $this->_dnsUser;
1407
					$post_data['auth-password'] = $this->_dnsPass;
1408
					$post_data['domain-name'] = $this->_dnsDomain;
1409
					$post_data['host'] = $this->_dnsHost;
1410
					$post_data['type'] = 'a';
1411
					curl_setopt($ch, CURLOPT_URL, $url);
1412
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1413
					$output = json_decode(curl_exec($ch));
1414
					$recordID = key(get_object_vars($output));
1415

    
1416
					// Step 2: Set the record
1417
					$needsIP = TRUE;
1418
					$url = 'https://api.cloudns.net/dns/mod-record.json';
1419
					$post_data = array();
1420
					$post_data['auth-id'] = $this->_dnsUser;
1421
					$post_data['auth-password'] = $this->_dnsPass;
1422
					$post_data['domain-name'] = $this->_dnsDomain;
1423
					$post_data['record-id'] = $recordID;
1424
					$post_data['host'] = $this->_dnsHost;
1425
					$post_data['record'] = $this->_dnsIP;
1426
					$post_data['ttl'] = $this->_dnsTTL;
1427
					curl_setopt($ch, CURLOPT_URL, $url);
1428
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1429
					break;
1430
				case 'azurev6':
1431
				case 'azure':
1432
					$hostname = "{$this->_dnsHost}";
1433
					$resourceid = trim($this->_dnsZoneID);
1434
					$app_id = $this->_dnsUser;
1435
					$client_secret = $this->_dnsPass;
1436
					$newip = $this->_dnsIP;
1437
					$newttl = $this->_dnsTTL;
1438
						// ensure resourceid starts with / and has no trailing /
1439
					$resourceid = '/' . trim($resourceid, '/');
1440
						// extract subscription id from resource id
1441
					preg_match('/\\/subscriptions\\/(?<sid>[^\\/]*)/', $resourceid, $result);
1442
					$subscriptionid = isset($result['sid']) ? $result['sid'] : '';
1443
					if (isset($result['sid'])) {
1444
						$subscriptionid = $result['sid'];
1445
					} else {
1446
						log_error("Azure subscription id not found in resource id");
1447
						return false;
1448
					}
1449
						// find tenant id from subscription id
1450
					curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01");
1451
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1452
					curl_setopt($ch, CURLOPT_HEADER, 1);
1453
					curl_setopt($ch, CURLOPT_NOBODY, 1);
1454
					$output = curl_exec($ch);
1455
					$pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?<tid>[^"]*)/i';
1456
					preg_match($pattern, $output, $result);
1457
					if (isset($result['tid'])) {
1458
						$tenantid = $result['tid'];
1459
					} else {
1460
						log_error("Tenant ID not found");
1461
						return false;
1462
					}
1463
						// get an bearer token
1464
					curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token");
1465
					curl_setopt($ch, CURLOPT_POST, 1);
1466
					$body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret);
1467
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1468
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1469
					$server_output = curl_exec($ch);
1470
					$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1471
					preg_match("/\"access_token\":\"(?<tok>[^\"]*)\"/", $server_output, $result);
1472
					if (isset($result['tok'])) {
1473
						$bearertoken = $result['tok'];
1474
					} else {
1475
						log_error("no valid bearer token");
1476
						return false;
1477
					}
1478
						// Update the DNS record
1479
					if ($this->_useIPv6) {
1480
						$url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01";
1481
						$body = '{"properties":{"TTL":"' . $newttl . '", "AAAARecords":[{"ipv6Address":"' . $newip . '"}]}}';
1482
					} else {
1483
						$url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01";
1484
						$body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}';
1485
					}
1486
					$request_headers = array();
1487
					$request_headers[] = 'Accept: application/json';
1488
					$request_headers[] = 'Authorization: Bearer ' . $bearertoken;
1489
					$request_headers[] = 'Content-Type: application/json';
1490
					curl_setopt($ch, CURLOPT_URL, $url);
1491
					curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1492
					curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1493
					curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1494
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1495
					curl_setopt($ch, CURLOPT_HEADER, 1);
1496
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1497
					break;
1498
				case 'linode':
1499
				case 'linode-v6':
1500
					$linode_api = "https://api.linode.com/v4";
1501
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1502
					$record_name = $this->_dnsHost == "@" ? "" : $this->_dnsHost;
1503
					$err_prefix = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): (" . gettext("Error") . ")";
1504
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1505
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1506
						'Accept: application/json',
1507
						'Content-Type: application/json',
1508
						'Authorization: Bearer ' . $this->_dnsPass
1509
					));
1510

    
1511
					// get domain id
1512
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains");
1513
					$domains_output = curl_exec($ch);
1514
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1515
					if ( $http_code == 401 && preg_match('/invalid oauth token/i', $domains_output) ) {
1516
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authentication Failed"));
1517
						return false;
1518
					} else if ( $http_code == 401 ) {
1519
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authorization Failed"));
1520
						return false;
1521
					} else if ( $http_code != 200 ) {
1522
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1523
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1524
						return false;
1525
					}
1526

    
1527
					$domains_result = json_decode($domains_output, TRUE);
1528
					foreach($domains_result["data"] as $domains_entry) {
1529
						if ($domains_entry["domain"] == $this->_dnsDomain) {
1530
							$domain_id = $domains_entry["id"];
1531
						}
1532
					}
1533
					if ( ! $domain_id ) {
1534
						log_error("{$err_prefix} " . gettext("no domain ID for domain") . ": '{$this->_dnsDomain}'");
1535
						return false;
1536
					}
1537
					if ($this->_dnsVerboseLog) {
1538
						log_error("_update(): " . sprintf(gettext("found domain id: %s"), $domain_id));
1539
					}
1540

    
1541
					// get existing record if present
1542
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1543
					$records_output = curl_exec($ch);
1544
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1545
					if ( $http_code != 200 )
1546
					{
1547
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1548
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1549
						return false;
1550
					}
1551

    
1552
					$records_result = json_decode($records_output, TRUE);
1553
					foreach($records_result["data"] as $records_entry) {
1554
						if ( $records_entry["type"] == $record_type && $records_entry["name"] == $record_name ) {
1555
							// not adding support for pagination at this time, hope you have < 100 records!
1556
							$record = $records_entry;
1557
						}
1558
					}
1559
					if ($this->_dnsVerboseLog) {
1560
						log_error("_update(): " . ( $record ? sprintf(gettext("found existing record id: %s"), $record["id"]) : gettext("no matching record found") ));
1561
					}
1562

    
1563
					if (is_array($record)) {
1564
						// update existing record
1565
						$record["target"] = $this->_dnsIP;
1566
						$record["ttl_sec"] = (int) $this->_dnsTTL; // linode may round this up, 0 = zone default
1567
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1568
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1569
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records/" . $record["id"]);
1570
					} else {
1571
						// create a new record
1572
						$record = array(
1573
							"type"    => $record_type,
1574
							"name"    => $record_name,
1575
							"target"  => $this->_dnsIP,
1576
							"ttl_sec" => (int) $this->_dnsTTL, // linode may round this up, 0 = zone default
1577
						);
1578
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1579
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
1580
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1581
					}
1582
					break;
1583
				case 'dynv6':
1584
					$needIP = TRUE;
1585
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv4=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1586
					break;
1587
				case 'dynv6-v6':
1588
					$needIP = TRUE;
1589
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv6=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1590
					break;
1591
				case 'gandi-livedns':
1592
				case 'gandi-livedns-v6':
1593
					// https://api.gandi.net/docs/livedns/#v5-livedns-domains-fqdn-records-rrset_name-rrset_type
1594
					// PUT overrides the existing record or creates if none exists
1595
					$gandi_api = 'https://api.gandi.net/v5/livedns/domains/';
1596
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1597
					$url = "{$gandi_api}{$this->_dnsDomain}/records/{$this->_dnsHost}/{$record_type}";
1598

    
1599
					$headers = array(
1600
						'Authorization: Apikey ' . $this->_dnsPass,
1601
						'Content-Type: application/json'
1602
					);
1603
					$body = array(
1604
						"rrset_ttl"  => $this->_dnsTTL,
1605
						"rrset_values" => array($this->_dnsIP),
1606
					);
1607

    
1608
					curl_setopt($ch, CURLOPT_URL, $url);
1609
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1610
					curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
1611
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
1612
					break;
1613
				case 'desec':
1614
					curl_setopt($ch, CURLOPT_URL, 'https://update.dedyn.io/?hostname=' . $this->_dnsHost);
1615
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Token ' . $this->_dnsPass));
1616
					break;
1617
				case 'desec-v6':
1618
					curl_setopt($ch, CURLOPT_URL, 'https://update6.dedyn.io/?hostname=' . $this->_dnsHost);
1619
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Token ' . $this->_dnsPass));
1620
					break;
1621
				default:
1622
					break;
1623
			}
1624
			if ($this->_dnsService != 'ods') {
1625
				curl_setopt($ch, CURLOPT_HEADER, 1);
1626
				if ($this->_curlProxy == true) {
1627
					set_curlproxy($ch);
1628
				}
1629
				$response = curl_exec($ch);
1630
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1631
				$header = substr($response, 0, $header_size);
1632
				$data = substr($response, $header_size);
1633
				if ($this->_dnsVerboseLog) {
1634
					foreach (explode(PHP_EOL, $header) as $hv) {
1635
						log_error(sprintf("Response Header: %s", rtrim($hv)));
1636
				  	}
1637
					log_error(sprintf("Response Data: %s", $data));
1638
				}
1639
				$this->_checkStatus($ch, $data, $header);
1640
				@curl_close($ch);
1641
			}
1642
		}
1643

    
1644
		/**
1645
		 * Private Function (added 23 Feb 17)
1646
		 *   Send Removal To Selected Service.
1647
		 *
1648
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1649
		 *   is to remove the existing record and add a new record.
1650
		 *
1651
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1652
		 */
1653
		function _remove($existing_ip = NULL) {
1654
			$remove_allowed = false;
1655
			if ($this->_dnsVerboseLog) {
1656
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1657
			}
1658

    
1659
			if (strstr($this->_dnsRequestIf, "_vip")) {
1660
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1661
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1662
			} else {
1663
				$realparentif = $this->_dnsRequestIf;
1664
			}
1665

    
1666
			$ch = curl_init();
1667

    
1668
			if ($this->_useIPv6 == false) {
1669
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1670
			}
1671

    
1672
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1673
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1674
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1675
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1676

    
1677
			switch ($this->_dnsService) {
1678
			case 'dreamhost':
1679
			case 'dreamhost-v6':
1680
				$server = 'https://api.dreamhost.com/';
1681
				$post_data['key'] = $this->_dnsPass;
1682
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1683
				$post_data['cmd'] = 'dns-remove_record';
1684
				$post_data['format'] = 'json';
1685
				$post_data['value'] = $existing_ip;
1686
				$post_data['record'] = $this->_dnsHost;
1687
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1688
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1689
				$port = "";
1690
				if ($this->_dnsServer) {
1691
					$server = $this->_dnsServer;
1692
				}
1693
				if ($this->_dnsPort) {
1694
					$port = ":" . $this->_dnsPort;
1695
				}
1696
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1697
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1698
				$remove_allowed = true;
1699
				break;
1700
			default:
1701
				break;
1702
			}
1703
			if ($remove_allowed) {
1704
				curl_setopt($ch, CURLOPT_HEADER, 1);
1705
				$response = curl_exec($ch);
1706
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1707
				$header = substr($response, 0, $header_size);
1708
				$data = substr($response, $header_size);
1709
				$this->_checkStatus($ch, $data, $header);
1710
				@curl_close($ch);
1711
			}
1712
		}
1713

    
1714
		/**
1715
		 * Private Function (added 23 Feb 17)
1716
		 * Retrieves current DNS records from an external API source.
1717
		 *
1718
		 * Some services cannot perform new operations without the caller
1719
		 * providing existing record information.
1720
		 */
1721
		function _lookup_current() {
1722
			$lookup_allowed = false;
1723
			if ($this->_dnsVerboseLog) {
1724
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1725
			}
1726

    
1727
			if (strstr($this->_dnsRequestIf, "_vip")) {
1728
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1729
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1730
			} else {
1731
				$realparentif = $this->_dnsRequestIf;
1732
			}
1733

    
1734
			$ch = curl_init();
1735

    
1736
			if ($this->_useIPv6 == false) {
1737
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1738
			}
1739

    
1740
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1741
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1742
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1743
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1744

    
1745
			switch ($this->_dnsService) {
1746
			case 'dreamhost':
1747
			case 'dreamhost-v6':
1748
				$server = 'https://api.dreamhost.com/';
1749
				$post_data['key'] = $this->_dnsPass;
1750
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1751
				$post_data['cmd'] = 'dns-list_records';
1752
				$post_data['format'] = 'json';
1753
				$port = "";
1754
				if ($this->_dnsServer) {
1755
					$server = $this->_dnsServer;
1756
				}
1757
				if ($this->_dnsPort) {
1758
					$port = ":" . $this->_dnsPort;
1759
				}
1760
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1761
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1762
				$lookup_allowed = true;
1763
				break;
1764
			default:
1765
				break;
1766
			}
1767
			if ($lookup_allowed) {
1768
				curl_setopt($ch, CURLOPT_HEADER, 1);
1769
				$response = curl_exec($ch);
1770
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1771
				$header = substr($response, 0, $header_size);
1772
				$data = substr($response, $header_size);
1773
				$this->_checkLookupStatus($ch, $data, $header);
1774
				@curl_close($ch);
1775
			}
1776
		}
1777

    
1778
		/*
1779
		 * Private Function (added 23 Feb 17)
1780
		 *   Retrieve Lookup Status from the provided data and/or header
1781
		 */
1782
		function _checkLookupStatus($ch, $data, $header) {
1783
			if ($this->_dnsVerboseLog) {
1784
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1785
			}
1786
			$success_str = "(" . gettext("Success") . ") ";
1787
			$error_str = "(" . gettext("Error") . ") ";
1788
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1789

    
1790
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1791
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1792
				log_error($status);
1793
				$this->status = $status;
1794
				return;
1795
			}
1796
			switch ($this->_dnsService) {
1797
			case 'dreamhost':
1798
			case 'dreamhost-v6':
1799
				$result = json_decode($data,true);
1800
				if($result["result"] != "success") {
1801
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1802
					$this->_debug($data);
1803
					return;
1804
				} else {
1805
					foreach($result["data"] as $key => $row) {
1806
						if($row["record"] == $this->_dnsHost &&
1807
								(($row["type"] == "A" && !$this->_useIPv6)
1808
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1809
								)) {
1810
							if($row["editable"] == 0) {
1811
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1812
								continue;
1813
							}
1814
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1815
						}
1816
					}
1817
				}
1818
				if (!is_array($this->_existingRecords)){
1819
					if ($this->_dnsVerboseLog) {
1820
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1821
					}
1822
				}
1823
				break;
1824
			default:
1825
				break;
1826
			}
1827
		}
1828

    
1829
		/*
1830
		 * Private Function (added 12 July 2005) [beta]
1831
		 *   Retrieve Update Status
1832
		 */
1833
		function _checkStatus($ch, $data, $header) {
1834
			if ($this->_dnsVerboseLog) {
1835
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1836
			}
1837
			$successful_update = false;
1838
			$success_str = "(" . gettext("Success") . ") ";
1839
			$error_str = "(" . gettext("Error") . ") ";
1840
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1841

    
1842
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1843
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1844
				log_error($status);
1845
				$this->status = $status;
1846
				return;
1847
			}
1848
			switch ($this->_dnsService) {
1849
				// The special custom provider
1850
				case 'custom':
1851
				case 'custom-v6':
1852
					$successful_update = false;
1853
					if ($this->_dnsResultMatch == "") {
1854
						$successful_update = true;
1855
					} else {
1856
						$this->_dnsResultMatch = str_replace("%IP%", $this->_dnsIP, $this->_dnsResultMatch);
1857
						$matches = preg_split("/(?<!\\\\)\\|/", $this->_dnsResultMatch);
1858
						foreach ($matches as $match) {
1859
							$match= str_replace("\\|", "|", $match);
1860
							if (strcmp($match, trim($data, "\t\n\r")) == 0) {
1861
								$successful_update = true;
1862
							}
1863
						}
1864
						unset ($matches);
1865
					}
1866
					if ($successful_update == true) {
1867
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1868
					} else {
1869
						$status = $status_intro . $error_str . gettext("Result did not match.") . " [" . $data . "]";
1870
					}
1871
					break;
1872
				// Providers in an alphabetical order. Add code for new providers below in a correct position.
1873
				case 'dyfi':
1874
					// see specification at https://www.dy.fi/page/specification
1875
					if (preg_match('/badauth/i', $data)) {
1876
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1877
					} else if (preg_match('/nohost/i', $data)) {
1878
						$status = $status_intro . $error_str . gettext("No such host");
1879
					} else if (preg_match('/notfqdn/i', $data)) {
1880
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1881
					} else if (preg_match('/badip/i', $data)) {
1882
						$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.");
1883
					} else if (preg_match('/nochg/i', $data)) {
1884
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1885
						$successful_update = true;
1886
					} else if (preg_match('/good/i', $data)) {
1887
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1888
						$successful_update = true;
1889
					} else if (preg_match('/dnserr/i', $data)) {
1890
						$status = $status_intro . $error_str . gettext("Server side error.");
1891
					} else if (preg_match('/abuse/i', $data)) {
1892
						$status = $status_intro . $error_str . gettext("Updating too frequently, considered abuse.");
1893
					} else {
1894
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1895
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1896
						$this->_debug($data);
1897
					}
1898
					break;
1899
				case 'glesys':
1900
					$status_intro = "GleSYS ({$this->_dnsHost}): ";
1901
					if (preg_match('/Record updated/i', $data)) {
1902
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1903
						$successful_update = true;
1904
					} else {
1905
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1906
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1907
						$this->_debug($data);
1908
					}
1909
					break;
1910
				//
1911
				// Not yet ordered providers.
1912
				// TODO: When editing a provider, move it above in a correct position.
1913
				//
1914
				case 'dnsomatic':
1915
					$status_intro = "DNS-O-Matic ({$this->_dnsHost}): ";
1916
					if (preg_match('/badauth/i', $data)) {
1917
						$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.");
1918
					} else if (preg_match('/notfqdn /i', $data)) {
1919
						$status = $status_intro . gettext("The hostname specified is not a fully-qualified domain name. If no hostnames included, notfqdn will be returned once.");
1920
					} else if (preg_match('/nohost/i', $data)) {
1921
						$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.");
1922
					} else if (preg_match('/numhost/i', $data)) {
1923
						$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.");
1924
					} else if (preg_match('/abuse/i', $data)) {
1925
						$status = $status_intro . gettext("The hostname is blocked for update abuse.");
1926
					} else if (preg_match('/good/i', $data)) {
1927
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1928
						$successful_update = true;
1929
					} else if (preg_match('/dnserr/i', $data)) {
1930
						$status = $status_intro . gettext("DNS error encountered. Stop updating for 30 minutes.");
1931
					} else {
1932
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1933
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1934
						$this->_debug($data);
1935
					}
1936
					break;
1937
				case 'domeneshop':
1938
				case 'domeneshop-v6':
1939
					/* Responds with HTTP 204 on success.
1940
					 * see https://api.domeneshop.no/docs/index.html#tag/ddns/paths/~1dyndns~1update/get */
1941

    
1942
                                        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1943

    
1944
					if ($code == "204") {
1945
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1946
						$successful_update = true;
1947
					} else {
1948
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $code . ")";
1949
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1950
						$this->_debug($data);
1951
					}
1952
					break;
1953
				case 'onecom':
1954
				case 'onecom-v6':
1955
                                        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1956

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

    
2552
					if ($code == "200") {
2553
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2554
						$successful_update = true;
2555
					} else if (preg_match("/4\d\d/i", $code)) {
2556
						$arrbody = json_decode($data, true);
2557
						$message = $arrbody['message'] . ".";
2558
						if (isset($arrbody['errors']['content'])) {
2559
							foreach ($arrbody['errors']['content'] as $key => $content) {
2560
								$message .= " " . $content . ".";
2561
							}
2562
						}
2563
						$status = $status_intro . $error_str . $message;
2564
					} else {
2565
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $code . ")";
2566
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2567
						$this->_debug($data);
2568
					}
2569
					break;
2570
				case 'godaddy':
2571
				case 'godaddy-v6':
2572
					/* Responds with HTTP 200 on success.
2573
					   Responds with HTTP 4xx or  on error.
2574
					   Returns JSON data as body */
2575
;
2576
					if (preg_match("/\s200\sOK/i", $header)) {
2577
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
2578
						$successful_update = true;
2579
					} else if (preg_match("/\s4\d\d\s/i", $header)) {
2580
						$arrbody = json_decode($data, true);
2581
						$message = $arrbody['message'] . ".";
2582
						if (isset($arrbody['fields'])) {
2583
							foreach ($arrbody['fields'] as $error) {
2584
								$message .= " " . $error['path'] . ": " . $error['message'] . ".";
2585
							}
2586
						}
2587
						$status = $status_intro . $error_str . $message;
2588
					} else {
2589
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2590
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2591
						$this->_debug($data);
2592
					}
2593
					break;
2594
				case 'googledomains':
2595
					if (preg_match('/notfqdn/i', $data)) {
2596
						$status = $status_intro . $error_str . gettext("Not A FQDN");
2597
					} else if (preg_match('/nochg/i', $data)) {
2598
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2599
						$successful_update = true;
2600
					} else if (preg_match('/good/i', $data)) {
2601
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2602
						$successful_update = true;
2603
					} else if (preg_match('/badauth/i', $data)) {
2604
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2605
					} else if (preg_match('/nohost/i', $data)) {
2606
						$status = $status_intro . $error_str . gettext("Hostname does not exist or DynDNS not enabled");
2607
					} else if (preg_match('/badagent/i', $data)) {
2608
						$status = $status_intro . $error_str . gettext("Bad request");
2609
					} else if (preg_match('/abuse/i', $data)) {
2610
						$status = $status_intro . $error_str . gettext("Dynamic DNS access has been blocked!");
2611
					} else if (preg_match('/911/i', $data)) {
2612
						$status = $status_intro . $error_str . gettext("Error on Google's end, retry in 5 minutes");
2613
					} else {
2614
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2615
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2616
						$this->_debug($data);
2617
					}
2618
					break;
2619
				case 'dnsmadeeasy':
2620
					switch ($data) {
2621
						case 'success':
2622
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2623
							$successful_update = true;
2624
							break;
2625
						case 'error-auth':
2626
							$status = $status_intro . $error_str . gettext("Invalid username or password");
2627
							break;
2628
						case 'error-auth-suspend':
2629
							$status = $status_intro . $error_str . gettext("Account suspended");
2630
							break;
2631
						case 'error-auth-voided':
2632
							$status = $status_intro . $error_str . gettext("Account revoked");
2633
							break;
2634
						case 'error-record-invalid':
2635
							$status = $status_intro . $error_str . gettext("Record does not exist in the system. Unable to update record");
2636
							break;
2637
						case 'error-record-auth':
2638
							$status = $status_intro . $error_str . gettext("User does not have access to this record");
2639
							break;
2640
						case 'error-record-ip-same':
2641
							$status = $status_intro . $success_str . gettext("No Change In IP Address");
2642
							$successful_update = true;
2643
							break;
2644
						case 'error-system':
2645
							$status = $status_intro . $error_str . gettext("General system error recognized by the system");
2646
							break;
2647
						case 'error':
2648
							$status = $status_intro . $error_str . gettext("General system error unrecognized by the system");
2649
							break;
2650
						default:
2651
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2652
							log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2653
							$this->_debug($data);
2654
							break;
2655
					}
2656
					break;
2657
				case 'spdyn':
2658
				case 'spdyn-v6':
2659
					if (preg_match('/notfqdn/i', $data)) {
2660
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
2661
					} else if (preg_match('/nohost/i', $data)) {
2662
						$status = $status_intro . $error_str . gettext("No such host");
2663
					} else if (preg_match('/nochg/i', $data)) {
2664
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
2665
						$successful_update = true;
2666
					} else if (preg_match('/good/i', $data)) {
2667
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2668
						$successful_update = true;
2669
					} else if (preg_match('/badauth/i', $data)) {
2670
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2671
					} else {
2672
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2673
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2674
						$this->_debug($data);
2675
					}
2676
					break;
2677
				case 'all-inkl':
2678
 					if (preg_match('/good\s' . $this->_dnsIP . '/i', $data)) {
2679
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2680
 							$successful_update = true;
2681
 					} else if (preg_match('/good/i', $data)) {
2682
						$status = $status_intro . $error_str . gettext("Result did not match.");
2683
					} else if (preg_match("/\s401\sUnauthorized/i", $header)) {
2684
						$status = $status_intro . $error_str . gettext("Invalid username or password");
2685
					}
2686
					else {
2687
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2688
							log_error($status_intro . gettext("PAYLOAD:") . " " . $header . $data);
2689
 							$this->_debug($data);
2690
 							$this->_debug($header);
2691
 					}
2692
 					break;
2693
				case 'hover':
2694
					if (preg_match('/succeeded":true/i', $data)) {
2695
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2696
						$successful_update = true;
2697
					} else {
2698
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2699
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2700
						$this->_debug($data);
2701
					}
2702
					break;
2703
				case 'cloudns':
2704
					$result = json_decode($data, true);
2705
					if ($result['status'] == 'Success') {
2706
						$successful_update = true;
2707
					} else {
2708
						log_error($result['status'] . "(" . $result['statusDescription'] . ")");
2709
					}
2710
					break;
2711
				case 'dreamhost':
2712
				case 'dreamhost-v6':
2713
					$result = json_decode($data,true);
2714
					if ($this->_dnsVerboseLog) {
2715
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2716
					}
2717
					switch ($result['data']) {
2718
					case 'success':
2719
					case 'record_added':
2720
					case 'record_removed':
2721
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2722
						$successful_update = true;
2723
						break;
2724
					case 'no_record':
2725
					case 'no_such_record ':
2726
						$status = $status_intro . $error_str . gettext("No record exists.");
2727
						break;
2728
					case 'no_type':
2729
					case 'no_such_type ':
2730
						$status = $status_intro . $error_str . gettext("No type exists.");
2731
						break;
2732
					case 'no_value':
2733
					case 'no_such_value ':
2734
						$status = $status_intro . $error_str . gettext("No value exists.");
2735
						break;
2736
					case 'no_such_zone':
2737
						$status = $status_intro . $error_str . gettext("No such zone exists.");
2738
						break;
2739
					case 'invalid_record':
2740
						$status = $status_intro . $error_str . gettext("The specified record is invalid.");
2741
						break;
2742
					case 'invalid_type':
2743
						$status = $status_intro . $error_str . gettext("The specified type is invalid.");
2744
						break;
2745
					case 'invalid_value':
2746
						$status = $status_intro . $error_str . gettext("The specified value is invalid.");
2747
						break;
2748
					case 'not_editable ':
2749
						$status = $status_intro . $error_str . gettext("Record is not editable.");
2750
						break;
2751
					case 'record_already_exists_not_editable':
2752
						$status = $status_intro . $error_str . gettext("Record exists but is not editable.");
2753
						break;
2754
					case 'record_already_exists_remove_first':
2755
						$status = $status_intro . $error_str . gettext("Record exists and must be removed before adding.");
2756
						break;
2757
					case 'internal_error_updating_zone':
2758
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2759
						break;
2760
					case 'internal_error_could_not_load_zone':
2761
						$status = $status_intro . $error_str . gettext("A remote server error occurred loading the zone.");
2762
						break;
2763
					case 'internal_error_could_not_update_zone':
2764
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2765
						break;
2766
					case 'internal_error_could_not_add_record':
2767
						$status = $status_intro . $error_str . gettext("A remote server error occurred adding a new record.");
2768
						break;
2769
					case 'internal_error_could_not_destroy_record ':
2770
						$status = $status_intro . $error_str . gettext("A remote server error occurred removing an existing record.");
2771
						break;
2772
					default:
2773
						break;
2774
					}
2775
				case 'azure':
2776
				case 'azurev6':
2777
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2778
					if ($http_code == 401) {
2779
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2780
					} else if ($http_code == 201) {
2781
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2782
						$successful_update = true;
2783
					} else if ($http_code == 200) {
2784
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2785
						$successful_update = true;
2786
					} else {
2787
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2788
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2789
						$this->_debug($data);
2790
					}
2791
					break;
2792
				case 'linode':
2793
				case 'linode-v6':
2794
					$status_intro = gettext("Dynamic DNS") . " {$this->_dnsService} ({$this->_FQDN}): ";
2795
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2796
					$result = json_decode($data,true);
2797
					if ($this->_dnsVerboseLog) {
2798
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2799
					}
2800
					if ($http_code == 200 && isset($result["id"]) && ! isset($result["errors"])) {
2801
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2802
						$successful_update = true;
2803
					} else if ( $http_code == 401 && preg_match('/not authorized to use/i', $data) ) {
2804
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2805
					} else {
2806
						$status = $status_intro . $error_str .
2807
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" );
2808
					}
2809
					break;
2810
				case 'gandi-livedns':
2811
				case 'gandi-livedns-v6':
2812
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2813
					if ($http_code == 401) {
2814
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2815
					} else if ($http_code == 200 || $http_code == 201) {
2816
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2817
						$successful_update = true;
2818
					} else {
2819
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2820
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2821
						$this->_debug($data);
2822
					}
2823
					break;
2824
				case 'desec':
2825
				case 'desec-v6':
2826
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2827
					switch ($http_code) {
2828
						case '200':
2829
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2830
							$successful_update = true;
2831
							break;
2832
						case '401':
2833
							$status = $status_intro . $error_str . gettext("User Authorization Failed");
2834
							break;
2835
						default:
2836
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2837
							log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2838
							$this->_debug($data);
2839
							break;
2840
					}
2841
					break;
2842
				default:
2843
					break;
2844
			}
2845

    
2846
			if ($successful_update == true) {
2847
				/* Write WAN IP to cache file */
2848
				$wan_ip = $this->_checkIP();
2849
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2850
					$currentTime = time();
2851
					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));
2852
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2853
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2854
				} else {
2855
					@unlink($this->_cacheFile);
2856
				}
2857
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2858
					$currentTime = time();
2859
					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));
2860
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2861
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2862
				} else {
2863
					@unlink($this->_cacheFile_v6);
2864
				}
2865
			}
2866
			$this->status = $status;
2867
			log_error($status);
2868
		}
2869

    
2870
		/*
2871
		 * Private Function (added 12 July 05) [beta]
2872
		 *   Return Error, Set Last Error, and Die.
2873
		 */
2874
		function _error($errorNumber = '1') {
2875
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2876
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2877
			switch ($errorNumber) {
2878
				case 0:
2879
					break;
2880
				case 2:
2881
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2882
					break;
2883
				case 3:
2884
					$error = $err_str . gettext('No Username Provided.');
2885
					break;
2886
				case 4:
2887
					$error = $err_str . gettext('No Password Provided.');
2888
					break;
2889
				case 5:
2890
					$error = $err_str . gettext('No Hostname Provided.');
2891
					break;
2892
				case 6:
2893
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2894
					break;
2895
				case 7:
2896
					$error = $err_str . gettext('No Update URL Provided.');
2897
					break;
2898
				case 8:
2899
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2900
					break;
2901
				case 9:
2902
					$status = $err_str_r53 . gettext("Invalid TTL");
2903
					break;
2904
				case 10:
2905
					$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);
2906
					break;
2907
				default:
2908
					$error = $err_str . gettext('Unknown Response.');
2909
					/* FIXME: $data isn't in scope here */
2910
					/* $this->_debug($data); */
2911
					break;
2912
			}
2913
			$this->lastError = $error;
2914
			log_error($error);
2915
		}
2916

    
2917
		/*
2918
		 * Private Function (added 12 July 05) [beta]
2919
		 *   - Detect whether or not IP needs to be updated.
2920
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2921
		 *      | work with other systems. pfSense base is FreeBSD.
2922
		 */
2923
		function _detectChange() {
2924
			global $debug;
2925

    
2926
			if ($debug) {
2927
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2928
			}
2929

    
2930
			$currentTime = time();
2931

    
2932
			$wan_ip = $this->_checkIP();
2933
			if ($wan_ip == 0) {
2934
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2935
				return false;
2936
			}
2937
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
2938

    
2939
			if ($this->_useIPv6 == true) {
2940
				if (file_exists($this->_cacheFile_v6)) {
2941
					$contents = file_get_contents($this->_cacheFile_v6);
2942
					list($cacheIP, $cacheTime) = explode('|', $contents);
2943
					$this->_debug($cacheIP . '/' . $cacheTime);
2944
					$initial = false;
2945
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
2946
				} else {
2947
					$cacheIP = '::';
2948
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
2949
					$cacheTime = $currentTime;
2950
					$initial = true;
2951
					$log_error .= gettext("No Cached IPv6 found.");
2952
				}
2953
			} else {
2954
				if (file_exists($this->_cacheFile)) {
2955
					$contents = file_get_contents($this->_cacheFile);
2956
					list($cacheIP, $cacheTime) = explode('|', $contents);
2957
					$this->_debug($cacheIP . '/' . $cacheTime);
2958
					$initial = false;
2959
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
2960
				} else {
2961
					$cacheIP = '0.0.0.0';
2962
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
2963
					$cacheTime = $currentTime;
2964
					$initial = true;
2965
					$log_error .= gettext("No Cached IP found.");
2966
				}
2967
			}
2968
			if ($this->_dnsVerboseLog) {
2969
				log_error($log_error);
2970
			}
2971

    
2972
			// Convert seconds = days * hr/day * min/hr * sec/min
2973
			// We subtract 1 hour, so that when this code is executed few seconds
2974
			// before _dnsMaxCacheAgeDays has passed, then we still consider that
2975
			// a required number of days has passed.
2976
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60 - 60 * 60;
2977

    
2978
			$needs_updating = FALSE;
2979
			/* lets determine if the item needs updating */
2980
			if ($cacheIP != $wan_ip) {
2981
				$needs_updating = true;
2982
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
2983
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
2984
			}
2985
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
2986
				$needs_updating = true;
2987
				$this->_forceUpdateNeeded = true;
2988
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
2989
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
2990
			}
2991
			if ($initial == true) {
2992
				$needs_updating = true;
2993
				$update_reason .= gettext("Initial update.");
2994
			}
2995

    
2996
			/*   finally if we need updating then store the
2997
			 *   new cache value and return true
2998
			 */
2999
			if ($needs_updating == true) {
3000
				if ($this->_dnsVerboseLog) {
3001
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
3002
				}
3003
				return true;
3004
			}
3005

    
3006
			return false;
3007
		}
3008

    
3009
		/*
3010
		 * Private Function (added 16 July 05) [beta]
3011
		 *   - Writes debug information to a file.
3012
		 *   - This function is only called when a unknown response
3013
		 *   - status is returned from a DynDNS service provider.
3014
		 */
3015
		function _debug($data) {
3016
			global $g;
3017

    
3018
			if (!$g['debug']) {
3019
				return;
3020
			}
3021
			$string = date('m-d-y h:i:s') . ' - (' . $this->_debugID . ') - [' . $this->_dnsService . '] - ' . $data . "\n";
3022
			$file = fopen($this->_debugFile, 'a');
3023
			fwrite($file, $string);
3024
			fclose($file);
3025
		}
3026
		function _checkIP() {
3027
			global $debug;
3028

    
3029
			if ($debug) {
3030
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
3031
			}
3032

    
3033
			if ($this->_useIPv6 == true) {
3034
				$ip_address = get_interface_ipv6($this->_if);
3035
				if (!is_ipaddrv6($ip_address)) {
3036
					return 0;
3037
				}
3038
			} else {
3039
				$ip_address = dyndnsCheckIP($this->_if);
3040
				if (!is_ipaddr($ip_address)) {
3041
					return 0;
3042
				}
3043
			}
3044
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
3045
				if (is_ipaddr($ip_address)) {
3046
					if ($this->_dnsVerboseLog) {
3047
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
3048
					}
3049
				} else {
3050
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
3051
					return 0;
3052
				}
3053
			} else {
3054
				if ($this->_dnsVerboseLog) {
3055
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
3056
				}
3057
			}
3058
			$this->_dnsIP = $ip_address;
3059

    
3060
			return $ip_address;
3061
		}
3062

    
3063
	}
3064

    
3065
?>
(15-15/61)