Project

General

Profile

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

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

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

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

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

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

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

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

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

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

    
390
			$this->_debugID = rand(1000000, 9999999);
391

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

    
498
			unlock($dyndnslck);
499
		}
500

    
501
		/*
502
		 * Private Function (added 12 July 05) [beta]
503
		 *   Send Update To Selected Service.
504
		 */
505
		function _update() {
506

    
507
			if ($this->_dnsVerboseLog) {
508
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
509
			}
510

    
511
			if (strstr($this->_dnsRequestIf, "_vip")) {
512
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
513
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
514
			} else {
515
				$realparentif = $this->_dnsRequestIf;
516
			}
517

    
518
			$ch = curl_init();
519

    
520
			if ($this->_useIPv6 == false) {
521
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
522
			}
523

    
524
			if ($this->_dnsService != 'ods') {
525
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
526
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
527
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
528
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
529
			}
530

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

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

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

    
944
					if (is_ipaddrv4($this->_dnsIP)) {
945
						$type = 'A';
946
					} else {
947
						$type = 'AAAA';
948
					}
949

    
950
					// get record_id
951
					curl_setopt($ch, CURLOPT_URL, "https://pddimp.yandex.ru/api2/admin/dns/list?domain={$this->_dnsDomain}");
952
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('PddToken: ' . $this->_dnsPass));
953
					$output = json_decode(curl_exec($ch), true);
954
					if (is_array($output["records"])) {
955
						foreach($output["records"] as $record) {
956
							if (($record["domain"] == $this->_dnsDomain) &&
957
							    ($record["subdomain"] == $this->_dnsHost) &&
958
							    ($record["type"] == $type)) {
959
								$record_id = $record["record_id"];
960
							}
961
						}
962
					}
963

    
964
					if ($record_id) {
965
						$action = 'edit';
966
						$post_data['record_id'] = $record_id;
967
					} else {
968
						$action = 'add';
969
					}
970

    
971
					$post_data['domain'] = $this->_dnsDomain;
972
					$post_data['subdomain'] = $this->_dnsHost;
973
					$post_data['content'] = $this->_dnsIP;
974
					$post_data['type'] = $type;
975
					if ($this->_dnsTTL) {
976
						$post_data['ttl'] = $this->_dnsTTL;
977
					}
978

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

    
1071
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1072

    
1073
					if ((!$this->_dnsUser) || (strpos($this->_dnsUser, '@') !== false)) {
1074
						if (strpos($this->_dnsUser, '@') !== false) {
1075
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1076
										'X-Auth-Email: ' . $this->_dnsUser,
1077
										'X-Auth-Key: ' . $this->_dnsPass,
1078
										'Content-Type: application/json'
1079
										));
1080
						} else {
1081
							curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1082
										'Authorization: Bearer ' . $this->_dnsPass,
1083
										'Content-Type: application/json'
1084
										));
1085
						}
1086

    
1087
						// Get zone ID
1088
						$getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$this->_dnsDomain}";
1089
						curl_setopt($ch, CURLOPT_URL, $getZoneId);
1090
						$output = json_decode(curl_exec($ch));
1091
						$zone = $output->result[0]->id;
1092
					} else {
1093
						curl_setopt($ch, CURLOPT_HTTPHEADER, array(
1094
							'Authorization: Bearer ' . $this->_dnsPass,
1095
							'Content-Type: application/json'
1096
						));
1097

    
1098
						$zone = $this->_dnsUser;
1099
					}
1100

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

    
1241
					//step 1: login to API
1242
					$post_data['username'] = $this->_dnsUser;
1243
					$post_data['password'] = $this->_dnsPass;
1244
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1245
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
1246
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1247
					$output = curl_exec($ch);
1248

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

    
1255
					//step 2: find the id of the A record
1256
					$post_data = null;
1257
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1258
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1259
					curl_setopt($ch, CURLOPT_HEADER, 0);
1260
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
1261
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1262

    
1263
					$output = curl_exec($ch);
1264
					$pregHost = preg_quote($this->_dnsHost);
1265
					$pregDomain = preg_quote($this->_dnsDomain);
1266
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$pregHost}\",\"type\":\"A\".*?\$/", $output, $hostID);
1267
					$hostID = $hostID[1];
1268
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$pregDomain}.*?entries.*?{[^\}]*?\"name\":\"{$pregHost}\",\"type\":\"A\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
1269
					$hostIP = $hostIP[1];
1270
					unset($pregHost);
1271
					unset($pregDomain);
1272

    
1273
					//step 3: update the IP
1274
					if ($hostID) {
1275
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1276
						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
1277
						curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1278
						$post_data['content'] = $this->_dnsIP;
1279
						curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1280
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1281
						curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns/{$hostID}");
1282
						log_error("HostID:{$hostID}, OldIP:{$hostIP}");
1283
					}
1284
					break;
1285
				case 'onecom':
1286
				case 'onecom-v6':
1287
					// see https://redmine.pfsense.org/issues/11293
1288

    
1289
					// login in
1290
					$post_data['displayUsername'] = $this->_dnsUser;
1291
					$post_data['username'] = $this->_dnsUser;
1292
					$post_data['password1'] = $this->_dnsPass;
1293
					$post_data['loginDomain'] = True;
1294
					$post_data['loginTarget'] = '';
1295
					$post_data['targetDomain'] = '';
1296

    
1297
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1298
					curl_setopt($ch, CURLOPT_URL, "https://www.one.com/admin/login.do");
1299
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
1300
					$output = curl_exec($ch);
1301

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

    
1308
					// gets all DNS records of the domain.
1309
					$post_data = null;
1310
					$url = "https://www.one.com/admin/api/domains/" . $this->_dnsDomain + "/dns/custom_records";
1311
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1312
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
1313
					curl_setopt($ch, CURLOPT_HEADER, 0);
1314
					curl_setopt($ch, CURLOPT_URL, $url);
1315
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
1316
					$output = curl_exec($ch);
1317
					$result = json_decode($output, true);
1318
					$records = $result['result']['data'];
1319

    
1320
					// finds the record id of a record from it's subdomain
1321
					foreach ($records as $rec) {
1322
						if ($rec['attributes']['prefix'] == $this->_dnsHostname) {
1323
							$id = $rec['id'];
1324
							break;
1325
						}
1326
					}
1327
					if (!$id) {
1328
						log_error("Could not find one.com hostname record id");
1329
						return false;
1330
					}
1331

    
1332
					// changes the IP Address of a TYPE A record. Default TTL=3800
1333
					$tosend = array();
1334
					$tosend['type'] = 'dns_service_records';
1335
					$tosend['id'] = $id;
1336
					if (is_ipaddrv4($this->_dnsIP)) {
1337
						$rectype = 'A';
1338
					} else {
1339
						$rectype = 'AAAA';
1340
					}
1341
					$tosend['attributes'] = array(
1342
						'type' => $rectype,
1343
						'prefix' => $this->_dnsHostname,
1344
						'content' => $this->_dnsIP,
1345
						'ttl' => $this->_dnsTTL
1346
					);
1347
					$url = "https://www.one.com/admin/api/domains/" . $this->_dnsDomain .
1348
						 "/dns/custom_records/" . $id;
1349
					curl_setopt($ch, CURLOPT_URL, $url);
1350
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
1351
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($tosend));
1352
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
1353
					break;
1354
				case 'dreamhost':
1355
				case 'dreamhost-v6':
1356
					$needsIP = TRUE;
1357
					$isv6 = ($this->_dnsService === 'dreamhost-v6');
1358
					$server = 'https://api.dreamhost.com/';
1359
					$post_data['key'] = $this->_dnsPass;
1360
					$post_data['unique_id'] = uniqid($this->_dnsHost);
1361
					$post_data['cmd'] = 'dns-add_record';
1362
					$post_data['format'] = 'json';
1363
					$post_data['value'] = $this->_dnsIP;
1364
					$post_data['record'] = $this->_dnsHost;
1365
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1366
					$post_data['comment'] = "Updated by pfSense:$this->_dnsUser on " . date('c');
1367
					$port = "";
1368
					if ($this->_dnsServer) {
1369
						$server = $this->_dnsServer;
1370
					}
1371
					if ($this->_dnsPort) {
1372
						$port = ":" . $this->_dnsPort;
1373
					}
1374
					curl_setopt($ch, CURLOPT_URL, $server . $port);
1375
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1376
					break;
1377
				case 'digitalocean':
1378
				case 'digitalocean-v6':
1379
					// Get record ID
1380
					$server = 'https://api.digitalocean.com/v2/domains/';
1381
					$isv6 = ($this->_dnsService === 'digitalocean-v6');
1382
					$url = $server . $this->_dnsDomain . '/records';
1383
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$this->_dnsPass}"));
1384
					curl_setopt($ch, CURLOPT_URL, $url);
1385
					$output = json_decode(curl_exec($ch));
1386
					if (!is_array($output->domain_records)) {
1387
						$output->domain_records = array();
1388
					}
1389

    
1390
					// DO's API lists 20 NS records per page, so additional pages needs to be downloaded
1391
					// https://redmine.pfsense.org/issues/10952
1392
					$_domain_records = $output->domain_records;
1393
					$_count = count($_domain_records);
1394
					$_total = 0;
1395
					if (property_exists($output, 'meta')) {
1396
						$meta = $output->meta;
1397
						if (property_exists($meta, 'total')) {
1398
							$_total = $meta->total;
1399
						}
1400
					}
1401
					$_next = '...';
1402
					$_last = '';
1403
					while ($_next != $_last) {
1404
						$_next = '';
1405
						if (property_exists($output, 'links')) {
1406
							$_links = $output->links;
1407
							if (property_exists($_links, 'pages')) {
1408
								$_pages = $_links->pages;
1409
								if (property_exists($_pages, 'next')) {
1410
									$_next = $_pages->next;
1411
								}
1412
								if (property_exists($_pages, 'last')) {
1413
									$_last = $_pages->last;
1414
								}
1415
								if ($_next != '') {
1416
									echo "getting $_next\n";
1417
									curl_setopt($ch, CURLOPT_URL, $_next);
1418
									$output = json_decode(curl_exec($ch));
1419
									if (!is_array($output->domain_records)) {
1420
										$output->domain_records = array();
1421
									}
1422
									$_domain_records = array_merge($_domain_records,$output->domain_records);
1423
								}
1424
							}
1425
						}
1426
					}
1427
					$_count = count($_domain_records);
1428

    
1429
					foreach($_domain_records as $dnsRecord) {
1430
						// NS records are named @ in DO's API, so check type as well
1431
						// https://redmine.pfsense.org/issues/9171
1432
						if ($this->_dnsHost == $dnsRecord->name && $dnsRecord->type == ($isv6 ? 'AAAA' : 'A')) {
1433
							$recordID = $dnsRecord->id;
1434
							break;
1435
						}
1436
					}
1437

    
1438
					// Create/update record
1439
					if ($recordID == null) {
1440
						$url = $server . $this->_dnsDomain . '/records';
1441
					} else {
1442
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1443
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1444
					}
1445
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1446
					$post_data['ttl'] = $this->_dnsTTL;
1447
					$post_data['name'] = $this->_dnsHost;
1448
					$post_data['data'] = $this->_dnsIP;
1449
					curl_setopt($ch, CURLOPT_URL, $url);
1450
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1451
					break;
1452
				case 'cloudns':
1453
					/* Uses ClouDNS REST API
1454
					   Requires auth-id or sub-auth-id or sub-auth-user */
1455
					// Step 1: Find the Record ID
1456
					$url = 'https://api.cloudns.net/dns/records.json';
1457
					$post_data['auth-id'] = $this->_dnsUser;
1458
					$post_data['auth-password'] = $this->_dnsPass;
1459
					$post_data['domain-name'] = $this->_dnsDomain;
1460
					$post_data['host'] = $this->_dnsHost;
1461
					$post_data['type'] = 'a';
1462
					curl_setopt($ch, CURLOPT_URL, $url);
1463
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1464
					$output = json_decode(curl_exec($ch));
1465
					$recordID = key(get_object_vars($output));
1466

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

    
1562
					// get domain id
1563
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains");
1564
					$domains_output = curl_exec($ch);
1565
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1566
					if ( $http_code == 401 && preg_match('/invalid oauth token/i', $domains_output) ) {
1567
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authentication Failed"));
1568
						return false;
1569
					} else if ( $http_code == 401 ) {
1570
						log_error("$err_prefix " . gettext("querying domains") . ": " . gettext("User Authorization Failed"));
1571
						return false;
1572
					} else if ( $http_code != 200 ) {
1573
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1574
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1575
						return false;
1576
					}
1577

    
1578
					$domains_result = json_decode($domains_output, TRUE);
1579
					foreach($domains_result["data"] as $domains_entry) {
1580
						if ($domains_entry["domain"] == $this->_dnsDomain) {
1581
							$domain_id = $domains_entry["id"];
1582
						}
1583
					}
1584
					if ( ! $domain_id ) {
1585
						log_error("{$err_prefix} " . gettext("no domain ID for domain") . ": '{$this->_dnsDomain}'");
1586
						return false;
1587
					}
1588
					if ($this->_dnsVerboseLog) {
1589
						log_error("_update(): " . sprintf(gettext("found domain id: %s"), $domain_id));
1590
					}
1591

    
1592
					// get existing record if present
1593
					curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1594
					$records_output = curl_exec($ch);
1595
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1596
					if ( $http_code != 200 )
1597
					{
1598
						log_error("$err_prefix " . gettext("querying domains") . ": " .
1599
							( isset($domains_result["errors"][0]["reason"]) ? $domains_result["errors"][0]["reason"] : "HTTP {$http_code}" ) );
1600
						return false;
1601
					}
1602

    
1603
					$records_result = json_decode($records_output, TRUE);
1604
					foreach($records_result["data"] as $records_entry) {
1605
						if ( $records_entry["type"] == $record_type && $records_entry["name"] == $record_name ) {
1606
							// not adding support for pagination at this time, hope you have < 100 records!
1607
							$record = $records_entry;
1608
						}
1609
					}
1610
					if ($this->_dnsVerboseLog) {
1611
						log_error("_update(): " . ( $record ? sprintf(gettext("found existing record id: %s"), $record["id"]) : gettext("no matching record found") ));
1612
					}
1613

    
1614
					if (is_array($record)) {
1615
						// update existing record
1616
						$record["target"] = $this->_dnsIP;
1617
						$record["ttl_sec"] = (int) $this->_dnsTTL; // linode may round this up, 0 = zone default
1618
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1619
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1620
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records/" . $record["id"]);
1621
					} else {
1622
						// create a new record
1623
						$record = array(
1624
							"type"    => $record_type,
1625
							"name"    => $record_name,
1626
							"target"  => $this->_dnsIP,
1627
							"ttl_sec" => (int) $this->_dnsTTL, // linode may round this up, 0 = zone default
1628
						);
1629
						curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($record));
1630
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
1631
						curl_setopt($ch, CURLOPT_URL, "{$linode_api}/domains/{$domain_id}/records");
1632
					}
1633
					break;
1634
				case 'dynv6':
1635
					$needIP = TRUE;
1636
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv4=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1637
					break;
1638
				case 'dynv6-v6':
1639
					$needIP = TRUE;
1640
					curl_setopt($ch, CURLOPT_URL, 'https://dynv6.com/api/update?ipv6=' . $this->_dnsIP . '&hostname=' . $this->_dnsHost . '&token=' . $this->_dnsPass);
1641
					break;
1642
				case 'gandi-livedns':
1643
				case 'gandi-livedns-v6':
1644
					// https://api.gandi.net/docs/livedns/#v5-livedns-domains-fqdn-records-rrset_name-rrset_type
1645
					// PUT overrides the existing record or creates if none exists
1646
					$gandi_api = 'https://api.gandi.net/v5/livedns/domains/';
1647
					$record_type = $this->_useIPv6 ? "AAAA" : "A";
1648
					$url = "{$gandi_api}{$this->_dnsDomain}/records/{$this->_dnsHost}/{$record_type}";
1649

    
1650
					$headers = array(
1651
						'Authorization: Apikey ' . $this->_dnsPass,
1652
						'Content-Type: application/json'
1653
					);
1654
					$body = array(
1655
						"rrset_ttl"  => $this->_dnsTTL,
1656
						"rrset_values" => array($this->_dnsIP),
1657
					);
1658

    
1659
					curl_setopt($ch, CURLOPT_URL, $url);
1660
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1661
					curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
1662
					curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
1663
					break;
1664
				case 'desec':
1665
					curl_setopt($ch, CURLOPT_URL, 'https://update.dedyn.io/?hostname=' . $this->_dnsHost);
1666
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Token ' . $this->_dnsPass));
1667
					break;
1668
				case 'desec-v6':
1669
					curl_setopt($ch, CURLOPT_URL, 'https://update6.dedyn.io/?hostname=' . $this->_dnsHost);
1670
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Token ' . $this->_dnsPass));
1671
					break;
1672
				default:
1673
					break;
1674
			}
1675
			if ($this->_dnsService != 'ods') {
1676
				curl_setopt($ch, CURLOPT_HEADER, 1);
1677
				if ($this->_curlProxy == true) {
1678
					set_curlproxy($ch);
1679
				}
1680
				$response = curl_exec($ch);
1681
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1682
				$header = substr($response, 0, $header_size);
1683
				$data = substr($response, $header_size);
1684
				if ($this->_dnsVerboseLog) {
1685
					foreach (explode(PHP_EOL, $header) as $hv) {
1686
						log_error(sprintf("Response Header: %s", rtrim($hv)));
1687
				  	}
1688
					log_error(sprintf("Response Data: %s", $data));
1689
				}
1690
				$this->_checkStatus($ch, $data, $header);
1691
				@curl_close($ch);
1692
			}
1693
		}
1694

    
1695
		/**
1696
		 * Private Function (added 23 Feb 17)
1697
		 *   Send Removal To Selected Service.
1698
		 *
1699
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1700
		 *   is to remove the existing record and add a new record.
1701
		 *
1702
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1703
		 */
1704
		function _remove($existing_ip = NULL) {
1705
			$remove_allowed = false;
1706
			if ($this->_dnsVerboseLog) {
1707
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1708
			}
1709

    
1710
			if (strstr($this->_dnsRequestIf, "_vip")) {
1711
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1712
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1713
			} else {
1714
				$realparentif = $this->_dnsRequestIf;
1715
			}
1716

    
1717
			$ch = curl_init();
1718

    
1719
			if ($this->_useIPv6 == false) {
1720
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1721
			}
1722

    
1723
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1724
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1725
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1726
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1727

    
1728
			switch ($this->_dnsService) {
1729
			case 'dreamhost':
1730
			case 'dreamhost-v6':
1731
				$server = 'https://api.dreamhost.com/';
1732
				$post_data['key'] = $this->_dnsPass;
1733
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1734
				$post_data['cmd'] = 'dns-remove_record';
1735
				$post_data['format'] = 'json';
1736
				$post_data['value'] = $existing_ip;
1737
				$post_data['record'] = $this->_dnsHost;
1738
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1739
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1740
				$port = "";
1741
				if ($this->_dnsServer) {
1742
					$server = $this->_dnsServer;
1743
				}
1744
				if ($this->_dnsPort) {
1745
					$port = ":" . $this->_dnsPort;
1746
				}
1747
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1748
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1749
				$remove_allowed = true;
1750
				break;
1751
			default:
1752
				break;
1753
			}
1754
			if ($remove_allowed) {
1755
				curl_setopt($ch, CURLOPT_HEADER, 1);
1756
				$response = curl_exec($ch);
1757
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1758
				$header = substr($response, 0, $header_size);
1759
				$data = substr($response, $header_size);
1760
				$this->_checkStatus($ch, $data, $header);
1761
				@curl_close($ch);
1762
			}
1763
		}
1764

    
1765
		/**
1766
		 * Private Function (added 23 Feb 17)
1767
		 * Retrieves current DNS records from an external API source.
1768
		 *
1769
		 * Some services cannot perform new operations without the caller
1770
		 * providing existing record information.
1771
		 */
1772
		function _lookup_current() {
1773
			$lookup_allowed = false;
1774
			if ($this->_dnsVerboseLog) {
1775
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1776
			}
1777

    
1778
			if (strstr($this->_dnsRequestIf, "_vip")) {
1779
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1780
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1781
			} else {
1782
				$realparentif = $this->_dnsRequestIf;
1783
			}
1784

    
1785
			$ch = curl_init();
1786

    
1787
			if ($this->_useIPv6 == false) {
1788
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1789
			}
1790

    
1791
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1792
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1793
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1794
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1795

    
1796
			switch ($this->_dnsService) {
1797
			case 'dreamhost':
1798
			case 'dreamhost-v6':
1799
				$server = 'https://api.dreamhost.com/';
1800
				$post_data['key'] = $this->_dnsPass;
1801
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1802
				$post_data['cmd'] = 'dns-list_records';
1803
				$post_data['format'] = 'json';
1804
				$port = "";
1805
				if ($this->_dnsServer) {
1806
					$server = $this->_dnsServer;
1807
				}
1808
				if ($this->_dnsPort) {
1809
					$port = ":" . $this->_dnsPort;
1810
				}
1811
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1812
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1813
				$lookup_allowed = true;
1814
				break;
1815
			default:
1816
				break;
1817
			}
1818
			if ($lookup_allowed) {
1819
				curl_setopt($ch, CURLOPT_HEADER, 1);
1820
				$response = curl_exec($ch);
1821
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1822
				$header = substr($response, 0, $header_size);
1823
				$data = substr($response, $header_size);
1824
				$this->_checkLookupStatus($ch, $data, $header);
1825
				@curl_close($ch);
1826
			}
1827
		}
1828

    
1829
		/*
1830
		 * Private Function (added 23 Feb 17)
1831
		 *   Retrieve Lookup Status from the provided data and/or header
1832
		 */
1833
		function _checkLookupStatus($ch, $data, $header) {
1834
			if ($this->_dnsVerboseLog) {
1835
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1836
			}
1837
			$success_str = "(" . gettext("Success") . ") ";
1838
			$error_str = "(" . gettext("Error") . ") ";
1839
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1840

    
1841
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1842
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1843
				log_error($status);
1844
				$this->status = $status;
1845
				return;
1846
			}
1847
			switch ($this->_dnsService) {
1848
			case 'dreamhost':
1849
			case 'dreamhost-v6':
1850
				$result = json_decode($data,true);
1851
				if($result["result"] != "success") {
1852
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1853
					$this->_debug($data);
1854
					return;
1855
				} else {
1856
					foreach($result["data"] as $key => $row) {
1857
						if($row["record"] == $this->_dnsHost &&
1858
								(($row["type"] == "A" && !$this->_useIPv6)
1859
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1860
								)) {
1861
							if($row["editable"] == 0) {
1862
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1863
								continue;
1864
							}
1865
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1866
						}
1867
					}
1868
				}
1869
				if (!is_array($this->_existingRecords)){
1870
					if ($this->_dnsVerboseLog) {
1871
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1872
					}
1873
				}
1874
				break;
1875
			default:
1876
				break;
1877
			}
1878
		}
1879

    
1880
		/*
1881
		 * Private Function (added 12 July 2005) [beta]
1882
		 *   Retrieve Update Status
1883
		 */
1884
		function _checkStatus($ch, $data, $header) {
1885
			if ($this->_dnsVerboseLog) {
1886
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1887
			}
1888
			$successful_update = false;
1889
			$success_str = "(" . gettext("Success") . ") ";
1890
			$error_str = "(" . gettext("Error") . ") ";
1891
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1892

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

    
1993
                                        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1994

    
1995
					if ($code == "204") {
1996
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1997
						$successful_update = true;
1998
					} else {
1999
						$status = $status_intro . "(" . gettext("Unknown Response") . ": " . $code . ")";
2000
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2001
						$this->_debug($data);
2002
					}
2003
					break;
2004
				case 'onecom':
2005
				case 'onecom-v6':
2006
                                        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2007

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

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

    
2908
			if ($successful_update == true) {
2909
				/* Write WAN IP to cache file */
2910
				$wan_ip = $this->_checkIP();
2911
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2912
					$currentTime = time();
2913
					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));
2914
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2915
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2916
				} else {
2917
					@unlink($this->_cacheFile);
2918
				}
2919
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2920
					$currentTime = time();
2921
					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));
2922
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2923
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2924
				} else {
2925
					@unlink($this->_cacheFile_v6);
2926
				}
2927
			}
2928
			$this->status = $status;
2929
			log_error($status);
2930
		}
2931

    
2932
		/*
2933
		 * Private Function (added 12 July 05) [beta]
2934
		 *   Return Error, Set Last Error, and Die.
2935
		 */
2936
		function _error($errorNumber = '1') {
2937
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2938
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2939
			switch ($errorNumber) {
2940
				case 0:
2941
					break;
2942
				case 2:
2943
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2944
					break;
2945
				case 3:
2946
					$error = $err_str . gettext('No Username Provided.');
2947
					break;
2948
				case 4:
2949
					$error = $err_str . gettext('No Password Provided.');
2950
					break;
2951
				case 5:
2952
					$error = $err_str . gettext('No Hostname Provided.');
2953
					break;
2954
				case 6:
2955
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2956
					break;
2957
				case 7:
2958
					$error = $err_str . gettext('No Update URL Provided.');
2959
					break;
2960
				case 8:
2961
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2962
					break;
2963
				case 9:
2964
					$status = $err_str_r53 . gettext("Invalid TTL");
2965
					break;
2966
				case 10:
2967
					$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);
2968
					break;
2969
				default:
2970
					$error = $err_str . gettext('Unknown Response.');
2971
					/* FIXME: $data isn't in scope here */
2972
					/* $this->_debug($data); */
2973
					break;
2974
			}
2975
			$this->lastError = $error;
2976
			log_error($error);
2977
		}
2978

    
2979
		/*
2980
		 * Private Function (added 12 July 05) [beta]
2981
		 *   - Detect whether or not IP needs to be updated.
2982
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2983
		 *      | work with other systems. pfSense base is FreeBSD.
2984
		 */
2985
		function _detectChange() {
2986
			global $debug;
2987

    
2988
			if ($debug) {
2989
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2990
			}
2991

    
2992
			$currentTime = time();
2993

    
2994
			$wan_ip = $this->_checkIP();
2995
			if ($wan_ip == 0) {
2996
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2997
				return false;
2998
			}
2999
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
3000

    
3001
			if ($this->_useIPv6 == true) {
3002
				if (file_exists($this->_cacheFile_v6)) {
3003
					$contents = file_get_contents($this->_cacheFile_v6);
3004
					list($cacheIP, $cacheTime) = explode('|', $contents);
3005
					$this->_debug($cacheIP . '/' . $cacheTime);
3006
					$initial = false;
3007
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
3008
				} else {
3009
					$cacheIP = '::';
3010
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
3011
					$cacheTime = $currentTime;
3012
					$initial = true;
3013
					$log_error .= gettext("No Cached IPv6 found.");
3014
				}
3015
			} else {
3016
				if (file_exists($this->_cacheFile)) {
3017
					$contents = file_get_contents($this->_cacheFile);
3018
					list($cacheIP, $cacheTime) = explode('|', $contents);
3019
					$this->_debug($cacheIP . '/' . $cacheTime);
3020
					$initial = false;
3021
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
3022
				} else {
3023
					$cacheIP = '0.0.0.0';
3024
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
3025
					$cacheTime = $currentTime;
3026
					$initial = true;
3027
					$log_error .= gettext("No Cached IP found.");
3028
				}
3029
			}
3030
			if ($this->_dnsVerboseLog) {
3031
				log_error($log_error);
3032
			}
3033

    
3034
			// Convert seconds = days * hr/day * min/hr * sec/min
3035
			// We subtract 1 hour, so that when this code is executed few seconds
3036
			// before _dnsMaxCacheAgeDays has passed, then we still consider that
3037
			// a required number of days has passed.
3038
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60 - 60 * 60;
3039

    
3040
			$needs_updating = FALSE;
3041
			/* lets determine if the item needs updating */
3042
			if ($cacheIP != $wan_ip) {
3043
				$needs_updating = true;
3044
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
3045
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
3046
			}
3047
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
3048
				$needs_updating = true;
3049
				$this->_forceUpdateNeeded = true;
3050
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
3051
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
3052
			}
3053
			if ($initial == true) {
3054
				$needs_updating = true;
3055
				$update_reason .= gettext("Initial update.");
3056
			}
3057

    
3058
			/*   finally if we need updating then store the
3059
			 *   new cache value and return true
3060
			 */
3061
			if ($needs_updating == true) {
3062
				if ($this->_dnsVerboseLog) {
3063
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
3064
				}
3065
				return true;
3066
			}
3067

    
3068
			return false;
3069
		}
3070

    
3071
		/*
3072
		 * Private Function (added 16 July 05) [beta]
3073
		 *   - Writes debug information to a file.
3074
		 *   - This function is only called when a unknown response
3075
		 *   - status is returned from a DynDNS service provider.
3076
		 */
3077
		function _debug($data) {
3078
			global $g;
3079

    
3080
			if (!$g['debug']) {
3081
				return;
3082
			}
3083
			$string = date('m-d-y h:i:s') . ' - (' . $this->_debugID . ') - [' . $this->_dnsService . '] - ' . $data . "\n";
3084
			$file = fopen($this->_debugFile, 'a');
3085
			fwrite($file, $string);
3086
			fclose($file);
3087
		}
3088
		function _checkIP() {
3089
			global $debug;
3090

    
3091
			if ($debug) {
3092
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
3093
			}
3094

    
3095
			if ($this->_useIPv6 == true) {
3096
				$ip_address = get_interface_ipv6($this->_if);
3097
				if (!is_ipaddrv6($ip_address)) {
3098
					return 0;
3099
				}
3100
			} else {
3101
				$ip_address = dyndnsCheckIP($this->_if);
3102
				if (!is_ipaddr($ip_address)) {
3103
					return 0;
3104
				}
3105
			}
3106
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
3107
				if (is_ipaddr($ip_address)) {
3108
					if ($this->_dnsVerboseLog) {
3109
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
3110
					}
3111
				} else {
3112
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
3113
					return 0;
3114
				}
3115
			} else {
3116
				if ($this->_dnsVerboseLog) {
3117
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
3118
				}
3119
			}
3120
			$this->_dnsIP = $ip_address;
3121

    
3122
			return $ip_address;
3123
		}
3124

    
3125
	}
3126

    
3127
?>
(15-15/61)