Merge pull request #2 from TheBlueMatt/master

Custom Dynamic DNS
This commit is contained in:
Ermal Luçi 2012-05-24 00:29:57 -07:00
commit 310ce280c1
4 changed files with 201 additions and 54 deletions

View File

@ -21,10 +21,12 @@
* - HE.net (dns.he.net)
* - HE.net Tunnelbroker IP update (ipv4.tunnelbroker.net)
* - SelfHost (selfhost.de)
* - Amazon Route 53 (aws.amazon.com)
* - Amazon Route 53 (aws.amazon.com)
* - DNS-O-Matic (dnsomatic.com)
* - Custom DDNS (any URL)
* +----------------------------------------------------+
* Requirements:
* - PHP version 4.0.2 or higher with CURL Library
* - PHP version 4.0.2 or higher with the CURL Library and the PCRE Library
* +----------------------------------------------------+
* Public Functions
* - updatedns()
@ -56,7 +58,8 @@
* HE.net - Last Tested: NEVER
* HE.net Tunnel - Last Tested: 28 June 2011
* SelfHost - Last Tested: 26 December 2011
+ * Amazon Route 53 - Last tested: 01 April 2012
* Amazon Route 53 - Last tested: 01 April 2012
* DNS-O-Matic - Last Tested: 9 September 2010
* +====================================================+
*
* @author E.Kristensen
@ -65,6 +68,7 @@
* @updated 13 October 05 at 21:02:42 GMT
*
* DNSexit/OpenDNS support and multiwan extension for pfSense by Ermal Luçi
* Custom DNS support by Matt Corallo
*
*/
@ -78,8 +82,6 @@
var $_dnsPass;
var $_dnsHost;
var $_dnsIP;
/* This is needed for support on addresses behind NAT. */
var $_ifIP;
var $_dnsWildcard;
var $_dnsMX;
var $_dnsBackMX;
@ -91,24 +93,33 @@
var $status;
var $_debugID;
var $_if;
var $_dnsResultMatch;
var $_dnsRequestIf;
var $_dnsRequestIfIP;
/*
* Public Constructor Function (added 12 July 05) [beta]
* - Gets the dice rolling for the update.
* - $dnsResultMatch should only be used with $dnsService = 'custom'
* - $dnsResultMatch is parsed for '%IP%', which is the IP the provider was updated to,
* - it is otherwise expected to be exactly identical to what is returned by the Prover.
* - $dnsUser, and $dnsPass indicate HTTP Auth for custom DNS, if they are needed in the URL (GET Variables), include them in $dnsUpdateURL.
* - $For custom requests, $dnsUpdateURL is parsed for '%IP%', which is replaced with the new IP.
*/
function updatedns ($dnsService = '', $dnsHost = '', $dnsUser = '', $dnsPass = '',
$dnsWildcard = 'OFF', $dnsMX = '', $dnsIf = '', $dnsBackMX = '',
$dnsServer = '', $dnsPort = '', $dnsUpdateURL = '', $forceUpdate = false,
$dnsZoneID ='', $dnsTTL='') {
$dnsZoneID ='', $dnsTTL='', $dnsResultMatch = '', $dnsRequestIf = '',
$dnsID = '') {
global $config, $g;
$this->_cacheFile = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($dnsHost) . ".cache";
$this->_debugFile = "{$g['varetc_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($dnsHost) . ".debug";
$this->_cacheFile = "{$g['conf_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($dnsHost) . "{$dnsID}.cache";
$this->_debugFile = "{$g['varetc_path']}/dyndns_{$dnsIf}{$dnsService}" . escapeshellarg($dnsHost) . "{$dnsID}.debug";
log_error("DynDns: updatedns() starting");
$dyndnslck = lock($dnsHost, LOCK_EX);
$dyndnslck = lock("DDNS".$dnsID, LOCK_EX);
if (!$dnsService) $this->_error(2);
switch ($dnsService) {
@ -119,11 +130,13 @@
if (!$dnsPass) $this->_error(4);
if (!$dnsHost) $this->_error(5);
break;
case 'route53':
if (!$dnsZoneID) $this->_error(8);
if (!$dnsTTL) $this->_error(9);
break;
case 'route53':
if (!$dnsZoneID) $this->_error(8);
if (!$dnsTTL) $this->_error(9);
break;
case 'custom':
if (!$dnsUpdateURL) $this->_error(7);
break;
default:
if (!$dnsUser) $this->_error(3);
if (!$dnsPass) $this->_error(4);
@ -141,11 +154,15 @@
$this->_dnsZoneID = $dnsZoneID;
$this->_dnsTTL = $dnsTTL;
$this->_if = get_real_interface($dnsIf);
$this->_ifIP = get_interface_ip($dnsIf);
$this->_checkIP();
$this->_dnsUpdateURL = $dnsUpdateURL;
$this->_dnsResultMatch = $dnsResultMatch;
$this->_dnsRequestIf = get_real_interface($dnsRequestIf);
$this->_dnsRequestIfIP = get_interface_ip($dnsRequestIf);
// Ensure that we where able to lookup the IP
if(!is_ipaddr($this->_ifIP)) {
log_error("There was an error trying to determine the IP for interface - {$dnsIf}({$this->_if}). Probably interface has no ip or is down. Dyndns update not possible for {$dnsService}.");
if(!is_ipaddr($this->_dnsIP)) {
log_error("There was an error trying to determine the public IP for interface - {$dnsIf}({$this->_if}). Probably interface is not a WAN interface.");
unlock($dyndnslck);
return;
}
@ -171,16 +188,15 @@
case 'loopia':
case 'staticcling':
case 'dnsexit':
case 'custom':
case 'opendns':
case 'namecheap':
case 'he-net':
case 'selfhost':
case 'he-net-tunnelbroker':
case 'route53':
$this->_update();
break;
case 'he-net-tunnelbroker':
$this->_update();
break;
default:
$this->_error(6);
break;
@ -203,7 +219,7 @@
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_USERAGENT, $this->_UserAgent);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_INTERFACE, $this->_ifIP);
curl_setopt($ch, CURLOPT_INTERFACE, $this->_dnsRequestIfIP);
curl_setopt($ch, CURLOPT_TIMEOUT, 120); // Completely empirical
}
@ -496,6 +512,17 @@
}
$this->_checkStatus(0, $result);
break;
case 'custom':
log_error("Custom DDNS: DNS update() starting.");
if (strstr($this->dnsUpdateURL, "%IP%")) {$needsIP = TRUE;} else {$needsIP = FALSE;}
if ($this->_dnsUser != '') {
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($ch, CURLOPT_USERPWD, "{$this->_dnsUser}:{$this->_dnsPass}");
}
$server = str_replace("%IP%", $this->_dnsIP, $this->_dnsUpdateURL);
log_error("Sending request to: ".$server);
curl_setopt($ch, CURLOPT_URL, $server);
break;
default:
break;
}
@ -786,7 +813,7 @@
$this->_debug($data);
}
break;
case 'staticcling':
case 'staticcling':
if (preg_match("/invalid ip/i", $data)) {
$status = "phpDynDNS: (Error) Bad Request - The IP provided was invalid.";
} else if (preg_match('/required info missing/i', $data)) {
@ -894,6 +921,25 @@
case 'route53':
$successful_update = true;
break;
case 'custom':
$successful_update = false;
if ($this->_dnsResultMatch == "") {
$successful_update = true;
}else {
$this->_dnsResultMatch = str_replace("%IP%", $this->_dnsIP, $this->_dnsResultMatch);
$matches = preg_split("/(?<!\\\\)\\|/", $this->_dnsResultMatch);
foreach($matches as $match) {
$match= str_replace("\\|", "|", $match);
if(strcmp($match, trim($data, "\t\n\r")) == 0)
$successful_update = true;
}
unset ($matches);
}
if ($successful_update == true)
$status = "phpDynDNS: (Success) IP Address Updated Successfully!";
else
$status = "phpDynDNS: (Error) Result did not match.";
break;
}
if($successful_update == true) {
@ -976,7 +1022,6 @@
log_error("DynDns: Current WAN IP could not be determined, skipping update process.");
return false;
}
$this->_dnsIP = $wan_ip;
$log_error = "DynDns: Current WAN IP: {$wan_ip} ";
if (file_exists($this->_cacheFile)) {
@ -1052,7 +1097,6 @@
$ip_address = find_interface_ip($this->_if);
if (!is_ipaddr($ip_address))
return 0;
$this->_ifIP = $ip_address;
if (is_private_ip($ip_address)) {
$hosttocheck = "checkip.dyndns.org";
$try = 0;
@ -1086,6 +1130,8 @@
} else
log_error("DynDns debug information: {$ip_address} extracted from local system.");
$this->_dnsIP = $ip_address;
return $ip_address;
}

View File

@ -1312,10 +1312,13 @@ function services_dyndns_configure_client($conf) {
$dnsBackMX = NULL,
$dnsServer = NULL,
$dnsPort = NULL,
$dnsUpdateURL = NULL,
$dnsUpdateURL = "{$conf['updateurl']}",
$forceUpdate = $conf['force'],
$dnsZoneID=$conf['zoneid'],
$dnsTTL=$conf['ttl']);
$dnsTTL=$conf['ttl'],
$dnsResultMatch = "{$conf['resultmatch']}",
$dnsRequestIf = "{$conf['requestif']}",
$dnsID = "{$conf['id']}");
}
function services_dyndns_configure($int = "") {

View File

@ -47,8 +47,17 @@ $a_dyndns = &$config['dyndnses']['dyndns'];
if ($_GET['act'] == "del") {
unset($a_dyndns[$_GET['id']]);
for($i = 0; $i < count($a_dyndns); $i++) {
$a_dyndns[$i]['id'] = $i;
}
//FIXME: Instead of rechecking all interfaces and removing the cache files, gracefully move all cache files to the appropriate ID number.
mwexec("/bin/rm {$g['conf_path']}/dyndns_*.cache");
write_config();
services_dyndns_configure();
header("Location: services_dyndns.php");
exit;
}
@ -114,8 +123,8 @@ include("head.inc");
</td>
<td class="listlr">
<?php
$types = explode(",", "DNS-O-Matic, DynDNS (dynamic),DynDNS (static),DynDNS (custom),DHS,DyNS,easyDNS,No-IP,ODS.org,ZoneEdit,Loopia,freeDNS, DNSexit, OpenDNS, Namecheap, HE.net, HE.net Tunnelbroker, SelfHost, Route 53");
$vals = explode(" ", "dnsomatic dyndns dyndns-static dyndns-custom dhs dyns easydns noip ods zoneedit loopia freedns dnsexit opendns namecheap he-net he-net-tunnelbroker selfhost route53");
$types = explode(",", "DNS-O-Matic, DynDNS (dynamic),DynDNS (static),DynDNS (custom),DHS,DyNS,easyDNS,No-IP,ODS.org,ZoneEdit,Loopia,freeDNS, DNSexit, OpenDNS, Namecheap, HE.net, HE.net Tunnelbroker, SelfHost, Route 53, Custom");
$vals = explode(" ", "dnsomatic dyndns dyndns-static dyndns-custom dhs dyns easydns noip ods zoneedit loopia freedns dnsexit opendns namecheap he-net he-net-tunnelbroker selfhost route53 custom");
$j = 0; for ($j = 0; $j < count($vals); $j++)
if ($vals[$j] == $dyndns['type']) {
echo htmlspecialchars($types[$j]);
@ -128,7 +137,7 @@ include("head.inc");
</td>
<td class="listlr">
<?php
$filename = "{$g['conf_path']}/dyndns_{$if}{$dyndns['type']}" . escapeshellarg($dyndns['host']) . ".cache";
$filename = "{$g['conf_path']}/dyndns_{$if}{$dyndns['type']}" . escapeshellarg($dyndns['host']) . "{$dyndns['id']}.cache";
$ipaddr = dyndnsCheckIP($if);
if(file_exists($filename)) {
$cached_ip_s = explode(":", file_get_contents($filename));

View File

@ -71,6 +71,9 @@ if (isset($id) && isset($a_dyndns[$id])) {
$pconfig['wildcard'] = isset($a_dyndns[$id]['wildcard']);
$pconfig['zoneid'] = $a_dyndns[$id]['zoneid'];
$pconfig['ttl'] = isset($a_dyndns[$id]['ttl']);
$pconfig['updateurl'] = $a_dyndns[$id]['updateurl'];
$pconfig['resultmatch'] = $a_dyndns[$id]['resultmatch'];
$pconfig['requestif'] = $a_dyndns[$id]['requestif'];
$pconfig['descr'] = $a_dyndns[$id]['descr'];
}
@ -85,15 +88,26 @@ if ($_POST) {
/* input validation */
$reqdfields = array();
$reqdfieldsn = array();
$reqdfields = array("host", "username", "password", "type");
$reqdfieldsn = array(gettext("Hostname"),gettext("Username"),gettext("Password"),gettext("Service type"));
$reqdfields = array("type");
$reqdfieldsn = array(gettext("Service type"));
if ($pconfig['type'] != "custom") {
$reqdfields[] = "host";
$reqdfieldsn[] = gettext("Hostname");
$reqdfields[] = "password";
$reqdfieldsn[] = gettext("Password");
$reqdfields[] = "username";
$reqdfieldsn[] = gettext("Username");
}else{
$reqdfields[] = "updateurl";
$reqdfieldsn[] = gettext("Update URL");
}
do_input_validation($_POST, $reqdfields, $reqdfieldsn, &$input_errors);
if (($_POST['mx'] && !is_domain($_POST['mx'])))
$input_errors[] = gettext("The MX contains invalid characters.");
if (($_POST['username'] && !is_dyndns_username($_POST['username'])) || (($pconfig['type'] != "namecheap") && ($_POST['username'] == "")))
$input_errors[] = gettext("The username contains invalid characters.");
if ((in_array("username", $reqdfields) && $_POST['username'] && !is_dyndns_username($_POST['username'])) || ((in_array("username", $reqdfields)) && ($_POST['username'] == "")))
$input_errors[] = gettext("The username contains invalid characters.");
if (!$input_errors) {
$dyndns = array();
@ -107,6 +121,10 @@ if ($_POST) {
$dyndns['interface'] = $_POST['interface'];
$dyndns['zoneid'] = $_POST['zoneid'];
$dyndns['ttl'] = $_POST['ttl'];
$dyndns['updateurl'] = $_POST['updateurl'];
// Trim hard-to-type but sometimes returned characters
$dyndns['resultmatch'] = trim($_POST['resultmatch'], "\t\n\r");
$dyndns['type'] == "custom" ? $dyndns['requestif'] = $_POST['requestif'] : $dyndns['requestif'] = $_POST['interface'];
$dyndns['descr'] = $_POST['descr'];
$dyndns['force'] = isset($_POST['force']);
@ -115,8 +133,16 @@ if ($_POST) {
if (isset($id) && $a_dyndns[$id])
$a_dyndns[$id] = $dyndns;
else
$a_dyndns[] = $dyndns;
else {
$a_dyndns[] = $dyndns;
$id = count($a_dyndns) - 1;
}
$dyndns['id'] = $id;
//Probably overkill, but its better to be safe
for($i = 0; $i < count($a_dyndns); $i++) {
$a_dyndns[$i]['id'] = $i;
}
write_config();
@ -136,6 +162,41 @@ include("head.inc");
<?php include("fbegin.inc"); ?>
<?php if ($input_errors) print_input_errors($input_errors); ?>
<?php if ($savemsg) print_info_box($savemsg); ?>
<script type="text/javascript">
function _onTypeChange(type){
switch(type) {
case "custom":
document.getElementById("_resulttr").style.display = '';
document.getElementById("_urltr").style.display = '';
document.getElementById("_requestiftr").style.display = '';
document.getElementById("_hostnametr").style.display = 'none';
document.getElementById("_mxtr").style.display = 'none';
document.getElementById("_wildcardtr").style.display = 'none';
document.getElementById("r53_zoneid").style.display='none';
document.getElementById("r53_ttl").style.display='none';
break;
case "route53":
document.getElementById("_resulttr").style.display = 'none';
document.getElementById("_urltr").style.display = 'none';
document.getElementById("_requestiftr").style.display = 'none';
document.getElementById("_hostnametr").style.display = '';
document.getElementById("_mxtr").style.display = '';
document.getElementById("_wildcardtr").style.display = '';
document.getElementById("r53_zoneid").style.display='';
document.getElementById("r53_ttl").style.display='';
break;
default:
document.getElementById("_resulttr").style.display = 'none';
document.getElementById("_urltr").style.display = 'none';
document.getElementById("_requestiftr").style.display = 'none';
document.getElementById("_hostnametr").style.display = '';
document.getElementById("_mxtr").style.display = '';
document.getElementById("_wildcardtr").style.display = '';
document.getElementById("r53_zoneid").style.display='none';
document.getElementById("r53_ttl").style.display='none';
}
}
</script>
<form action="services_dyndns_edit.php" method="post" name="iform" id="iform">
<table width="100%" border="0" cellpadding="6" cellspacing="0">
<tr>
@ -154,10 +215,10 @@ include("head.inc");
<tr>
<td width="22%" valign="top" class="vncellreq"><?=gettext("Service type");?></td>
<td width="78%" class="vtable">
<select name="type" class="formselect" id="type">
<select name="type" class="formselect" id="type" onchange="_onTypeChange(this.options[this.selectedIndex].value);">
<?php
$types = explode(",", "DNS-O-Matic, DynDNS (dynamic),DynDNS (static),DynDNS (custom),DHS,DyNS,easyDNS,No-IP,ODS.org,ZoneEdit,Loopia,freeDNS, DNSexit, OpenDNS, Namecheap, HE.net, HE.net Tunnelbroker, SelfHost, Route 53");
$vals = explode(" ", "dnsomatic dyndns dyndns-static dyndns-custom dhs dyns easydns noip ods zoneedit loopia freedns dnsexit opendns namecheap he-net he-net-tunnelbroker selfhost route53");
$types = explode(",", "DNS-O-Matic, DynDNS (dynamic),DynDNS (static),DynDNS (custom),DHS,DyNS,easyDNS,No-IP,ODS.org,ZoneEdit,Loopia,freeDNS, DNSexit, OpenDNS, Namecheap, HE.net, HE.net Tunnelbroker, SelfHost, Route 53, Custom");
$vals = explode(" ", "dnsomatic dyndns dyndns-static dyndns-custom dhs dyns easydns noip ods zoneedit loopia freedns dnsexit opendns namecheap he-net he-net-tunnelbroker selfhost route53 custom");
$j = 0; for ($j = 0; $j < count($vals); $j++): ?>
<option value="<?=$vals[$j];?>" <?php if ($vals[$j] == $pconfig['type']) echo "selected";?>>
<?=htmlspecialchars($types[$j]);?>
@ -177,7 +238,20 @@ include("head.inc");
</td>
</td>
</tr>
<tr>
<tr id="_requestiftr">
<td width="22%" valign="top" class="vncellreq"><?=gettext("Interface to send update from");?></td>
<td width="78%" class="vtable">
<select name="requestif" class="formselect" id="requestif">
<?php $iflist = get_configured_interface_with_descr();
foreach ($iflist as $if => $ifdesc):?>
<option value="<?=$if;?>" <?php if ($pconfig['requestif'] == $if) echo "selected";?>><?=$ifdesc;?></option>
<?php endforeach; ?>
</select>
<br/><?= gettext("Note: This is almost always the same as the Interface to Monitor.");?>
</td>
</td>
</tr>
<tr id="_hostnametr">
<td width="22%" valign="top" class="vncellreq"><?=gettext("Hostname");?></td>
<td width="78%" class="vtable">
<input name="host" type="text" class="formfld unknown" id="host" size="30" value="<?=htmlspecialchars($pconfig['host']);?>">
@ -190,7 +264,7 @@ include("head.inc");
</span>
</td>
</tr>
<tr>
<tr id="_mxtr">
<td width="22%" valign="top" class="vncell"><?=gettext("MX"); ?></td>
<td width="78%" class="vtable">
<input name="mx" type="text" class="formfld unknown" id="mx" size="30" value="<?=htmlspecialchars($pconfig['mx']);?>">
@ -200,18 +274,19 @@ include("head.inc");
<?=gettext("Set this option only if you need a special MX record. Not".
" all services support this.");?></td>
</tr>
<tr>
<tr id="_wildcardtr">
<td width="22%" valign="top" class="vncell"><?=gettext("Wildcards"); ?></td>
<td width="78%" class="vtable">
<input name="wildcard" type="checkbox" id="wildcard" value="yes" <?php if ($pconfig['wildcard']) echo "checked"; ?>>
<?=gettext("Enable ");?><?=gettext("Wildcard"); ?></td>
</tr>
<tr>
<tr id="_usernametr">
<td width="22%" valign="top" class="vncellreq"><?=gettext("Username");?></td>
<td width="78%" class="vtable">
<input name="username" type="text" class="formfld user" id="username" size="20" value="<?=htmlspecialchars($pconfig['username']);?>">
<br/><?= gettext("Username is required for all types except Namecheap and FreeDNS.");?>
<br/><?= gettext("Username is required for all types except Namecheap, FreeDNS and Custom Entries.");?>
<br/><?= gettext("Route 53: Enter your Access Key ID.");?>
<br/><?= gettext("For Custom Entries, Username and Password represent HTTP Authentication username and passwords.");?>
</td>
</tr>
<tr>
@ -231,6 +306,30 @@ include("head.inc");
<br/><?= gettext("Enter Zone ID that you received when you created your domain in Route 53.");?>
</td>
</tr>
<tr id="_urltr">
<td width="22%" valign="top" class="vncell"><?=gettext("Update URL");?></td>
<td width="78%" class="vtable">
<input name="updateurl" type="text" class="formfld unknown" id="updateurl" size="60" value="<?=htmlspecialchars($pconfig['updateurl']);?>">
<br/><?= gettext("This is the only field required by for Custom Dynamic DNS, and is only used by Custom Entries.");?>
<br/>
<?= gettext("If you need the new IP to be included in the request, put %IP% in its place.");?>
</td>
</tr>
<tr id="_resulttr">
<td width="22%" valign="top" class="vncell"><?=gettext("Result Match");?></td>
<td width="78%" class="vtable">
<textarea name="resultmatch" class="formpre" id="resultmatch" cols="65" rows="7"><?=htmlspecialchars($pconfig['resultmatch']);?></textarea>
<br/><?= gettext("This field is only used by Custom Dynamic DNS Entries.");?>
<br/>
<?= gettext("This field should be identical to what your DDNS Provider will return if the update succeeds, leave it blank to disable checking of returned results.");?>
<br/>
<?= gettext("If you need the new IP to be included in the request, put %IP% in its place.");?>
<br/>
<?= gettext("If you need to include multiple possible values, sperate them with a |. If your provider includes a |, escape it with \\|");?>
<br/>
<?= gettext("Tabs (\\t), newlines (\\n) and carriage returns (\\r) at the beginning or end of the returned results are removed before comparison.");?>
</td>
</tr>
<tr>
<tr id="r53_ttl" style="display:none">
@ -272,17 +371,7 @@ include("head.inc");
<?php include("fend.inc"); ?>
<script type="text/javascript">
var selectmenu=document.getElementById("type")
selectmenu.onchange=function(){
var chosenoption=this.options[this.selectedIndex]
if (chosenoption.value=="route53"){
document.getElementById("r53_zoneid").style.display="";
document.getElementById("r53_ttl").style.display="";
} else if (chosenoption.value !="route53"){
document.getElementById("r53_zoneid").style.display="none";
document.getElementById("r53_ttl").style.display="none";
}
}
_onTypeChange("<?php echo $pconfig['type']; ?>");
</script>
</body>