Project

General

Profile

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

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

    
141
	class updatedns {
142
		var $_cacheFile;
143
		var $_cacheFile_v6;
144
		var $_debugFile;
145
		var $_UserAgent = 'phpDynDNS/0.7';
146
		var $_errorVerbosity = 0;
147
		var $_dnsService;
148
		var $_dnsUser;
149
		var $_dnsPass;
150
		var $_dnsHost;
151
		var $_dnsDomain;
152
		var $_FQDN;
153
		var $_dnsIP;
154
		var $_dnsWildcard;
155
		var $_dnsProxied;
156
		var $_dnsMX;
157
		var $_dnsBackMX;
158
		var $_dnsServer;
159
		var $_dnsPort;
160
		var $_dnsUpdateURL;
161
		var $_dnsZoneID;
162
		var $_dnsTTL;
163
		var $status;
164
		var $_debugID;
165
		var $_if;
166
		var $_dnsResultMatch;
167
		var $_dnsRequestIf;
168
		var $_dnsRequestIfIP;
169
		var $_dnsVerboseLog;
170
		var $_curlIpresolveV4;
171
		var $_curlSslVerifypeer;
172
		var $_dnsMaxCacheAgeDays;
173
		var $_dnsDummyUpdateDone;
174
		var $_forceUpdateNeeded;
175
		var $_useIPv6;
176
		var $_existingRecords;
177

    
178
		/*
179
		 * Public Constructor Function (added 12 July 05) [beta]
180
		 *   - Gets the dice rolling for the update.
181
		 *   - $dnsResultMatch should only be used with $dnsService = 'custom'
182
		 *   -  $dnsResultMatch is parsed for '%IP%', which is the IP the provider was updated to,
183
		 *   -  it is otherwise expected to be exactly identical to what is returned by the Provider.
184
		 *   - $dnsUser, and $dnsPass indicate HTTP Auth for custom DNS, if they are needed in the URL (GET Variables), include them in $dnsUpdateURL.
185
		 *   - $For custom requests, $dnsUpdateURL is parsed for '%IP%', which is replaced with the new IP.
186
		 */
187
		function updatedns ($dnsService = '', $dnsHost = '', $dnsDomain = '', $dnsUser = '', $dnsPass = '',
188
					$dnsWildcard = 'OFF', $dnsProxied = false, $dnsMX = '', $dnsIf = '', $dnsBackMX = '',
189
					$dnsServer = '', $dnsPort = '', $dnsUpdateURL = '', $forceUpdate = false,
190
					$dnsZoneID ='', $dnsTTL='', $dnsResultMatch = '', $dnsRequestIf = '',
191
					$dnsID = '', $dnsVerboseLog = false, $curlIpresolveV4 = false, $curlSslVerifypeer = true) {
192

    
193
			global $config, $g, $dyndns_split_domain_types;
194
			if (in_array($dnsService, $dyndns_split_domain_types)) {
195
				$this->_FQDN = $dnsHost . "." . $dnsDomain;
196
			} else {
197
				$this->_FQDN = $dnsHost;
198
			}
199

    
200
			$this->_cacheFile = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.cache";
201
			$this->_cacheFile_v6 = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}_v6.cache";
202
			$this->_debugFile = "{$g['varetc_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($this->_FQDN) . "{$dnsID}.debug";
203

    
204
			$this->_curlIpresolveV4 = $curlIpresolveV4;
205
			$this->_curlSslVerifypeer = $curlSslVerifypeer;
206
			$this->_dnsVerboseLog = $dnsVerboseLog;
207
			if ($this->_dnsVerboseLog) {
208
				log_error(gettext("Dynamic DNS: updatedns() starting"));
209
			}
210

    
211
			$dyndnslck = lock("DDNS".$dnsID, LOCK_EX);
212

    
213
			if (!$dnsService) $this->_error(2);
214
			switch ($dnsService) {
215
			case 'freedns':
216
			case 'freedns-v6':
217
				if (!$dnsHost) $this->_error(5);
218
				break;
219
			case "namecheap":
220
				if (!$dnsPass) $this->_error(4);
221
				if (!$dnsHost) $this->_error(5);
222
				if (!$dnsDomain) $this->_error(5);
223
				break;
224
			case "cloudflare-v6":
225
			case "cloudflare":
226
			case "gratisdns":
227
			case "hover":
228
				if (!$dnsUser) $this->_error(3);
229
				if (!$dnsPass) $this->_error(4);
230
				if (!$dnsHost) $this->_error(5);
231
				if (!$dnsDomain) $this->_error(5);
232
				break;
233
			case 'route53-v6':
234
			case 'route53':
235
				if (!$dnsZoneID) $this->_error(8);
236
				if (!$dnsTTL) $this->_error(9);
237
				break;
238
			case 'cloudns':
239
			case "godaddy":
240
			case "godaddy-v6":
241
				if (!$dnsUser) $this->_error(3);
242
				if (!$dnsPass) $this->_error(4);
243
				if (!$dnsHost) $this->_error(5);
244
				if (!$dnsDomain) $this->_error(5);
245
				if (!$dnsTTL) $this->_error(9);
246
				break;
247
			case 'digitalocean':
248
				if (!$dnsPass) $this->_error(4);
249
				if (!$dnsHost) $this->_error(5);
250
				if (!$dnsDomain) $this->_error(5);
251
				if (!$dnsTTL) $this->_error(9);
252
				break;
253
			case 'azure':
254
			case 'azurev6':
255
				if (!$dnsUser) $this->_error(3);
256
				if (!$dnsPass) $this->_error(4);
257
				if (!$dnsHost) $this->_error(5);
258
				if (!$dnsZoneID) $this->_error(8);
259
				if (!$dnsTTL) $this->_error(9);
260
				break;
261
			case 'custom':
262
			case 'custom-v6':
263
				if (!$dnsUpdateURL) $this->_error(7);
264
				break;
265
			default:
266
				if (!$dnsUser) $this->_error(3);
267
				if (!$dnsPass) $this->_error(4);
268
				if (!$dnsHost) $this->_error(5);
269
			}
270

    
271
			switch ($dnsService) {
272
				case 'he-net-v6':
273
				case 'custom-v6':
274
				case 'spdyn-v6':
275
				case 'route53-v6':
276
				case 'duiadns-v6':
277
				case 'freedns-v6':
278
				case 'cloudflare-v6':
279
				case 'dreamhost-v6':
280
				case 'godaddy-v6':
281
				case 'azurev6':
282
					$this->_useIPv6 = true;
283
					break;
284
				default:
285
					$this->_useIPv6 = false;
286
			}
287
			$this->_dnsService = strtolower($dnsService);
288
			$this->_dnsUser = $dnsUser;
289
			$this->_dnsPass = base64_decode($dnsPass);
290
			$this->_dnsHost = $dnsHost;
291
			$this->_dnsDomain = $dnsDomain;
292
			$this->_dnsServer = $dnsServer;
293
			$this->_dnsPort = $dnsPort;
294
			$this->_dnsWildcard = $dnsWildcard;
295
			$this->_dnsProxied = $dnsProxied;
296
			$this->_dnsMX = $dnsMX;
297
			$this->_dnsZoneID = $dnsZoneID;
298
			$this->_dnsTTL = $dnsTTL;
299
			$this->_if = get_failover_interface($dnsIf);
300
			$this->_checkIP();
301
			$this->_dnsUpdateURL = $dnsUpdateURL;
302
			$this->_dnsResultMatch = $dnsResultMatch;
303
			$this->_dnsRequestIf = get_failover_interface($dnsRequestIf);
304
			if ($this->_dnsVerboseLog) {
305
				log_error(sprintf(gettext('Dynamic DNS (%1$s): running get_failover_interface for %2$s. found %3$s'), $this->_FQDN, $dnsRequestIf, $this->_dnsRequestIf));
306
			}
307
			$this->_dnsRequestIfIP = get_interface_ip($dnsRequestIf);
308
			$this->_dnsMaxCacheAgeDays = 25;
309
			$this->_dnsDummyUpdateDone = false;
310
			$this->_forceUpdateNeeded = $forceUpdate;
311

    
312
			// Ensure that we were able to lookup the IP
313
			if (!is_ipaddr($this->_dnsIP)) {
314
				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));
315
				unlock($dyndnslck);
316
				return;
317
			}
318

    
319
			$this->_debugID = rand(1000000, 9999999);
320

    
321
			if ($forceUpdate == false && $this->_detectChange() == false) {
322
				$this->_error(10);
323
			} else {
324
				switch ($this->_dnsService) {
325
					case 'glesys':
326
					case 'dnsomatic':
327
					case 'dyndns':
328
					case 'dyndns-static':
329
					case 'dyndns-custom':
330
					case 'dhs':
331
					case 'noip':
332
					case 'noip-free':
333
					case 'easydns':
334
					case 'hn':
335
					case 'zoneedit':
336
					case 'dyns':
337
					case 'ods':
338
					case 'freedns':
339
					case 'freedns-v6':
340
					case 'loopia':
341
					case 'staticcling':
342
					case 'dnsexit':
343
					case 'custom':
344
					case 'custom-v6':
345
					case 'opendns':
346
					case 'namecheap':
347
					case 'he-net':
348
					case 'he-net-v6':
349
					case 'duiadns':
350
					case 'duiadns-v6':
351
					case 'selfhost':
352
					case 'he-net-tunnelbroker':
353
					case 'route53':
354
					case 'route53-v6':
355
					case 'cloudflare':
356
					case 'cloudflare-v6':
357
					case 'eurodns':
358
					case 'gratisdns':
359
					case 'ovh-dynhost':
360
					case 'citynetwork':
361
					case 'dnsimple':
362
					case 'googledomains':
363
					case 'dnsmadeeasy':
364
					case 'spdyn':
365
					case 'spdyn-v6':
366
					case 'all-inkl':
367
					case 'cloudns':
368
					case 'hover':
369
					case 'digitalocean':
370
					case 'godaddy':
371
					case 'godaddy-v6':
372
					case  'azure':
373
					case  'azurev6':
374
						$this->_update();
375
						if ($this->_dnsDummyUpdateDone == true) {
376
							// If a dummy update was needed, then sleep a while and do the update again to put the proper address back.
377
							// Some providers (e.g. No-IP free accounts) need to have at least 1 address change every month.
378
							// If the address has not changed recently, or the user did "Force Update", then the code does
379
							// a dummy address change for providers like this.
380
							sleep(10);
381
							$this->_update();
382
						}
383
						break;
384
					case 'dreamhost':
385
					case 'dreamhost-v6':
386
						$this->_lookup_current();
387
						if (isset($this->status)) {
388
							return;
389
						}
390
						foreach ($this->_existingRecords as $record) {
391
							$this->_remove($record['existing_val']);
392
							$this->_update();
393
						}
394
						break;
395
					default:
396
						$this->_error(6);
397
						break;
398
				}
399
			}
400

    
401
			unlock($dyndnslck);
402
		}
403

    
404
		/*
405
		 * Private Function (added 12 July 05) [beta]
406
		 *   Send Update To Selected Service.
407
		 */
408
		function _update() {
409

    
410
			if ($this->_dnsVerboseLog) {
411
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _update() starting.'), $this->_dnsService, $this->_FQDN));
412
			}
413

    
414
			if (strstr($this->_dnsRequestIf, "_vip")) {
415
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
416
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
417
			} else {
418
				$realparentif = $this->_dnsRequestIf;
419
			}
420

    
421
			$ch = curl_init();
422

    
423
			if ($this->_useIPv6 == false) {
424
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
425
			}
426

    
427
			if ($this->_dnsService != 'ods') {
428
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
429
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
430
				curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
431
				curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
432
			}
433

    
434
			switch ($this->_dnsService) {
435
				case 'glesys':
436
					$needsIP = TRUE;
437
					$server = 'https://api.glesys.com/domain/updaterecord/format/json';
438
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
439
					$post_data['recordid'] = $this->_FQDN;
440
					$post_data['data'] = $this->_dnsIP;
441
					curl_setopt($ch, CURLOPT_URL, $server);
442
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
443
					break;
444
				case 'dyndns':
445
				case 'dyndns-static':
446
				case 'dyndns-custom':
447
					$needsIP = FALSE;
448
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
449
						$this->_dnsWildcard = "ON";
450
					}
451
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
452
					$server = "https://members.dyndns.org/nic/update";
453
					$port = "";
454
					if ($this->_dnsServer) {
455
						$server = $this->_dnsServer;
456
					}
457
					if ($this->_dnsPort) {
458
						$port = ":" . $this->_dnsPort;
459
					}
460
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
461
					break;
462
				case 'dhs':
463
					// DHS is disabled in the GUI because the following doesn't work.
464
					$needsIP = TRUE;
465
					$post_data['hostscmd'] = 'edit';
466
					$post_data['hostscmdstage'] = '2';
467
					$post_data['type'] = '4';
468
					$post_data['updatetype'] = 'Online';
469
					$post_data['mx'] = $this->_dnsMX;
470
					$post_data['mx2'] = '';
471
					$post_data['txt'] = '';
472
					$post_data['offline_url'] = '';
473
					$post_data['cloak'] = 'Y';
474
					$post_data['cloak_title'] = '';
475
					$post_data['ip'] = $this->_dnsIP;
476
					$post_data['domain'] = 'dyn.dhs.org';
477
					$post_data['hostname'] = $this->_dnsHost;
478
					$post_data['submit'] = 'Update';
479
					$server = "https://members.dhs.org/nic/hosts";
480
					$port = "";
481
					if ($this->_dnsServer) {
482
						$server = $this->_dnsServer;
483
					}
484
					if ($this->_dnsPort) {
485
						$port = ":" . $this->_dnsPort;
486
					}
487
					curl_setopt($ch, CURLOPT_URL, $server . $port);
488
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
489
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
490
					break;
491
				case 'noip':
492
				case 'noip-free':
493
					$needsIP = TRUE;
494
					$server = "https://dynupdate.no-ip.com/ducupdate.php";
495
					$port = "";
496
					if ($this->_dnsServer) {
497
						$server = $this->_dnsServer;
498
					}
499
					if ($this->_dnsPort) {
500
						$port = ":" . $this->_dnsPort;
501
					}
502
					if (($this->_dnsService == "noip-free") &&
503
					    ($this->_forceUpdateNeeded == true) &&
504
					    ($this->_dnsDummyUpdateDone == false)) {
505
						// Update the IP to a dummy value to force No-IP free accounts to see a change.
506
						$iptoset = "192.168.1.1";
507
						$this->_dnsDummyUpdateDone = true;
508
						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));
509
					} else {
510
						$iptoset = $this->_dnsIP;
511
					}
512
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&pass=' . urlencode($this->_dnsPass) . '&h[]=' . $this->_dnsHost.'&ip=' . $iptoset);
513
					break;
514
				case 'easydns':
515
					$needsIP = TRUE;
516
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
517
					$server = "https://members.easydns.com/dyn/dyndns.php";
518
					$port = "";
519
					if ($this->_dnsServer) {
520
						$server = $this->_dnsServer;
521
					}
522
					if ($this->_dnsPort) {
523
						$port = ":" . $this->_dnsPort;
524
					}
525
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard=' . $this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=' . $this->_dnsBackMX);
526
					break;
527
				case 'hn':
528
					$needsIP = TRUE;
529
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
530
					$server = "http://dup.hn.org/vanity/update";
531
					$port = "";
532
					if ($this->_dnsServer) {
533
						$server = $this->_dnsServer;
534
					}
535
					if ($this->_dnsPort) {
536
						$port = ":" . $this->_dnsPort;
537
					}
538
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?ver=1&IP=' . $this->_dnsIP);
539
					break;
540
				case 'zoneedit':
541
					$needsIP = FALSE;
542
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
543

    
544
					$server = "https://dynamic.zoneedit.com/auth/dynamic.html";
545
					$port = "";
546
					if ($this->_dnsServer) {
547
						$server = $this->_dnsServer;
548
					}
549
					if ($this->_dnsPort) {
550
						$port = ":" . $this->_dnsPort;
551
					}
552
					curl_setopt($ch, CURLOPT_URL, "{$server}{$port}?host=" . $this->_dnsHost . '&dnsto=' . $this->_dnsIP);
553
					break;
554
				case 'dyns':
555
					$needsIP = FALSE;
556
					$server = "http://www.dyns.net/postscript011.php";
557
					$port = "";
558
					if ($this->_dnsServer) {
559
						$server = $this->_dnsServer;
560
					}
561
					if ($this->_dnsPort) {
562
						$port = ":" . $this->_dnsPort;
563
					}
564
					curl_setopt($ch, CURLOPT_URL, $server . $port . '?username=' . urlencode($this->_dnsUser) . '&password=' . $this->_dnsPass . '&host=' . $this->_dnsHost);
565
					break;
566
				case 'ods':
567
					$needsIP = FALSE;
568
					$misc_errno = 0;
569
					$misc_error = "";
570
					$server = "ods.org";
571
					$port = "";
572
					if ($this->_dnsServer) {
573
						$server = $this->_dnsServer;
574
					}
575
					if ($this->_dnsPort) {
576
						$port = ":" . $this->_dnsPort;
577
					}
578
					$this->con['socket'] = fsockopen("{$server}{$port}", "7070", $misc_errno, $misc_error, 30);
579
					/* Check that we have connected */
580
					if (!$this->con['socket']) {
581
						print "error! could not connect.";
582
						break;
583
					}
584
					/* Here is the loop. Read the incoming data (from the socket connection) */
585
					while (!feof($this->con['socket'])) {
586
						$this->con['buffer']['all'] = trim(fgets($this->con['socket'], 4096));
587
						$code = substr($this->con['buffer']['all'], 0, 3);
588
						sleep(1);
589
						switch ($code) {
590
							case 100:
591
								fputs($this->con['socket'], "LOGIN ".$this->_dnsUser." ".$this->_dnsPass."\n");
592
								break;
593
							case 225:
594
								fputs($this->con['socket'], "DELRR ".$this->_dnsHost." A\n");
595
								break;
596
							case 901:
597
								fputs($this->con['socket'], "ADDRR ".$this->_dnsHost." A ".$this->_dnsIP."\n");
598
								break;
599
							case 795:
600
								fputs($this->con['socket'], "QUIT\n");
601
								break;
602
						}
603
					}
604
					$this->_checkStatus(0, $code);
605
					break;
606
				case 'freedns':
607
				case 'freedns-v6':
608
					$needIP = TRUE;
609
					curl_setopt($ch, CURLOPT_URL, 'https://freedns.afraid.org/dynamic/update.php?' . $this->_dnsPass . '&address=' . $this->_dnsIP);
610
					break;
611
				case 'dnsexit':
612
					$needsIP = TRUE;
613
					curl_setopt($ch, CURLOPT_URL, 'https://www.dnsexit.com/RemoteUpdate.sv?login='.$this->_dnsUser. '&password='.$this->_dnsPass.'&host='.$this->_dnsHost.'&myip='.$this->_dnsIP);
614
					break;
615
				case 'loopia':
616
					$needsIP = TRUE;
617
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
618
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
619
					curl_setopt($ch, CURLOPT_URL, 'https://dns.loopia.se/XDynDNSServer/XDynDNS.php?hostname='.$this->_dnsHost.'&myip='.$this->_dnsIP.'&wildcard='.$this->_dnsWildcard);
620
					break;
621
				case 'opendns':
622
					$needsIP = FALSE;
623
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
624
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
625
					$server = "https://updates.opendns.com/nic/update?hostname=". $this->_dnsHost;
626
					$port = "";
627
					if ($this->_dnsServer) {
628
						$server = $this->_dnsServer;
629
					}
630
					if ($this->_dnsPort) {
631
						$port = ":" . $this->_dnsPort;
632
					}
633
					curl_setopt($ch, CURLOPT_URL, $server .$port);
634
					break;
635

    
636
				case 'staticcling':
637
					$needsIP = FALSE;
638
					curl_setopt($ch, CURLOPT_URL, 'https://www.staticcling.org/update.html?login='.$this->_dnsUser.'&pass='.$this->_dnsPass);
639
					break;
640
				case 'dnsomatic':
641
					/* Example syntax
642
						https://username:password@updates.dnsomatic.com/nic/update?hostname=yourhostname&myip=ipaddress&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG
643
					*/
644
					$needsIP = FALSE;
645
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
646
						$this->_dnsWildcard = "ON";
647
					}
648
					/*
649
					Reference: https://www.dnsomatic.com/wiki/api
650
						DNS-O-Matic usernames are 3-25 characters.
651
						DNS-O-Matic passwords are 6-20 characters.
652
						All ASCII letters and numbers accepted.
653
						Dots, dashes, and underscores allowed, but not at the beginning or end of the string.
654
					Required: "rawurlencode" http://www.php.net/manual/en/function.rawurlencode.php
655
						Encodes the given string according to RFC 3986.
656
					*/
657
					$server = "https://" . rawurlencode($this->_dnsUser) . ":" . rawurlencode($this->_dnsPass) . "@updates.dnsomatic.com/nic/update?hostname=";
658
					if ($this->_dnsServer) {
659
						$server = $this->_dnsServer;
660
					}
661
					if ($this->_dnsPort) {
662
						$port = ":" . $this->_dnsPort;
663
					}
664
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NOCHG');
665
					break;
666
				case 'namecheap':
667
					/* Example:
668
						https://dynamicdns.park-your-domain.com/update?host=[host_name]&domain=[domain.com]&password=[domain_password]&ip=[your_ip]
669
					*/
670
					$needsIP = FALSE;
671
					$dnspass = trim($this->_dnsPass);
672
					$server = "https://dynamicdns.park-your-domain.com/update?host={$this->_dnsHost}&domain={$this->_dnsDomain}&password={$dnspass}&ip={$this->_dnsIP}";
673
					curl_setopt($ch, CURLOPT_URL, $server);
674
					break;
675
				case 'duiadns':
676
				case 'duiadns-v6':
677
					$needsIP = FALSE;
678
					$server = "https://ipv4.duiadns.net/dyndns.duia?";
679
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
680
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
681
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
682
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
683
					break;
684
				case 'he-net':
685
				case 'he-net-v6':
686
					$needsIP = FALSE;
687
					$server = "https://dyn.dns.he.net/nic/update?";
688
					curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
689
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
690
					curl_setopt($ch, CURLOPT_URL, $server . 'hostname=' . $this->_dnsHost . '&password=' . $this->_dnsPass . '&myip=' . $this->_dnsIP);
691
					break;
692
				case 'he-net-tunnelbroker':
693
					$needsIP = FALSE;
694
					$server = "https://ipv4.tunnelbroker.net/ipv4_end.php?";
695
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser . ':' . $this->_dnsPass);
696
					curl_setopt($ch, CURLOPT_URL, $server . 'tid=' . $this->_dnsHost);
697
					break;
698
				case 'selfhost':
699
					$needsIP = FALSE;
700
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") {
701
						$this->_dnsWildcard = "ON";
702
					}
703
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
704
					$server = "https://carol.selfhost.de/nic/update";
705
					$port = "";
706
					if ($this->_dnsServer) {
707
						$server = $this->_dnsServer;
708
					}
709
					if ($this->_dnsPort) {
710
						$port = ":" . $this->_dnsPort;
711
					}
712
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
713
					break;
714
				case 'route53':
715
					require_once("r53.class");
716
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
717
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
718
					$xmlreq = $r53->getRequestBody($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
719
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
720
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
721
					if($this->_dnsVerboseLog){
722
						log_error(sprintf("Sending request to: %s", $apiurl));
723
						foreach($httphead as $hv){
724
							log_error(sprintf("Header: %s", $hv));
725
						}
726
						log_error(sprintf("XMLPOST: %s", $xmlreq));
727
					}
728
					curl_setopt($ch, CURLOPT_URL, $apiurl);
729
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
730
					break;
731
				case 'route53-v6':
732
					require_once("r53.class");
733
					$r53 = new Route53($this->_dnsUser, $this->_dnsPass);
734
					$apiurl = $r53->getApiUrl($this->_dnsZoneID);
735
					$xmlreq = $r53->getRequestBodyV6($this->_dnsHost, $this->_dnsIP, $this->_dnsTTL);
736
					$httphead = $r53->getHttpPostHeaders($this->_dnsZoneID, "us-east-1", hash("sha256",$xmlreq));
737
					curl_setopt($ch, CURLOPT_HTTPHEADER, $httphead);
738
					if($this->_dnsVerboseLog){
739
						log_error(sprintf("Sending request to: %s", $apiurl));
740
						foreach($httphead as $hv){
741
							log_error(sprintf("Header: %s", $hv));
742
						}
743
						log_error(sprintf("XMLPOST: %s", $xmlreq));
744
					}
745
					curl_setopt($ch, CURLOPT_URL, $apiurl);
746
					curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlreq);
747
					break;
748
				case 'custom':
749
				case 'custom-v6':
750
					if (strstr($this->dnsUpdateURL, "%IP%")) {$needsIP = TRUE;} else {$needsIP = FALSE;}
751
					if ($this->_dnsUser != '') {
752
						if ($this->_curlIpresolveV4) {
753
							curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
754
						}
755
						if ($this->_curlSslVerifypeer) {
756
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
757
						} else {
758
							curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
759
						}
760
						curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
761
					}
762
					$server = str_replace("%IP%", $this->_dnsIP, $this->_dnsUpdateURL);
763
					if ($this->_dnsVerboseLog) {
764
						log_error(sprintf(gettext("Sending request to: %s"), $server));
765
					}
766
					curl_setopt($ch, CURLOPT_URL, $server);
767
					break;
768
				case 'cloudflare-v6':
769
				case 'cloudflare':
770
					$this->_FQDN = ltrim($this->_FQDN, '@.');
771
					$isv6 = ($this->_dnsService === 'cloudflare-v6');
772
					$recordType = $isv6 ? "AAAA" : "A";
773
					$needsIP = TRUE;
774
					$dnsServer ='api.cloudflare.com';
775
					$dnsHost = str_replace(' ', '', $this->_dnsHost);
776

    
777
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
778
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
779
						'X-Auth-Email: '.$this->_dnsUser.'',
780
						'X-Auth-Key: '.$this->_dnsPass.'',
781
						'Content-Type: application/json'
782
					));
783

    
784
					// Get zone ID
785
					$getZoneId = "https://{$dnsServer}/client/v4/zones/?name={$this->_dnsDomain}";
786
					curl_setopt($ch, CURLOPT_URL, $getZoneId);
787
					$output = json_decode(curl_exec($ch));
788
					$zone = $output->result[0]->id;
789
					if ($zone) { // If zone ID was found get host ID
790
						$getHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records?name={$this->_FQDN}&type={$recordType}";
791
						curl_setopt($ch, CURLOPT_URL, $getHostId);
792
						$output = json_decode(curl_exec($ch));
793
						$host = $output->result[0]->id;
794
						if ($host) { // If host ID was found update host
795
							$hostData = array(
796
								"content" => "{$this->_dnsIP}",
797
								"type" => "{$recordType}",
798
								"proxied" => $this->_dnsProxied,
799
								"name" => "{$this->_dnsHost}"
800
							);
801
							$data_json = json_encode($hostData);
802
							$updateHostId = "https://{$dnsServer}/client/v4/zones/{$zone}/dns_records/{$host}";
803
							curl_setopt($ch, CURLOPT_URL, $updateHostId);
804
							curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
805
							curl_setopt($ch, CURLOPT_POSTFIELDS, $data_json);
806
						}
807
					}
808
					break;
809
				case 'eurodns':
810
					$needsIP = TRUE;
811
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
812
					$server = "https://update.eurodyndns.org/update/";
813
					$port = "";
814
					if ($this->_dnsPort) {
815
						$port = ":" . $this->_dnsPort;
816
					}
817
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
818
					break;
819
				case 'gratisdns':
820
					$needsIP = TRUE;
821
					$server = "https://ssl.gratisdns.dk/ddns.phtml";
822
					curl_setopt($ch, CURLOPT_URL, $server . '?u=' . $this->_dnsUser . '&p=' . $this->_dnsPass . '&h=' . $this->_dnsHost . '&d=' . $this->_dnsDomain . '&i=' . $this->_dnsIP);
823
					break;
824
				case 'ovh-dynhost':
825
					$needsIP = FALSE;
826
					if (isset($this->_dnsWildcard) && $this->_dnsWildcard != "OFF") $this->_dnsWildcard = "ON";
827
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
828
					$server = "https://www.ovh.com/nic/update";
829
					$port = "";
830
					if ($this->_dnsServer) {
831
						$server = $this->_dnsServer;
832
					}
833
					if ($this->_dnsPort) {
834
						$port = ":" . $this->_dnsPort;
835
					}
836
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?system=dyndns&hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP . '&wildcard='.$this->_dnsWildcard . '&mx=' . $this->_dnsMX . '&backmx=NO');
837
					break;
838
				case 'citynetwork':
839
					$needsIP = TRUE;
840
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
841
					$server = 'https://dyndns.citynetwork.se/nic/update';
842
					$port = "";
843
					if ($this->_dnsServer) {
844
						$server = $this->_dnsServer;
845
					}
846
					if ($this->_dnsPort) {
847
						$port = ":" . $this->_dnsPort;
848
					}
849
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
850
					break;
851
				case 'dnsimple':
852
					/* Uses DNSimple's v2 REST API
853
					   Requires the Account ID as the username (found in the URL when pull up the domain)
854
					   And an API Token for the password (generated in the User Settings -> API tokens area of the website)
855
					   Piggybacks on Route 53's ZoneID field for the DNSimple record ID to update
856
					   The DNS record MUST exist before it can update since it performs a PATCH operation
857
					   Data sent as JSON over HTTPS */
858
					$needsIP = TRUE;
859
					$server = 'https://api.dnsimple.com/v2/';
860
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
861
					curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json', 'Content-Type: application/json', 'Authorization: Bearer ' . $this->_dnsPass));
862
					curl_setopt($ch, CURLOPT_URL, $server . $this->_dnsUser . '/zones/' . $this->_dnsHost . '/records/' . $this->_dnsZoneID);
863
					curl_setopt($ch, CURLOPT_POSTFIELDS, '{"content":"' . $this->_dnsIP . '","ttl":"' . $this->_dnsTTL . '"}');
864
					break;
865
				case 'godaddy':
866
				case 'godaddy-v6':
867
					/* Uses GoDaddy's REST API
868
					   Requires username and Account API sso-key passed in header
869
					   Data sent as JSON */
870
					$needsIP = TRUE;
871
					$server = 'https://api.godaddy.com/v1/domains/';
872
					$recordType = $this->_useIPv6 ? "AAAA" : "A";
873
					$url = $server  . $this->_dnsDomain . '/records/' . $recordType . '/' . $this->_dnsHost;
874
					$jsondata = '[{"data":"' . $this->_dnsIP . '","ttl":' . $this->_dnsTTL . '}]';
875
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
876
					curl_setopt($ch, CURLOPT_HTTPHEADER, array(
877
						'Accept: application/json',
878
						'Content-Type: application/json',
879
						'Authorization: sso-key ' . $this->_dnsUser . ':' . $this->_dnsPass
880
					));
881
					curl_setopt($ch, CURLOPT_URL, $url);
882
					curl_setopt($ch, CURLOPT_POSTFIELDS, $jsondata);
883
					break;
884
				case 'googledomains':
885
					$needsIP = FALSE;
886
					$post_data['username:password'] = $this->_dnsUser . ':' . $this->_dnsPass;
887
					$post_data['hostname'] = $this->_dnsHost;
888
					$post_data['myip'] = $this->_dnsIP;
889
					$post_data['offline'] = 'no';
890
					$server = "https://domains.google.com/nic/update";
891
					$port = "";
892
					curl_setopt($ch, CURLOPT_URL, 'https://domains.google.com/nic/update');
893
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
894
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
895
					break;
896
				case 'dnsmadeeasy':
897
					$needsIP = TRUE;
898
					$server = "https://cp.dnsmadeeasy.com/servlet/updateip";
899
					curl_setopt($ch, CURLOPT_URL, $server . '?username=' . $this->_dnsUser . '&password=' . $this->_dnsPass . '&id=' . $this->_dnsHost . '&ip=' . $this->_dnsIP);
900
					break;
901
				case 'spdyn':
902
				case 'spdyn-v6':
903
					$needsIP = FALSE;
904
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
905
					$server = "https://update.spdyn.de/nic/update";
906
					$port = "";
907
					if ($this->_dnsServer) {
908
						$server = $this->_dnsServer;
909
					}
910
					if ($this->_dnsPort) {
911
						$port = ":" . $this->_dnsPort;
912
					}
913
					curl_setopt($ch, CURLOPT_URL, $server .$port . '?hostname=' . $this->_dnsHost . '&myip=' . $this->_dnsIP);
914
					break;
915
				case 'all-inkl':
916
					$needsIP = FALSE;
917
					$server = 'https://dyndns.kasserver.com/';
918
					curl_setopt($ch, CURLOPT_USERPWD, $this->_dnsUser.':'.$this->_dnsPass);
919
					curl_setopt($ch, CURLOPT_URL, $server . 'myip=' . $this->_dnsIP);
920
					break;
921
				case 'hover':
922
					$needsIP = FALSE;
923
					$port = "";
924
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
925
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
926

    
927
					//step 1: login to API
928
					$post_data['username'] = $this->_dnsUser;
929
					$post_data['password'] = $this->_dnsPass;
930
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
931
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/login");
932
					curl_setopt($ch, CURLOPT_HEADER, 1); //return the full headers to extract the cookies
933
					$output = curl_exec($ch);
934

    
935
					//extract the cookies
936
					preg_match_all("/^Set-cookie: (.*?);/ism", $output, $cookies);
937
					if( count($cookies[1]) > 0 ){
938
						$cookie_data = implode("; ",$cookies[1]);
939
					}
940

    
941
					//step 2: find the id of the A record
942
					$post_data = null;
943
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
944
					curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
945
					curl_setopt($ch, CURLOPT_HEADER, 0);
946
					curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns");
947
					curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
948

    
949
					$output = curl_exec($ch);
950
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$this->_dnsDomain}.*?entries.*?{\"id\":\"([^\"]*?)\",\"name\":\"{$this->_dnsHost}\".*?\$/", $output, $hostID);
951
					$hostID = $hostID[1];
952
					preg_match("/^{\"succeeded\":true.*?domain_name\":\"{$this->_dnsDomain}.*?entries.*?{[^\}]*?\"name\":\"{$this->_dnsHost}\".*?content\":\"([^\"]*?)\".*?\$/", $output, $hostIP);
953
					$hostIP = $hostIP[1];
954

    
955
					//step 3: update the IP
956
					if ($hostID) {
957
						curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
958
						curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
959
						curl_setopt($ch, CURLOPT_COOKIE, $cookie_data);
960
						$post_data['content'] = $this->_dnsIP;
961
						curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
962
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
963
						curl_setopt($ch, CURLOPT_URL, "https://www.hover.com/api/dns/{$hostID}");
964
						log_error("HostID:{$hostID}, OldIP:{$hostIP}");
965
					}
966
					break;
967
				case 'dreamhost':
968
				case 'dreamhost-v6':
969
					$needsIP = TRUE;
970
					$isv6 = ($this->_dnsService === 'dreamhost-v6');
971
					$server = 'https://api.dreamhost.com/';
972
					$post_data['key'] = $this->_dnsPass;
973
					$post_data['unique_id'] = uniqid($this->_dnsHost);
974
					$post_data['cmd'] = 'dns-add_record';
975
					$post_data['format'] = 'json';
976
					$post_data['value'] = $this->_dnsIP;
977
					$post_data['record'] = $this->_dnsHost;
978
					$post_data['type'] = $isv6 ? 'AAAA' : 'A';
979
					$post_data['comment'] = "Updated by pfSense:$this->_dnsUser on ".date('c');
980
					$port = "";
981
					if ($this->_dnsServer) {
982
						$server = $this->_dnsServer;
983
					}
984
					if ($this->_dnsPort) {
985
						$port = ":" . $this->_dnsPort;
986
					}
987
					curl_setopt($ch, CURLOPT_URL, $server . $port);
988
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
989
					break;
990
				case 'digitalocean':
991
					// Get record ID
992
					$server = 'https://api.digitalocean.com/v2/domains/';
993
					$url = $server . $this->_dnsDomain . '/records';
994
					curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: Bearer {$this->_dnsPass}"));
995
					curl_setopt($ch, CURLOPT_URL, $url);
996
					$output = json_decode(curl_exec($ch));
997
					if (!is_array($output->domain_records)) {
998
						$output->domain_records = array();
999
					}
1000
					foreach($output->domain_records as $dnsRecord) {
1001
						// NS records are named @ in DO's API, so check type as well 
1002
						// https://redmine.pfsense.org/issues/9171
1003
						if ($this->_dnsHost == $dnsRecord->name && $dnsRecord->type == 'A') {
1004
							$recordID = $dnsRecord->id;
1005
							break;
1006
						}
1007
					}
1008

    
1009
					// Create/update record
1010
					if ($recordID == null) {
1011
						$url = $server . $this->_dnsDomain . '/records';
1012
					} else {
1013
						$url = $server . $this->_dnsDomain . '/records/' . $recordID;
1014
						curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
1015
					}
1016
					$post_data['type'] = 'A';
1017
					$post_data['ttl'] = $this->_dnsTTL;
1018
					$post_data['name'] = $this->_dnsHost;
1019
					$post_data['data'] = $this->_dnsIP;
1020
					curl_setopt($ch, CURLOPT_URL, $url);
1021
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1022
					break;
1023
				case 'cloudns':
1024
					/* Uses ClouDNS REST API
1025
					   Requires auth-id or sub-auth-id or sub-auth-user */
1026
					// Step 1: Find the Record ID
1027
					$url = 'https://api.cloudns.net/dns/records.json';
1028
					$post_data['auth-id'] = $this->_dnsUser;
1029
					$post_data['auth-password'] = $this->_dnsPass;
1030
					$post_data['domain-name'] = $this->_dnsDomain;
1031
					$post_data['host'] = $this->_dnsHost;
1032
					$post_data['type'] = 'a';
1033
					curl_setopt($ch, CURLOPT_URL, $url);
1034
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1035
					$output = json_decode(curl_exec($ch));
1036
					$recordID = key(get_object_vars($output));
1037

    
1038
					// Step 2: Set the record
1039
					$needsIP = TRUE;
1040
					$url = 'https://api.cloudns.net/dns/mod-record.json';
1041
					$post_data = array();
1042
					$post_data['auth-id'] = $this->_dnsUser;
1043
					$post_data['auth-password'] = $this->_dnsPass;
1044
					$post_data['domain-name'] = $this->_dnsDomain;
1045
					$post_data['record-id'] = $recordID;
1046
					$post_data['host'] = $this->_dnsHost;
1047
					$post_data['record'] = $this->_dnsIP;
1048
					$post_data['ttl'] = $this->_dnsTTL;
1049
					curl_setopt($ch, CURLOPT_URL, $url);
1050
					curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1051
					break;
1052
			case 'azurev6':
1053
			case 'azure':
1054
				$hostname = "{$this->_dnsHost}";
1055
				$resourceid = trim($this->_dnsZoneID);
1056
				$app_id = $this->_dnsUser;
1057
				$client_secret = $this->_dnsPass;
1058
				$newip = $this->_dnsIP;
1059
				$newttl = $this->_dnsTTL;
1060
					// ensure resourceid starts with / and has no trailing /
1061
				$resourceid = '/' . trim($resourceid, '/');
1062
					// extract subscription id from resource id
1063
				preg_match('/\\/subscriptions\\/(?<sid>[^\\/]*)/', $resourceid, $result);
1064
				$subscriptionid = isset($result['sid']) ? $result['sid'] : '';
1065
				if (isset($result['sid'])) {
1066
					$subscriptionid = $result['sid'];
1067
				} else {
1068
					log_error("Azure subscription id not found in resource id");
1069
					return false;
1070
				}
1071
					// find tenant id from subscription id
1072
				curl_setopt($ch, CURLOPT_URL, "https://management.azure.com/subscriptions/" . $subscriptionid . "?api-version=2016-09-01");
1073
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1074
				curl_setopt($ch, CURLOPT_HEADER, 1);
1075
				curl_setopt($ch, CURLOPT_NOBODY, 1);
1076
				$output = curl_exec($ch);
1077
				$pattern = '/Bearer authorization_uri="https:\\/\\/login.windows.net\\/(?<tid>[^"]*)/i';
1078
				preg_match($pattern, $output, $result);
1079
				if (isset($result['tid'])) {
1080
					$tenantid = $result['tid'];
1081
				} else {
1082
					log_error("Tenant ID not found");
1083
					return false;
1084
				}
1085
					// get an bearer token
1086
				curl_setopt($ch, CURLOPT_URL, "https://login.microsoftonline.com/" . $tenantid . "/oauth2/token");
1087
				curl_setopt($ch, CURLOPT_POST, 1);
1088
				$body = "resource=" . urlencode("https://management.core.windows.net/") . "&grant_type=client_credentials&client_id=" . $app_id . "&client_secret=" . urlencode($client_secret);
1089
				curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1090
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1091
				$server_output = curl_exec($ch);
1092
				$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1093
				preg_match("/\"access_token\":\"(?<tok>[^\"]*)\"/", $server_output, $result);
1094
				if (isset($result['tok'])) {
1095
					$bearertoken = $result['tok'];
1096
				} else {
1097
					log_error("no valid bearer token");
1098
					return false;
1099
				}
1100
					// Update the DNS record
1101
				if ($this->_useIPv6) {
1102
					$url = "https://management.azure.com" . $resourceid . "/AAAA/" . $hostname . "?api-version=2017-09-01";
1103
					$body = '{"properties":{"TTL":"' . $newttl . '", "AaaaRecords":[{"ipv6Address":"' . $newip . '"}]}}';
1104
				} else {
1105
					$url = "https://management.azure.com" . $resourceid . "/A/" . $hostname . "?api-version=2017-09-01";
1106
					$body = '{"properties":{"TTL":"' . $newttl . '", "ARecords":[{"ipv4Address":"' . $newip . '"}]}}';
1107
				}
1108
				$request_headers = array();
1109
				$request_headers[] = 'Accept: application/json';
1110
				$request_headers[] = 'Authorization: Bearer ' . $bearertoken;
1111
				$request_headers[] = 'Content-Type: application/json';
1112
				curl_setopt($ch, CURLOPT_URL, $url);
1113
				curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1114
				curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
1115
				curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
1116
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1117
				curl_setopt($ch, CURLOPT_HEADER, 1);
1118
				curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
1119
				break;
1120

    
1121
			default:
1122
				break;
1123
			}
1124
			if ($this->_dnsService != 'ods') {
1125
				curl_setopt($ch, CURLOPT_HEADER, 1);
1126
				$response = curl_exec($ch);
1127
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1128
				$header = substr($response, 0, $header_size);
1129
				$data = substr($response, $header_size);
1130
				$this->_checkStatus($ch, $data, $header);
1131
				@curl_close($ch);
1132
			}
1133
		}
1134

    
1135
		/**
1136
		 * Private Function (added 23 Feb 17)
1137
		 *   Send Removal To Selected Service.
1138
		 *
1139
		 *   Some services do not perform an inplace upgrade.  If they do not then the solution
1140
		 *   is to remove the existing record and add a new record.
1141
		 *
1142
		 * @param unknown $existing_ip If required, an existing IP address for the record.
1143
		 */
1144
		function _remove($existing_ip = NULL) {
1145
			$remove_allowed = false;
1146
			if ($this->_dnsVerboseLog) {
1147
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _remove() starting.'), $this->_dnsService, $this->_FQDN));
1148
			}
1149

    
1150
			if (strstr($this->_dnsRequestIf, "_vip")) {
1151
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1152
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1153
			} else {
1154
				$realparentif = $this->_dnsRequestIf;
1155
			}
1156

    
1157
			$ch = curl_init();
1158

    
1159
			if ($this->_useIPv6 == false) {
1160
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1161
			}
1162

    
1163
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1164
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1165
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1166
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1167

    
1168
			switch ($this->_dnsService) {
1169
			case 'dreamhost':
1170
			case 'dreamhost-v6':
1171
				$server = 'https://api.dreamhost.com/';
1172
				$post_data['key'] = $this->_dnsPass;
1173
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1174
				$post_data['cmd'] = 'dns-remove_record';
1175
				$post_data['format'] = 'json';
1176
				$post_data['value'] = $existing_ip;
1177
				$post_data['record'] = $this->_dnsHost;
1178
				$isv6 = ($this->_dnsService === 'dreamhost-v6');
1179
				$post_data['type'] = $isv6 ? 'AAAA' : 'A';
1180
				$port = "";
1181
				if ($this->_dnsServer) {
1182
					$server = $this->_dnsServer;
1183
				}
1184
				if ($this->_dnsPort) {
1185
					$port = ":" . $this->_dnsPort;
1186
				}
1187
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1188
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1189
				$remove_allowed = true;
1190
				break;
1191
			default:
1192
				break;
1193
			}
1194
			if ($remove_allowed) {
1195
				curl_setopt($ch, CURLOPT_HEADER, 1);
1196
				$response = curl_exec($ch);
1197
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1198
				$header = substr($response, 0, $header_size);
1199
				$data = substr($response, $header_size);
1200
				$this->_checkStatus($ch, $data, $header);
1201
				@curl_close($ch);
1202
			}
1203
		}
1204

    
1205
		/**
1206
		 * Private Function (added 23 Feb 17)
1207
		 * Retrieves current DNS records from an external API source.
1208
		 *
1209
		 * Some services cannot perform new operations without the caller
1210
		 * providing existing record information.
1211
		 */
1212
		function _lookup_current() {
1213
			$lookup_allowed = false;
1214
			if ($this->_dnsVerboseLog) {
1215
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _listCurrent() starting.'), $this->_dnsService, $this->_FQDN));
1216
			}
1217

    
1218
			if (strstr($this->_dnsRequestIf, "_vip")) {
1219
				$parentif = get_configured_vip_interface($this->_dnsRequestIf);
1220
				$realparentif = convert_friendly_interface_to_real_interface_name($parentif);
1221
			} else {
1222
				$realparentif = $this->_dnsRequestIf;
1223
			}
1224

    
1225
			$ch = curl_init();
1226

    
1227
			if ($this->_useIPv6 == false) {
1228
				curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1229
			}
1230

    
1231
			curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
1232
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1233
			curl_setopt($ch, CURLOPT_INTERFACE, 'if!' . $realparentif);
1234
			curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
1235

    
1236
			switch ($this->_dnsService) {
1237
			case 'dreamhost':
1238
			case 'dreamhost-v6':
1239
				$server = 'https://api.dreamhost.com/';
1240
				$post_data['key'] = $this->_dnsPass;
1241
				$post_data['unique_id'] = uniqid($this->_dnsHost);
1242
				$post_data['cmd'] = 'dns-list_records';
1243
				$post_data['format'] = 'json';
1244
				$port = "";
1245
				if ($this->_dnsServer) {
1246
					$server = $this->_dnsServer;
1247
				}
1248
				if ($this->_dnsPort) {
1249
					$port = ":" . $this->_dnsPort;
1250
				}
1251
				curl_setopt($ch, CURLOPT_URL, $server . $port);
1252
				curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
1253
				$lookup_allowed = true;
1254
				break;
1255
			default:
1256
				break;
1257
			}
1258
			if ($lookup_allowed) {
1259
				curl_setopt($ch, CURLOPT_HEADER, 1);
1260
				$response = curl_exec($ch);
1261
				$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1262
				$header = substr($response, 0, $header_size);
1263
				$data = substr($response, $header_size);
1264
				$this->_checkLookupStatus($ch, $data, $header);
1265
				@curl_close($ch);
1266
			}
1267
		}
1268

    
1269
		/*
1270
		 * Private Function (added 23 Feb 17)
1271
		 *   Retrieve Lookup Status from the provided data and/or header
1272
		 */
1273
		function _checkLookupStatus($ch, $data, $header) {
1274
			if ($this->_dnsVerboseLog) {
1275
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() starting.'), $this->_dnsService, $this->_FQDN));
1276
			}
1277
			$success_str = "(" . gettext("Success") . ") ";
1278
			$error_str = "(" . gettext("Error") . ") ";
1279
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1280

    
1281
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1282
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1283
				log_error($status);
1284
				$this->status = $status;
1285
				return;
1286
			}
1287
			switch ($this->_dnsService) {
1288
			case 'dreamhost':
1289
			case 'dreamhost-v6':
1290
				$result = json_decode($data,true);
1291
				if($result["result"] != "success") {
1292
					log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1293
					$this->_debug($data);
1294
					return;
1295
				} else {
1296
					foreach($result["data"] as $key => $row) {
1297
						if($row["record"] == $this->_dnsHost &&
1298
								(($row["type"] == "A" && !$this->_useIPv6)
1299
										|| ($row["type"] == "AAAA" && $this->_useIPv6)
1300
								)) {
1301
							if($row["editable"] == 0) {
1302
								log_error($status_intro . "host " . $this->_dnsHost . " is not editable.");
1303
								continue;
1304
							}
1305
							$this->_existingRecords[]=array("record"=>$row["type"], "type"=>$row["type"], "existing_val"=>$row["value"]);
1306
						}
1307
					}
1308
				}
1309
				if (!is_array($this->_existingRecords)){
1310
					if ($this->_dnsVerboseLog) {
1311
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkLookupStatus() ending.  No matching records found.'), $this->_dnsService, $this->_FQDN));
1312
					}
1313
				}
1314
				break;
1315
			default:
1316
				break;
1317
			}
1318
		}
1319

    
1320
		/*
1321
		 * Private Function (added 12 July 2005) [beta]
1322
		 *   Retrieve Update Status
1323
		 */
1324
		function _checkStatus($ch, $data, $header) {
1325
			if ($this->_dnsVerboseLog) {
1326
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkStatus() starting.'), $this->_dnsService, $this->_FQDN));
1327
			}
1328
			$successful_update = false;
1329
			$success_str = "(" . gettext("Success") . ") ";
1330
			$error_str = "(" . gettext("Error") . ") ";
1331
			$status_intro = "phpDynDNS ({$this->_dnsHost}): ";
1332

    
1333
			if ($this->_dnsService != 'ods' && @curl_error($ch)) {
1334
				$status = gettext("Curl error occurred:") . " " . curl_error($ch);
1335
				log_error($status);
1336
				$this->status = $status;
1337
				return;
1338
			}
1339
			switch ($this->_dnsService) {
1340
				case 'glesys':
1341
					$status_intro = "GleSYS ({$this->_dnsHost}): ";
1342
					if (preg_match('/Record updated/i', $data)) {
1343
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1344
						$successful_update = true;
1345
					} else {
1346
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1347
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1348
						$this->_debug($data);
1349
					}
1350
					break;
1351
				case 'dnsomatic':
1352
					$status_intro = "DNS-O-Matic ({$this->_dnsHost}): ";
1353
					if (preg_match('/badauth/i', $data)) {
1354
						$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.");
1355
					} else if (preg_match('/notfqdn /i', $data)) {
1356
						$status = $status_intro . gettext("The hostname specified is not a fully-qualified domain name. If no hostnames included, notfqdn will be returned once.");
1357
					} else if (preg_match('/nohost/i', $data)) {
1358
						$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.");
1359
					} else if (preg_match('/numhost/i', $data)) {
1360
						$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.");
1361
					} else if (preg_match('/abuse/i', $data)) {
1362
						$status = $status_intro . gettext("The hostname is blocked for update abuse.");
1363
					} else if (preg_match('/good/i', $data)) {
1364
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1365
						$successful_update = true;
1366
					} else if (preg_match('/dnserr/i', $data)) {
1367
						$status = $status_intro . gettext("DNS error encountered. Stop updating for 30 minutes.");
1368
					} else {
1369
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1370
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1371
						$this->_debug($data);
1372
					}
1373
					break;
1374
				case 'citynetwork':
1375
					if (preg_match('/notfqdn/i', $data)) {
1376
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1377
					} else if (preg_match('/nohost/i', $data)) {
1378
						$status = $status_intro . $error_str . gettext("No such host");
1379
					} else if (preg_match('/nochg/i', $data)) {
1380
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1381
						$successful_update = true;
1382
					} else if (preg_match('/good/i', $data)) {
1383
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1384
						$successful_update = true;
1385
					} else if (preg_match('/badauth/i', $data)) {
1386
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1387
					} else {
1388
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1389
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1390
						$this->_debug($data);
1391
					}
1392
					break;
1393
				case 'ovh-dynhost':
1394
				case 'dyndns':
1395
					if (preg_match('/notfqdn/i', $data)) {
1396
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1397
					} else if (preg_match('/nochg/i', $data)) {
1398
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1399
						$successful_update = true;
1400
					} else if (preg_match('/good/i', $data)) {
1401
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1402
						$successful_update = true;
1403
					} else if (preg_match('/noauth/i', $data)) {
1404
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1405
					} else {
1406
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1407
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1408
						$this->_debug($data);
1409
					}
1410
					break;
1411
				case 'dyndns-static':
1412
					if (preg_match('/notfqdn/i', $data)) {
1413
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1414
					} else if (preg_match('/nochg/i', $data)) {
1415
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1416
						$successful_update = true;
1417
					} else if (preg_match('/good/i', $data)) {
1418
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1419
						$successful_update = true;
1420
					} else if (preg_match('/noauth/i', $data)) {
1421
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1422
					} else {
1423
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1424
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1425
						$this->_debug($data);
1426
					}
1427
					break;
1428
				case 'dyndns-custom':
1429
					if (preg_match('/notfqdn/i', $data)) {
1430
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1431
					} else if (preg_match('/nochg/i', $data)) {
1432
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1433
						$successful_update = true;
1434
					} else if (preg_match('/good/i', $data)) {
1435
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1436
						$successful_update = true;
1437
					} else if (preg_match('/noauth/i', $data)) {
1438
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1439
					} else {
1440
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1441
						log_error($status_intro . gettext("PAYLOAD:") . " {$data}");
1442
						$this->_debug($data);
1443
					}
1444
					break;
1445
				case 'dhs':
1446
					break;
1447
				case 'noip':
1448
				case 'noip-free':
1449
					list($ip, $code) = explode(":", $data);
1450
					switch ($code) {
1451
						case 0:
1452
							$status = $status_intro . $success_str . gettext("IP address is current, no update performed.");
1453
							$successful_update = true;
1454
							break;
1455
						case 1:
1456
							$status = $status_intro . $success_str . gettext("DNS hostname update successful.");
1457
							$successful_update = true;
1458
							break;
1459
						case 2:
1460
							$status = $status_intro . $error_str . gettext("Hostname supplied does not exist.");
1461
							break;
1462
						case 3:
1463
							$status = $status_intro . $error_str . gettext("Invalid Username.");
1464
							break;
1465
						case 4:
1466
							$status = $status_intro . $error_str . gettext("Invalid Password.");
1467
							break;
1468
						case 5:
1469
							$status = $status_intro . $error_str . gettext("Too many updates sent.");
1470
							break;
1471
						case 6:
1472
							$status = $status_intro . $error_str . gettext("Account disabled due to violation of No-IP terms of service.");
1473
							break;
1474
						case 7:
1475
							$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.");
1476
							break;
1477
						case 8:
1478
							$status = $status_intro . $error_str . gettext("Disabled / Locked Hostname.");
1479
							break;
1480
						case 9:
1481
							$status = $status_intro . $error_str . gettext("Host updated is configured as a web redirect and no update was performed.");
1482
							break;
1483
						case 10:
1484
							$status = $status_intro . $error_str . gettext("Group supplied does not exist.");
1485
							break;
1486
						case 11:
1487
							$status = $status_intro . $success_str . gettext("DNS group update is successful.");
1488
							$successful_update = true;
1489
							break;
1490
						case 12:
1491
							$status = $status_intro . $success_str . gettext("DNS group is current, no update performed.");
1492
							$successful_update = true;
1493
							break;
1494
						case 13:
1495
							$status = $status_intro . $error_str . gettext("Update client support not available for supplied hostname or group.");
1496
							break;
1497
						case 14:
1498
							$status = $status_intro . $error_str . gettext("Hostname supplied does not have offline settings configured.");
1499
							break;
1500
						case 99:
1501
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
1502
							break;
1503
						case 100:
1504
							$status = $status_intro . $error_str . gettext("Client disabled. Client should exit and not perform any more updates without user intervention.");
1505
							break;
1506
						default:
1507
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1508
							$this->_debug(gettext("Unknown Response:") . " " . $data);
1509
							break;
1510
					}
1511
					break;
1512
				case 'easydns':
1513
					if (preg_match('/NOACCESS/i', $data)) {
1514
						$status = $status_intro . $error_str . gettext("Authentication Failed: Username and/or Password was Incorrect.");
1515
					} else if (preg_match('/NOSERVICE/i', $data)) {
1516
						$status = $status_intro . $error_str . gettext("No Service: Dynamic DNS Service has been disabled for this domain.");
1517
					} else if (preg_match('/ILLEGAL INPUT/i', $data)) {
1518
						$status = $status_intro . $error_str . gettext("Illegal Input: Self-Explanatory");
1519
					} else if (preg_match('/TOOSOON/i', $data)) {
1520
						$status = $status_intro . $error_str . gettext("Too Soon: Not Enough Time Has Elapsed Since Last Update");
1521
					} else if (preg_match('/NOERROR/i', $data)) {
1522
						$status = $status_intro . $success_str . gettext("IP Updated Successfully!");
1523
						$successful_update = true;
1524
					} else {
1525
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1526
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1527
						$this->_debug($data);
1528
					}
1529
					break;
1530
				case 'hn':
1531
					/* FIXME: add checks */
1532
					break;
1533
				case 'zoneedit':
1534
					if (preg_match('/799/i', $data)) {
1535
						$status = $status_intro . "(" . gettext("Error 799") . ") " . gettext("Update Failed!");
1536
					} else if (preg_match('/700/i', $data)) {
1537
						$status = $status_intro . "(" . gettext("Error 700") . ") " . gettext("Update Failed!");
1538
					} else if (preg_match('/200/i', $data)) {
1539
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1540
						$successful_update = true;
1541
					} else if (preg_match('/201/i', $data)) {
1542
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1543
						$successful_update = true;
1544
					} else {
1545
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1546
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1547
						$this->_debug($data);
1548
					}
1549
					break;
1550
				case 'dyns':
1551
					if (preg_match("/400/i", $data)) {
1552
						$status = $status_intro . $error_str . gettext("Bad Request - The URL was malformed. Required parameters were not provided.");
1553
					} else if (preg_match('/402/i', $data)) {
1554
						$status = $status_intro . $error_str . gettext("Update Too Soon - Attempted to update too quickly since last change.");
1555
					} else if (preg_match('/403/i', $data)) {
1556
						$status = $status_intro . $error_str . gettext("Database Error - There was a server-sided database error.");
1557
					} else if (preg_match('/405/i', $data)) {
1558
						$status = $status_intro . $error_str . sprintf(gettext('Hostname Error - The hostname (%1$s) doesn\'t belong to user (%2$s).'), $this->_dnsHost, $this->_dnsUser);
1559
					} else if (preg_match('/200/i', $data)) {
1560
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1561
						$successful_update = true;
1562
					} else {
1563
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1564
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1565
						$this->_debug($data);
1566
					}
1567
					break;
1568
				case 'ods':
1569
					if (preg_match("/299/i", $data)) {
1570
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1571
						$successful_update = true;
1572
					} else {
1573
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1574
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1575
						$this->_debug($data);
1576
					}
1577
					break;
1578
				case 'freedns':
1579
				case 'freedns-v6':
1580
					if (preg_match("/has not changed./i", $data)) {
1581
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1582
						$successful_update = true;
1583
					} else if (preg_match("/Updated/i", $data)) {
1584
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1585
						$successful_update = true;
1586
					} else {
1587
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1588
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1589
						$this->_debug($data);
1590
					}
1591
					break;
1592
				case 'dnsexit':
1593
					if (preg_match("/is the same/i", $data)) {
1594
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1595
						$successful_update = true;
1596
					} else if (preg_match("/Success/i", $data)) {
1597
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1598
						$successful_update = true;
1599
					} else {
1600
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1601
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1602
						$this->_debug($data);
1603
					}
1604
					break;
1605
				case 'loopia':
1606
					if (preg_match("/nochg/i", $data)) {
1607
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1608
						$successful_update = true;
1609
					} else if (preg_match("/good/i", $data)) {
1610
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
1611
						$successful_update = true;
1612
					} else if (preg_match('/badauth/i', $data)) {
1613
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1614
					} else {
1615
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1616
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1617
						$this->_debug($data);
1618
					}
1619
					break;
1620
				case 'opendns':
1621
					if (preg_match('/badauth/i', $data)) {
1622
						$status = $status_intro . $error_str . gettext("Not a valid username or password!");
1623
					} else if (preg_match('/nohost/i', $data)) {
1624
						$status = $status_intro . $error_str . gettext("Hostname specified does not exist.");
1625
						$successful_update = true;
1626
					} else if (preg_match('/good/i', $data)) {
1627
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1628
						$successful_update = true;
1629
					} else if (preg_match('/yours/i', $data)) {
1630
						$status = $status_intro . $error_str . gettext("Hostname specified exists, but not under the username specified.");
1631
					} else if (preg_match('/abuse/i', $data)) {
1632
						$status = $status_intro . $error_str . gettext("Updating too frequently, considered abuse.");
1633
					} else {
1634
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1635
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1636
						$this->_debug($data);
1637
					}
1638
					break;
1639
				case 'staticcling':
1640
					if (preg_match("/invalid ip/i", $data)) {
1641
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
1642
					} else if (preg_match('/required info missing/i', $data)) {
1643
						$status = $status_intro . $error_str . gettext("Bad Request - Required parameters were not provided.");
1644
					} else if (preg_match('/invalid characters/i', $data)) {
1645
						$status = $status_intro . $error_str . gettext("Bad Request - Illegal characters in either the username or the password.");
1646
					} else if (preg_match('/bad password/i', $data)) {
1647
						$status = $status_intro . $error_str . gettext("Invalid password.");
1648
					} else if (preg_match('/account locked/i', $data)) {
1649
						$status = $status_intro . $error_str . gettext("This account has been administratively locked.");
1650
					} else if (preg_match('/update too frequent/i', $data)) {
1651
						$status = $status_intro . $error_str . gettext("Updating too frequently.");
1652
					} else if (preg_match('/DB error/i', $data)) {
1653
						$status = $status_intro . $error_str . gettext("Server side error.");
1654
					} else if (preg_match('/success/i', $data)) {
1655
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1656
						$successful_update = true;
1657
					} else {
1658
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1659
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1660
						$this->_debug($data);
1661
					}
1662
					break;
1663
				case 'namecheap':
1664
					$tmp = str_replace("^M", "", $data);
1665
					$ncresponse = @xml2array($tmp);
1666
					if (preg_match("/internal server error/i", $data)) {
1667
						$status = $status_intro . $error_str . gettext("Server side error.");
1668
					} else if (preg_match("/request is badly formed/i", $data)) {
1669
						$status = $status_intro . $error_str . gettext("Badly Formed Request (check the settings).");
1670
					} else if ($ncresponse['interface-response']['ErrCount'] === "0") {
1671
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1672
						$successful_update = true;
1673
					} else if (is_numeric($ncresponse['interface-response']['ErrCount']) && ($ncresponse['interface-response']['ErrCount'] > 0)) {
1674
						$status = $status_intro . $error_str . implode(", ", $ncresponse["interface-response"]["errors"]);
1675
						$successful_update = true;
1676
					} else {
1677
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1678
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1679
						$this->_debug($data);
1680
					}
1681
					break;
1682
				case 'duiadns':
1683
				case 'duiadns-v6':
1684
					if (preg_match("/error/i", $data)) {
1685
						$status = $status_intro . $error_str . gettext("Server side error.");
1686
					} else if (preg_match('/nohost/i', $data)) {
1687
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
1688
					} else if (preg_match('/badauth/i', $data)) {
1689
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
1690
					} else if (preg_match('/good/i', $data)) {
1691
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1692
						$successful_update = true;
1693
					} else if (preg_match('/nochg/i', $data)) {
1694
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
1695
						$successful_update = true;
1696
					} else {
1697
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1698
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1699
						$this->_debug($data);
1700
					}
1701
					break;
1702
				case 'he-net':
1703
				case 'he-net-v6':
1704
					if (preg_match("/badip/i", $data)) {
1705
						$status = $status_intro . $error_str . gettext("Bad Request - The IP provided was invalid.");
1706
					} else if (preg_match('/nohost/i', $data)) {
1707
						$status = $status_intro . $error_str . gettext("Bad Request - A hostname was not provided.");
1708
					} else if (preg_match('/badauth/i', $data)) {
1709
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
1710
					} else if (preg_match('/good/i', $data)) {
1711
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1712
						$successful_update = true;
1713
					} else if (preg_match('/nochg/i', $data)) {
1714
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
1715
						$successful_update = true;
1716
					} else {
1717
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1718
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1719
						$this->_debug($data);
1720
					}
1721
					break;
1722
				case 'he-net-tunnelbroker':
1723
					/*
1724
					-ERROR: Missing parameter(s).
1725
					-ERROR: Invalid API key or password
1726
					-ERROR: Tunnel not found
1727
					-ERROR: Another tunnel exists for this IP.
1728
					-ERROR: This tunnel is already associated with this IP address
1729
					+OK: Tunnel endpoint updated to: x.x.x.x
1730
					*/
1731
					if (preg_match("/Missing parameter/i", $data)) {
1732
						$status = $status_intro . $error_str . gettext("Bad Request - Missing/Invalid Parameters.");
1733
					} else if (preg_match('/Tunnel not found/i', $data)) {
1734
						$status = $status_intro . $error_str . gettext("Bad Request - Invalid Tunnel ID.");
1735
					} else if (preg_match('/Invalid API key or password/i', $data)) {
1736
						$status = $status_intro . $error_str . gettext("Invalid username or password.");
1737
					} else if (preg_match('/OK:/i', $data)) {
1738
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1739
						$successful_update = true;
1740
					} else if (preg_match('/This tunnel is already associated with this IP address/i', $data)) {
1741
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
1742
						$successful_update = true;
1743
					} else {
1744
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1745
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1746
						$this->_debug($data);
1747
					}
1748
					break;
1749
				case 'selfhost':
1750
					if (preg_match('/notfqdn/i', $data)) {
1751
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1752
					} else if (preg_match('/nochg/i', $data)) {
1753
						$status = $status_intro . $success_str . gettext("No Change In IP Address.");
1754
						$successful_update = true;
1755
					} else if (preg_match('/good/i', $data)) {
1756
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1757
						$successful_update = true;
1758
					} else if (preg_match('/noauth/i', $data)) {
1759
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1760
					} else {
1761
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1762
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1763
						$this->_debug($data);
1764
					}
1765
					break;
1766
				case 'route53':
1767
				case 'route53-v6':
1768
					if(preg_match('/ErrorResponse/', $data)){
1769
						$status = $status_intro . $error_str . gettext("Route53 API call failed");
1770
						log_error(sprintf("error message: %s", $data));
1771
						$status_update = false;
1772
					} else {
1773
						$status = $status_intro . $success_str . gettext("IP address changed successfully");
1774
						$successful_update = true;
1775
					}
1776
					break;
1777
				case 'custom':
1778
				case 'custom-v6':
1779
					$successful_update = false;
1780
					if ($this->_dnsResultMatch == "") {
1781
						$successful_update = true;
1782
					} else {
1783
						$this->_dnsResultMatch = str_replace("%IP%", $this->_dnsIP, $this->_dnsResultMatch);
1784
						$matches = preg_split("/(?<!\\\\)\\|/", $this->_dnsResultMatch);
1785
						foreach ($matches as $match) {
1786
							$match= str_replace("\\|", "|", $match);
1787
							if (strcmp($match, trim($data, "\t\n\r")) == 0) {
1788
								$successful_update = true;
1789
							}
1790
						}
1791
						unset ($matches);
1792
					}
1793
					if ($successful_update == true) {
1794
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1795
					} else {
1796
						$status = $status_intro . $error_str . gettext("Result did not match.") . " [" . $data . "]";
1797
					}
1798
					break;
1799
				case 'cloudflare-v6':
1800
				case 'cloudflare':
1801
					$output = json_decode($data);
1802
					if ($output->result->content === $this->_dnsIP) {
1803
						$status = $status_intro . $success_str . sprintf(gettext('%1$s updated to %2$s'), $this->_dnsHost, $this->_dnsIP);
1804
						$successful_update = true;
1805
					} elseif ($output->errors[0]->code === 9103) {
1806
						$status = $status_intro . $error_str . gettext("Invalid Credentials! Don't forget to use API Key for password field with Cloudflare.");
1807
					} elseif (($output->success) && (!$output->result[0]->id)) {
1808
						$status = $status_intro . $error_str . gettext("Zone or Host ID was not found, check the hostname.");
1809
					} else {
1810
						$status = $status_intro . gettext("UNKNOWN ERROR") . " - " . $output->errors[0]->message;
1811
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1812
					}
1813
					break;
1814
				case 'eurodns':
1815
					if (preg_match('/notfqdn/i', $data)) {
1816
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1817
					} else if (preg_match('/nochg/i', $data)) {
1818
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1819
						$successful_update = true;
1820
					} else if (preg_match('/good/i', $data)) {
1821
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1822
						$successful_update = true;
1823
					} else if (preg_match('/badauth/i', $data)) {
1824
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1825
					} else {
1826
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1827
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1828
						$this->_debug($data);
1829
					}
1830
					break;
1831
				case 'gratisdns':
1832
					if (preg_match('/Forkerte værdier/i', $data)) {
1833
						$status = $status_intro . $error_str . gettext("Wrong values - Update could not be completed.");
1834
					} else if (preg_match('/Bruger login: Bruger eksistere ikke/i', $data)) {
1835
						$status = $status_intro . $error_str . gettext("Unknown username - User does not exist.");
1836
					} else if (preg_match('/Bruger login: 1Fejl i kodeord/i', $data)) {
1837
						$status = $status_intro . $error_str . gettext("Wrong password - Remember password is case sensitive.");
1838
					} else if (preg_match('/Domæne kan IKKE administreres af bruger/i', $data)) {
1839
						$status = $status_intro . $error_str . gettext("User unable to administer the selected domain.");
1840
					} else if (preg_match('/OK/i', $data)) {
1841
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1842
						$successful_update = true;
1843
					} else {
1844
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1845
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1846
						$this->_debug($data);
1847
					}
1848
					break;
1849
				case 'digitalocean':
1850
					// Creating new records returns an HTTP 201, updating existing records get 200
1851
					// https://redmine.pfsense.org/issues/9171
1852
					if (preg_match("/HTTP\/2\s20[0,1]/i", $header)) {
1853
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1854
						$successful_update = true;
1855
					} else {
1856
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1857
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1858
						$this->_debug($data);
1859
					}
1860
					break;
1861
				case 'dnsimple':
1862
					/* Responds with HTTP 200 on success.
1863
					   Responds with HTTP 4xx on error.
1864
					   Returns JSON data as body */
1865
;
1866
					if (preg_match("/\s200\sOK/i", $header)) {
1867
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1868
						$successful_update = true;
1869
					} else if (preg_match("/\s4\d\d\s/i", $header)) {
1870
						$arrbody = json_decode($data, true);
1871
						$message = $arrbody['message'] . ".";
1872
						if (isset($arrbody['errors']['content'])) {
1873
							foreach ($arrbody['errors']['content'] as $key => $content) {
1874
								$message .= " " . $content . ".";
1875
							}
1876
						}
1877
						$status = $status_intro . $error_str . $message;
1878
					} else {
1879
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1880
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1881
						$this->_debug($data);
1882
					}
1883
					break;
1884
				case 'godaddy':
1885
				case 'godaddy-v6':
1886
					/* Responds with HTTP 200 on success.
1887
					   Responds with HTTP 4xx or  on error.
1888
					   Returns JSON data as body */
1889
;
1890
					if (preg_match("/\s200\sOK/i", $header)) {
1891
						$status = $status_intro . $success_str . gettext("IP Address Updated Successfully!");
1892
						$successful_update = true;
1893
					} else if (preg_match("/\s4\d\d\s/i", $header)) {
1894
						$arrbody = json_decode($data, true);
1895
						$message = $arrbody['message'] . ".";
1896
						if (isset($arrbody['fields'])) {
1897
							foreach ($arrbody['fields'] as $error) {
1898
								$message .= " " . $error['path'] . ": " . $error['message'] . ".";
1899
							}
1900
						}
1901
						$status = $status_intro . $error_str . $message;
1902
					} else {
1903
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1904
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1905
						$this->_debug($data);
1906
					}
1907
					break;
1908
				case 'googledomains':
1909
					if (preg_match('/notfqdn/i', $data)) {
1910
						$status = $status_intro . $error_str . gettext("Not A FQDN");
1911
					} else if (preg_match('/nochg/i', $data)) {
1912
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1913
						$successful_update = true;
1914
					} else if (preg_match('/good/i', $data)) {
1915
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1916
						$successful_update = true;
1917
					} else if (preg_match('/badauth/i', $data)) {
1918
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1919
					} else if (preg_match('/nohost/i', $data)) {
1920
						$status = $status_intro . $error_str . gettext("Hostname does not exist or DynDNS not enabled");
1921
					} else if (preg_match('/badagent/i', $data)) {
1922
						$status = $status_intro . $error_str . gettext("Bad request");
1923
					} else if (preg_match('/abuse/i', $data)) {
1924
						$status = $status_intro . $error_str . gettext("Dynamic DNS access has been blocked!");
1925
					} else if (preg_match('/911/i', $data)) {
1926
						$status = $status_intro . $error_str . gettext("Error on Google's end, retry in 5 minutes");
1927
					} else {
1928
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1929
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1930
						$this->_debug($data);
1931
					}
1932
					break;
1933
				case 'dnsmadeeasy':
1934
					switch ($data) {
1935
						case 'success':
1936
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1937
							$successful_update = true;
1938
							break;
1939
						case 'error-auth':
1940
							$status = $status_intro . $error_str . gettext("Invalid username or password");
1941
							break;
1942
						case 'error-auth-suspend':
1943
							$status = $status_intro . $error_str . gettext("Account suspended");
1944
							break;
1945
						case 'error-auth-voided':
1946
							$status = $status_intro . $error_str . gettext("Account revoked");
1947
							break;
1948
						case 'error-record-invalid':
1949
							$status = $status_intro . $error_str . gettext("Record does not exist in the system. Unable to update record");
1950
							break;
1951
						case 'error-record-auth':
1952
							$status = $status_intro . $error_str . gettext("User does not have access to this record");
1953
							break;
1954
						case 'error-record-ip-same':
1955
							$status = $status_intro . $success_str . gettext("No Change In IP Address");
1956
							$successful_update = true;
1957
							break;
1958
						case 'error-system':
1959
							$status = $status_intro . $error_str . gettext("General system error recognized by the system");
1960
							break;
1961
						case 'error':
1962
							$status = $status_intro . $error_str . gettext("General system error unrecognized by the system");
1963
							break;
1964
						default:
1965
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1966
							log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1967
							$this->_debug($data);
1968
							break;
1969
					}
1970
					break;
1971
				case 'spdyn':
1972
				case 'spdyn-v6':
1973
					if (preg_match('/notfqdn/i', $data)) {
1974
						$status = $status_intro . $error_str . gettext("Not A FQDN!");
1975
					} else if (preg_match('/nohost/i', $data)) {
1976
						$status = $status_intro . $error_str . gettext("No such host");
1977
					} else if (preg_match('/nochg/i', $data)) {
1978
						$status = $status_intro . $success_str . gettext("No Change In IP Address");
1979
						$successful_update = true;
1980
					} else if (preg_match('/good/i', $data)) {
1981
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1982
						$successful_update = true;
1983
					} else if (preg_match('/badauth/i', $data)) {
1984
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
1985
					} else {
1986
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
1987
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
1988
						$this->_debug($data);
1989
					}
1990
					break;
1991
				case 'all-inkl':
1992
 					if (preg_match('/good\s'.$this->_dnsIP.'/i', $data)) {
1993
							$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
1994
 							$successful_update = true;
1995
 					} else if (preg_match('/good/i', $data)) {
1996
						$status = $status_intro . $error_str . gettext("Result did not match.");
1997
					} else if (preg_match("/\s401\sUnauthorized/i", $header)) {
1998
						$status = $status_intro . $error_str . gettext("Invalid username or password");
1999
					}
2000
					else {
2001
							$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2002
							log_error($status_intro . gettext("PAYLOAD:") . " " . $header.$data);
2003
 							$this->_debug($data);
2004
 							$this->_debug($header);
2005
 					}
2006
 					break;
2007
				case 'hover':
2008
					if (preg_match('/succeeded":true/i', $data)) {
2009
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2010
						$successful_update = true;
2011
					} else {
2012
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2013
						log_error($status_intro . gettext("PAYLOAD:") . " " . $data);
2014
						$this->_debug($data);
2015
					}
2016
					break;
2017
				case 'cloudns':
2018
					$result = json_decode($data, true);
2019
					if ($result['status'] == 'Success') {
2020
						$successful_update = true;
2021
					} else {
2022
						log_error($result['status'] . "(" . $result['statusDescription'] . ")");
2023
					}
2024
					break;
2025
				case 'dreamhost':
2026
				case 'dreamhost-v6':
2027
					$result = json_decode($data,true);
2028
					if ($this->_dnsVerboseLog) {
2029
						log_error(sprintf(gettext('_checkStatus() results: %1$s'), $data));
2030
					}
2031
					switch ($result['data']) {
2032
					case 'success':
2033
					case 'record_added':
2034
					case 'record_removed':
2035
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!") . " (" . $this->_dnsIP . ")";
2036
						$successful_update = true;
2037
						break;
2038
					case 'no_record':
2039
					case 'no_such_record ':
2040
						$status = $status_intro . $error_str . gettext("No record exists.");
2041
						break;
2042
					case 'no_type':
2043
					case 'no_such_type ':
2044
						$status = $status_intro . $error_str . gettext("No type exists.");
2045
						break;
2046
					case 'no_value':
2047
					case 'no_such_value ':
2048
						$status = $status_intro . $error_str . gettext("No value exists.");
2049
						break;
2050
					case 'no_such_zone':
2051
						$status = $status_intro . $error_str . gettext("No such zone exists.");
2052
						break;
2053
					case 'invalid_record':
2054
						$status = $status_intro . $error_str . gettext("The specified record is invalid.");
2055
						break;
2056
					case 'invalid_type':
2057
						$status = $status_intro . $error_str . gettext("The specified type is invalid.");
2058
						break;
2059
					case 'invalid_value':
2060
						$status = $status_intro . $error_str . gettext("The specified value is invalid.");
2061
						break;
2062
					case 'not_editable ':
2063
						$status = $status_intro . $error_str . gettext("Record is not editable.");
2064
						break;
2065
					case 'record_already_exists_not_editable':
2066
						$status = $status_intro . $error_str . gettext("Record exists but is not editable.");
2067
						break;
2068
					case 'record_already_exists_remove_first':
2069
						$status = $status_intro . $error_str . gettext("Record exists and must be removed before adding.");
2070
						break;
2071
					case 'internal_error_updating_zone':
2072
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2073
						break;
2074
					case 'internal_error_could_not_load_zone':
2075
						$status = $status_intro . $error_str . gettext("A remote server error occurred loading the zone.");
2076
						break;
2077
					case 'internal_error_could_not_update_zone':
2078
						$status = $status_intro . $error_str . gettext("A remote server error occurred updating the zone.");
2079
						break;
2080
					case 'internal_error_could_not_add_record':
2081
						$status = $status_intro . $error_str . gettext("A remote server error occurred adding a new record.");
2082
						break;
2083
					case 'internal_error_could_not_destroy_record ':
2084
						$status = $status_intro . $error_str . gettext("A remote server error occurred removing an existing record.");
2085
						break;
2086
					default:
2087
						break;
2088
					}
2089
				case 'azure':
2090
				case 'azurev6':
2091
					$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
2092
					if ($http_code == 401) {
2093
						$status = $status_intro . $error_str . gettext("User Authorization Failed");
2094
					} else if ($http_code == 201) {
2095
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2096
						$successful_update = true;
2097
					} else if ($http_code == 200) {
2098
						$status = $status_intro . $success_str . gettext("IP Address Changed Successfully!");
2099
						$successful_update = true;
2100
					} else {
2101
						$status = $status_intro . "(" . gettext("Unknown Response") . ")";
2102
						log_error($status_intro . gettext("PAYLOAD:") . " " . $http_code);
2103
						$this->_debug($data);
2104
					}
2105
					break;
2106
				default:
2107
					break;
2108
			}
2109

    
2110
			if ($successful_update == true) {
2111
				/* Write WAN IP to cache file */
2112
				$wan_ip = $this->_checkIP();
2113
				if ($this->_useIPv6 == false && $wan_ip > 0) {
2114
					$currentTime = time();
2115
					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));
2116
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile, $wan_ip));
2117
					@file_put_contents($this->_cacheFile, "{$wan_ip}|{$currentTime}");
2118
				} else {
2119
					@unlink($this->_cacheFile);
2120
				}
2121
				if ($this->_useIPv6 == true && $wan_ip > 0) {
2122
					$currentTime = time();
2123
					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));
2124
					log_error(sprintf(gettext('phpDynDNS: updating cache file %1$s: %2$s'), $this->_cacheFile_v6, $wan_ip));
2125
					@file_put_contents($this->_cacheFile_v6, "{$wan_ip}|{$currentTime}");
2126
				} else {
2127
					@unlink($this->_cacheFile_v6);
2128
				}
2129
			}
2130
			$this->status = $status;
2131
			log_error($status);
2132
		}
2133

    
2134
		/*
2135
		 * Private Function (added 12 July 05) [beta]
2136
		 *   Return Error, Set Last Error, and Die.
2137
		 */
2138
		function _error($errorNumber = '1') {
2139
			$err_str = 'phpDynDNS: (' . gettext('ERROR!') . ') ';
2140
			$err_str_r53 = 'Route 53: (' . gettext('Error') . ') ';
2141
			switch ($errorNumber) {
2142
				case 0:
2143
					break;
2144
				case 2:
2145
					$error = $err_str . gettext('No Dynamic DNS Service provider was selected.');
2146
					break;
2147
				case 3:
2148
					$error = $err_str . gettext('No Username Provided.');
2149
					break;
2150
				case 4:
2151
					$error = $err_str . gettext('No Password Provided.');
2152
					break;
2153
				case 5:
2154
					$error = $err_str . gettext('No Hostname Provided.');
2155
					break;
2156
				case 6:
2157
					$error = $err_str . gettext('The Dynamic DNS Service provided is not yet supported.');
2158
					break;
2159
				case 7:
2160
					$error = $err_str . gettext('No Update URL Provided.');
2161
					break;
2162
				case 8:
2163
					$status = $err_str_r53 . gettext("Invalid ZoneID");
2164
					break;
2165
				case 9:
2166
					$status = $err_str_r53 . gettext("Invalid TTL");
2167
					break;
2168
				case 10:
2169
					$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);
2170
					break;
2171
				default:
2172
					$error = $err_str . gettext('Unknown Response.');
2173
					/* FIXME: $data isn't in scope here */
2174
					/* $this->_debug($data); */
2175
					break;
2176
			}
2177
			$this->lastError = $error;
2178
			log_error($error);
2179
		}
2180

    
2181
		/*
2182
		 * Private Function (added 12 July 05) [beta]
2183
		 *   - Detect whether or not IP needs to be updated.
2184
		 *      | Written Specifically for pfSense (https://www.pfsense.org) may
2185
		 *      | work with other systems. pfSense base is FreeBSD.
2186
		 */
2187
		function _detectChange() {
2188
			global $debug;
2189

    
2190
			if ($debug) {
2191
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _detectChange() starting.'), $this->_dnsService, $this->_FQDN));
2192
			}
2193

    
2194
			$currentTime = time();
2195

    
2196
			$wan_ip = $this->_checkIP();
2197
			if ($wan_ip == 0) {
2198
				log_error(sprintf(gettext("Dynamic Dns (%s): Current WAN IP could not be determined, skipping update process."), $this->_FQDN));
2199
				return false;
2200
			}
2201
			$log_error = sprintf(gettext('Dynamic Dns (%1$s): Current WAN IP: %2$s'), $this->_FQDN, $wan_ip) . " ";
2202

    
2203
			if ($this->_useIPv6 == true) {
2204
				if (file_exists($this->_cacheFile_v6)) {
2205
					$contents = file_get_contents($this->_cacheFile_v6);
2206
					list($cacheIP, $cacheTime) = explode('|', $contents);
2207
					$this->_debug($cacheIP.'/'.$cacheTime);
2208
					$initial = false;
2209
					$log_error .= sprintf(gettext("Cached IPv6: %s"), $cacheIP);
2210
				} else {
2211
					$cacheIP = '::';
2212
					@file_put_contents($this->_cacheFile, "::|{$currentTime}");
2213
					$cacheTime = $currentTime;
2214
					$initial = true;
2215
					$log_error .= gettext("No Cached IPv6 found.");
2216
				}
2217
			} else {
2218
				if (file_exists($this->_cacheFile)) {
2219
					$contents = file_get_contents($this->_cacheFile);
2220
					list($cacheIP, $cacheTime) = explode('|', $contents);
2221
					$this->_debug($cacheIP.'/'.$cacheTime);
2222
					$initial = false;
2223
					$log_error .= sprintf(gettext("Cached IP: %s"), $cacheIP);
2224
				} else {
2225
					$cacheIP = '0.0.0.0';
2226
					@file_put_contents($this->_cacheFile, "0.0.0.0|{$currentTime}");
2227
					$cacheTime = $currentTime;
2228
					$initial = true;
2229
					$log_error .= gettext("No Cached IP found.");
2230
				}
2231
			}
2232
			if ($this->_dnsVerboseLog) {
2233
				log_error($log_error);
2234
			}
2235

    
2236
			// Convert seconds = days * hr/day * min/hr * sec/min
2237
			$maxCacheAgeSecs = $this->_dnsMaxCacheAgeDays * 24 * 60 * 60;
2238

    
2239
			$needs_updating = FALSE;
2240
			/* lets determine if the item needs updating */
2241
			if ($cacheIP != $wan_ip) {
2242
				$needs_updating = true;
2243
				$update_reason = gettext("Dynamic Dns: cacheIP != wan_ip. Updating.") . " ";
2244
				$update_reason .= sprintf(gettext('Cached IP: %1$s WAN IP: %2$s'), $cacheIP, $wan_ip) . " ";
2245
			}
2246
			if (($currentTime - $cacheTime) > $maxCacheAgeSecs) {
2247
				$needs_updating = true;
2248
				$this->_forceUpdateNeeded = true;
2249
				$update_reason = sprintf(gettext("Dynamic Dns: More than %s days. Updating."), $this->_dnsMaxCacheAgeDays);
2250
				$update_reason .= " {$currentTime} - {$cacheTime} > {$maxCacheAgeSecs} ";
2251
			}
2252
			if ($initial == true) {
2253
				$needs_updating = true;
2254
				$update_reason .= gettext("Initial update.");
2255
			}
2256

    
2257
			/*   finally if we need updating then store the
2258
			 *   new cache value and return true
2259
			 */
2260
			if ($needs_updating == true) {
2261
				if ($this->_dnsVerboseLog) {
2262
					log_error("DynDns ({$this->_FQDN}): {$update_reason}");
2263
				}
2264
				return true;
2265
			}
2266

    
2267
			return false;
2268
		}
2269

    
2270
		/*
2271
		 * Private Function (added 16 July 05) [beta]
2272
		 *   - Writes debug information to a file.
2273
		 *   - This function is only called when a unknown response
2274
		 *   - status is returned from a DynDNS service provider.
2275
		 */
2276
		function _debug($data) {
2277
			global $g;
2278

    
2279
			if (!$g['debug']) {
2280
				return;
2281
			}
2282
			$string = date('m-d-y h:i:s').' - ('.$this->_debugID.') - ['.$this->_dnsService.'] - '.$data."\n";
2283
			$file = fopen($this->_debugFile, 'a');
2284
			fwrite($file, $string);
2285
			fclose($file);
2286
		}
2287
		function _checkIP() {
2288
			global $debug;
2289

    
2290
			if ($debug) {
2291
				log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): _checkIP() starting.'), $this->_dnsService, $this->_FQDN));
2292
			}
2293

    
2294
			if ($this->_useIPv6 == true) {
2295
				$ip_address = get_interface_ipv6($this->_if);
2296
				if (!is_ipaddrv6($ip_address)) {
2297
					return 0;
2298
				}
2299
			} else {
2300
				$ip_address = dyndnsCheckIP($this->_if);
2301
				if (!is_ipaddr($ip_address)) {
2302
					return 0;
2303
				}
2304
			}
2305
			if ($this->_useIPv6 == false && is_private_ip(get_interface_ip($this->_if))) {
2306
				if (is_ipaddr($ip_address)) {
2307
					if ($this->_dnsVerboseLog) {
2308
						log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from Check IP Service'), $this->_dnsService, $this->_FQDN, $ip_address));
2309
					}
2310
				} else {
2311
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): IP address could not be extracted from Check IP Service'), $this->_dnsService, $this->_FQDN));
2312
					return 0;
2313
				}
2314
			} else {
2315
				if ($this->_dnsVerboseLog) {
2316
					log_error(sprintf(gettext('Dynamic DNS %1$s (%2$s): %3$s extracted from local system.'), $this->_dnsService, $this->_FQDN, $ip_address));
2317
				}
2318
			}
2319
			$this->_dnsIP = $ip_address;
2320

    
2321
			return $ip_address;
2322
		}
2323

    
2324
	}
2325

    
2326
?>
(15-15/60)