--- /dev/null
+Copyright (c) 2007-2013 Nico Kaiser (nico@kaiser.me)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
-Introduction
-============
+Dyndns: a simple DynDNS server in PHP
+=====================================
-This script takes the same parameters as the original members.dyndns.org
-server does. It can update a BIND DNS server via "nsupdate".
+This script takes the same parameters as the original dyndns.org server does. It can update a BIND DNS server via `nsupdate`.
-As it uses the same syntax as the original DynDNS.org servers do, a dynamic DNS server equipped with
-this script can be used with DynDNS compatible clients without having to modify anything on the
-client side.
+As it uses the same syntax as the original DynDNS.org servers do, a dynamic DNS server equipped with this script can be used with DynDNS compatible clients without having to modify anything on the client side.
Installation
-============
+------------
To mimic the original DynDNS.org behavior, the Script's URL must be
- http://members.dyndns.org/nic/update
+ http://members.dyndns.org/nic/update
You may have to adjust your own DNS configuration to make "members.dyndns.org" point to your own
Server and you Web Servers configuration to make "/nic/update" call the PHP script provided in this
Furthermore, to be able to dynamically update the BIND DNS server, DNS key must be generated with
the command:
- dnskeygen -n dyndns.example.com -H 512 -h
+ dnskeygen -n dyndns.example.com -H 512 -h
(Where "dyndns.example.com" is the key name)
The resulting key (look at the "Key:" line in the resulting Kdyndns.example.com.+157+00000.private)
The key has to be added to the BIND configuration (named.conf), as well as a DNS zone:
-key dyndns.example.com. {
- algorithm HMAC-MD5;
- secret "bvZ....K5A==";
-};
+ key dyndns.example.com. {
+ algorithm HMAC-MD5;
+ secret "bvZ....K5A==";
+ };
-zone "dyndns.example.com" {
- type master;
- file "dyndns.example.com.zone";
- allow-update {
- key dyndns.example.com.;
- };
-};
+ zone "dyndns.example.com" {
+ type master;
+ file "dyndns.example.com.zone";
+ allow-update {
+ key dyndns.example.com.;
+ };
+ };
In this case, the zone is also called "dyndns.example.com". The (initial) dyndns.example.com.zone
file (located in BIND's cache directory) looks like this:
finally update the DNS with the new data. Its configuration is rather simple, the user database is
implemented as text file "dyndns.user" with each line containing
- <user>:<password>
+ <user>:<password>
Where <password> is crypt'ed like in Apache's htpasswd files.
Hosts are assigned to users in using the file "dyndns.hosts":
- <host>:<user>(,<user>,<user>,...)
+ <host>:<user>(,<user>,<user>,...)
(So users can update multiple hosts, and a host can be updated by multiple users).
Implementation
-==============
+--------------
Here you can find details on which capabilities of the DynDNS specification are implemented.
(See http://www.dyndns.com/developers/specs/return.html for more details)
+Todo
+----
-Nico Kaiser
-nico@siriux.net
+* Implement Wildcards
+* Implement NOCHG
+* Implement more features from DynDNS.org
+* Provide Apache templates (for mod_rewrite, etc.)
<?php
-/*
- * DynDNS Server Script
- * Copyright (c) 2007 Nico Kaiser
- *
- * http://siriux.net/
- */
require_once(dirname(__FILE__) . '/DyndnsHelper.class.php');
require_once(dirname(__FILE__) . '/DyndnsHosts.class.php');
require_once(dirname(__FILE__) . '/DyndnsUsers.class.php');
/**
- * Simple Dynamic DNS
+ * Simple Dynamic DNS server.
*
* @package Dyndns
- * @author Nico Kaiser <nico@siriux.net>
- * @version $Revision: 13 $
+ * @author Nico Kaiser <nico@kaiser.me>
*/
-class Dyndns {
-
- /**
+class Dyndns
+{
+ /**
* Storage for all configuration variables, set in config.php
- * @var array
- * @access private
+ * @var array
+ */
+ private $config;
+
+ /**
+ * The user currently logged in
+ * @var string
+ */
+ private $user;
+
+ /**
+ * IP the hostnames should point to
+ * @var string
+ */
+ private $myIp;
+
+ /**
+ * Hostnames that should be updated
+ * @var array
*/
- var $_config;
-
- /**
- * The user logged in
- * @var string
- * @access private
- */
- var $_user;
-
- /**
- * IP the hostnames should point to
- * @var string
- * @access private
- */
- var $_myIp;
-
- /**
- * Hostnames that should be updated
- * @var array
- * @access private
- */
- var $_hostnames;
-
- /**
- * Debug buffer
- * @var string
- * @access private
- */
- var $_debugBuffer;
-
-
- function Dyndns() {
- /* Default config settings */
- $this->_config = array (
- 'hostsFile' => 'dyndns.hosts', /* Location of the hosts database */
- 'userFile' => 'dyndns.user', /* Location of the user database */
- 'debugFile' => 'dyndns.log', /* Debug file */
- 'debug' => false, /* Enable debugging */
-
- 'bind.server' => false,
- 'bind.zone' => '',
- 'bind.ttl' => 300,
- 'bind.key' => '',
- );
- }
-
- /**
- * Initializes the Dyndns script
- */
- function init() {
- $this->_users = new DyndnsUsers($this->_config['userFile']);
- $this->_hosts = new DyndnsHosts($this->_config['hostsFile']);
-
- $this->_checkHttpMethod();
- $this->_checkAuthentication();
-
- /* Get IP address, fallback to REMOTE_ADDR */
- $this->_myIp = DyndnsHelper::getMyIp();
- if (array_key_exists('myip', $_REQUEST)) {
- if (DyndnsHelper::checkValidIp($_REQUEST['myip'])) {
- $this->_myIp = $_REQUEST['myip'];
- } else {
- $this->debug('Invalid parameter myip. Using default REMOTE_ADDR');
- }
- }
-
- /* Get hostnames to be updated */
- $this->_hostnames = array ();
- if (array_key_exists('hostname', $_REQUEST) && ($_REQUEST['hostname'] != '')) {
- $this->_hostnames = explode(',', strtolower($_REQUEST['hostname']));
- $this->_checkHostnames();
- } else {
- $this->_returnCode('notfqdn');
- }
-
- $this->_updateHosts();
-
- /* Return "good" code as everything seems to be ok now */
- $this->_returnCode('good');
- }
-
- /**
- * Store a value in the config table
- *
- * @param string $key
- * @param mixed $value
- */
- function setConfig($key, $value) {
- $this->_config[$key] = $value;
- }
-
- /**
- * Get a value from the config table
- *
- * @return mixed an arbitrary value
- */
- function getConfig($key) {
- return $this->_config[$key];
+ private $hostnames;
+
+ /**
+ * Debug buffer
+ * @var string
+ */
+ private $debugBuffer;
+
+ public function __construct()
+ {
+ $this->config = array (
+ 'hostsFile' => 'dyndns.hosts', // Location of the hosts database
+ 'userFile' => 'dyndns.user', // Location of the user database
+ 'debugFile' => 'dyndns.log', // Debug file
+ 'debug' => false, // Enable debugging
+
+ 'bind.server' => false,
+ 'bind.zone' => '',
+ 'bind.ttl' => 300,
+ 'bind.key' => '',
+ );
+ }
+
+ public function init()
+ {
+ $this->users = new DyndnsUsers($this->config['userFile']);
+ $this->hosts = new DyndnsHosts($this->config['hostsFile']);
+
+ $this->checkHttpMethod();
+ $this->checkAuthentication();
+
+ // Get IP address, fallback to REMOTE_ADDR
+ $this->myIp = DyndnsHelper::getMyIp();
+ if (array_key_exists('myip', $_REQUEST)) {
+ if (DyndnsHelper::checkValidIp($_REQUEST['myip'])) {
+ $this->myIp = $_REQUEST['myip'];
+ } else {
+ $this->debug('Invalid parameter myip. Using default REMOTE_ADDR');
+ }
+ }
+
+ // Get hostnames to be updated
+ $this->hostnames = array ();
+ if (array_key_exists('hostname', $_REQUEST) && ($_REQUEST['hostname'] != '')) {
+ $this->hostnames = explode(',', strtolower($_REQUEST['hostname']));
+ $this->checkHostnames();
+ } else {
+ $this->returnCode('notfqdn');
+ }
+
+ $this->updateHosts();
+
+ // Return "good" code as everything seems to be ok now
+ $this->returnCode('good');
+ }
+
+ public function setConfig($key, $value)
+ {
+ $this->config[$key] = $value;
}
- /**
- * Checks if the HTTP method is supported. Currently, only GET is supported,
- * all other methods will result in a "badagent" code.
- *
- * @access private
- */
- function _checkHttpMethod() {
- /* Only HTTP method "GET" is allowed here */
- if ($_SERVER['REQUEST_METHOD'] != 'GET') {
- $this->debug('ERROR: HTTP method ' . $_SERVER['REQUEST_METHOD'] . ' is not allowed.');
- $this->_returnCode('badagent', Array ('HTTP/1.0 405 Method Not Allowed'));
- }
- }
-
- /**
- * Handles authentication. Requests HTTP authentication and if user/pw is submitted
- * check if they are valid
- *
- * @access private
- */
- function _checkAuthentication() {
- /* Request user/pw if not submitted yet */
- if (!isset($_SERVER['PHP_AUTH_USER'])) {
- $this->debug('No authentication data sent');
- $this->_returnCode('badauth', Array (
- 'WWW-Authenticate: Basic realm="DynDNS API Access"',
- 'HTTP/1.0 401 Unauthorized')
- );
- }
- $user = strtolower($_SERVER['PHP_AUTH_USER']);
- $password = $_SERVER['PHP_AUTH_PW'];
- if (! $this->_users->checkCredentials($user, $password)) {
- $this->_returnCode('badauth', Array ('HTTP/1.0 403 Forbidden'));
- }
- $this->_user = $user;
- }
-
- /**
- * Checks if all hostnames are valid (FQDN) and belong to the user
- *
- * @access private
- */
- function _checkHostnames() {
- foreach ($this->_hostnames as $hostname) {
- if (! DyndnsHelper::checkValidHost($hostname)) {
- $this->_returnCode('notfqdn');
- }
- if (! $this->_hosts->checkUserHost($this->_user, $hostname)) {
- $this->_returnCode('nohost');
- }
- }
- }
-
- /**
- * Updates all hosts
- *
- * @param string hostname Hostname
- * @param string myip IP address
- */
- function _updateHosts() {
- foreach ($this->_hostnames as $hostname) {
- if (! $this->_hosts->update($hostname, $this->_myIp) ) {
- $this->_returnCode('dnserr');
- }
- }
- /* Flush host database (write to hosts file) */
- if (! $this->_hosts->flush()) {
- $this->_returnCode('dnserr');
- }
- }
-
- /**
- * Returns a "Return code". The program exits after output.
- *
- * @param string code Return code (like "notfqdn")
- * @param array additionalHeaders HTTP headers to be added
- * @param string debugMessage Message for the debug log
- */
- function _returnCode($code, $additionalHeaders = Array (), $debugMessage = "") {
- foreach ($additionalHeaders as $header) {
- header($header);
- }
- $this->debug('Sending return code: ' . $code);
- echo $code;
- $this->_shutdown();
- }
-
- /**
- * Shuts down, closes files, writes debug output, etc.
- *
- * @access private
- */
- function _shutdown() {
- /* Write debug buffer */
- if ( ($this->_debugBuffer != "") && ($this->_config['debug'])) {
- if ($fh = @fopen($this->_config['debugFile'], 'a')) {
- fwrite($fh, $this->_debugBuffer);
- fclose($fh);
- }
- }
- exit;
- }
-
- /**
- * Saves a debug message (if debugging is turned on)
- *
- * @param string message Debug message
- */
- function debug($message) {
- $this->_debugBuffer .= date('M j G:i:s') . ' Dyndns: ' . $message . "\n";
- }
+ public function getConfig($key)
+ {
+ return $this->config[$key];
+ }
+
+ private function checkHttpMethod()
+ {
+ // Only HTTP method "GET" is allowed here
+ if ($_SERVER['REQUEST_METHOD'] != 'GET') {
+ $this->debug('ERROR: HTTP method ' . $_SERVER['REQUEST_METHOD'] . ' is not allowed.');
+ $this->returnCode('badagent', array('HTTP/1.0 405 Method Not Allowed'));
+ }
+ }
+
+ private function checkAuthentication()
+ {
+ // Request user/pw if not submitted yet
+ if (!isset($_SERVER['PHP_AUTH_USER'])) {
+ $this->debug('No authentication data sent');
+ $this->returnCode('badauth', array(
+ 'WWW-Authenticate: Basic realm="DynDNS API Access"',
+ 'HTTP/1.0 401 Unauthorized')
+ );
+ }
+
+ $user = strtolower($_SERVER['PHP_AUTH_USER']);
+ $password = $_SERVER['PHP_AUTH_PW'];
+ if (! $this->users->checkCredentials($user, $password)) {
+ $this->returnCode('badauth', array('HTTP/1.0 403 Forbidden'));
+ }
+
+ $this->user = $user;
+ }
+
+ private function checkHostnames()
+ {
+ foreach ($this->hostnames as $hostname) {
+ // check if the hostname is valid FQDN
+ if (! DyndnsHelper::checkValidHost($hostname)) {
+ $this->returnCode('notfqdn');
+ }
+
+ // check if the user is allowed to update the hostname
+ if (! $this->hosts->checkUserHost($this->user, $hostname)) {
+ $this->returnCode('nohost');
+ }
+ }
+ }
+
+ private function updateHosts()
+ {
+ foreach ($this->hostnames as $hostname) {
+ if (! $this->hosts->update($hostname, $this->myIp) ) {
+ $this->returnCode('dnserr');
+ }
+ }
+
+ // Flush host database (write to hosts file)
+ if (! $this->hosts->flush()) {
+ $this->returnCode('dnserr');
+ }
+ }
+
+ private function returnCode($code, $additionalHeaders = array(), $debugMessage = "")
+ {
+ foreach ($additionalHeaders as $header) {
+ header($header);
+ }
+ $this->debug('Sending return code: ' . $code);
+ echo $code;
+ $this->shutdown();
+ }
+
+ private function shutdown()
+ {
+ // Flush debug buffer
+ if (($this->debugBuffer != "") && ($this->config['debug'])) {
+ if ($fh = @fopen($this->config['debugFile'], 'a')) {
+ fwrite($fh, $this->debugBuffer);
+ fclose($fh);
+ }
+ }
+
+ exit;
+ }
+
+ private function debug($message)
+ {
+ $this->debugBuffer .= date('M j G:i:s') . ' Dyndns: ' . $message . "\n";
+ }
}
-?>
\ No newline at end of file
<?php
-/*
- * DynDNS Server Script
- * Copyright (c) 2007 Nico Kaiser
- *
- * http://siriux.net/
- */
/**
- * Collection of useful helper functions
+ * Helper functions.
*
* @package Dyndns
- * @author Nico Kaiser <nico@siriux.net>
- * @version $Revision: 13 $
- * @static
+ * @author Nico Kaiser <nico@kaiser.me>
*/
-class DyndnsHelper {
-
- /**
- * Simple function to check valid IP address
- *
- * @param string IP address
- * @return boolean True if IP is valid
- */
- function checkValidIp($ip) {
- if (! eregi("^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$", $ip))
- return false;
- $tmp = explode(".", $ip);
- foreach ($tmp as $sub) {
- $sub = $sub * 1;
- if ($sub < 0 || $sub > 256) return true;
- }
- return true;
- }
-
- /**
- * Simple function to check valid Hostname (FQDN)
- *
- * @param string Hostname
- * @return boolean True if Hostname is valid
- */
- function checkValidHost($hostname) {
- return eregi('^[a-z0-9.-]+$', $hostname);
- }
-
- /**
- * Tries to get the IPv4 of the client
- *
- * @param access public
- * @return string ip
- */
- function getMyIp() {
- $ip = $_SERVER['REMOTE_ADDR'];
- /* Some IPv6 Servers add ::ffff: */
- $ip = preg_replace('/^::ffff:/', '', $ip);
- return $ip;
- }
-
- /**
- * Compares if two hostnames are the same, with regard to a wildcard
- *
- * @param string host1
- * @param string host2
- * @return boolean true or false
- */
- function compareHosts($host1, $host2, $wildcard = false) {
- $a = explode('.', $host1);
- $b = explode('.', $host2);
- if (count($a) != count($b))
- return false;
- for ($i = 0; $i < count($a); $i++) {
- if (($wildcard === false) or (($a[$i] != $wildcard) and ($b[$i] != $wildcard)))
- if ($a[$i] != $b[$i])
- return false;
- }
- return true;
- }
+class DyndnsHelper
+{
+ /**
+ * Check valid IP address
+ *
+ * @param string IP address
+ * @return boolean True if IP is valid
+ */
+ public static function checkValidIp($ip)
+ {
+ if (! eregi("^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$", $ip))
+ return false;
+
+ $tmp = explode(".", $ip);
+ foreach ($tmp as $sub) {
+ $sub = $sub * 1;
+ if ($sub < 0 || $sub > 256) return true;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check valid hostname (FQDN)
+ *
+ * @param string Hostname
+ * @return boolean
+ */
+ public static function checkValidHost($hostname)
+ {
+ return eregi('^[a-z0-9.-]+$', $hostname);
+ }
+
+ /**
+ * Tries to get the IPv4 of the client
+ *
+ * @return string ip
+ */
+ public static function getMyIp()
+ {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ $ip = preg_replace('/^::ffff:/', '', $ip); // Some IPv6 Servers add ::ffff:
+ return $ip;
+ }
+
+ /**
+ * Compares if two hostnames are the same, with regard to a wildcard
+ *
+ * @param string host1
+ * @param string host2
+ * @return boolean
+ */
+ public static function compareHosts($host1, $host2, $wildcard = false)
+ {
+ $a = explode('.', $host1);
+ $b = explode('.', $host2);
+
+ if (count($a) != count($b))
+ return false;
+
+ for ($i = 0; $i < count($a); $i++) {
+ if (($wildcard === false) or (($a[$i] != $wildcard) and ($b[$i] != $wildcard)))
+ if ($a[$i] != $b[$i])
+ return false;
+ }
+
+ return true;
+ }
}
-?>
\ No newline at end of file
<?php
-/*
- * DynDNS Server Script
- * Copyright (c) 2007 Nico Kaiser
- *
- * http://siriux.net/
- */
/**
- * Host database
+ * Host database.
*
* @package Dyndns
- * @author Nico Kaiser <nico@siriux.net>
- * @version $Revision: 13 $
+ * @author Nico Kaiser <nico@kaiser.me>
*/
-class DyndnsHosts {
-
- /**
- * Filename of the hosts file (dyndns.hosts)
- * @var string
- * @access private
- */
- var $_hostsFile;
-
- /**
- * Host/Users array: 'hostname' => array ('user1', 'user2', ...)
- * @var array
- * @access private
- */
- var $_hosts;
-
- /**
- * List of updates in the format 'hostname' => 'ip'
- * @var array
- * @access
- */
- var $_updates;
-
- /**
- * This is true if the status / user files were read
- * @var boolean
- * @access private
- */
- var $_initialized;
-
-
- function DyndnsHosts($hostsFile) {
- $this->_hostsFile = $hostsFile;
- $this->_initialized = false;
- $this->_updates = array ();
- }
-
- /**
- * Adds an update to the list
- */
- function update($hostname, $ip) {
- if (! $this->_initialized)
- $this->_init();
-
- $GLOBALS['dyndns']->debug('Update: '.$hostname . ':'.$ip);
- $this->_updates[$hostname] = $ip;
- return true;
- }
-
- /**
- * Checks if the host belongs to the user
- *
- * @param string user Username
- * @param string hostname Hostname
- * @return boolean TRUE if the host belongs to the user
- */
- function checkUserHost($user, $hostname) {
- if ($hostname == 'members.dyndns.org') {
- $GLOBALS['dyndns']->debug('Cannot change members.dyndns.org');
- return false;
- }
- if (! DyndnsHelper::checkValidHost($hostname)) {
- $GLOBALS['dyndns']->debug('Invalid host: ' . $hostname);
- return false;
- }
- if (! $this->_initialized)
- $this->_init();
- if (is_array($this->_hosts)) {
- foreach ($this->_hosts as $line) {
- if (preg_match("/^(.*?):(.*)/", $line, $matches)) {
- if (DyndnsHelper::compareHosts($matches[1], $hostname, '*') &&
- in_array($user, explode(',', strtolower($matches[2])))) {
- return true;
- }
- }
- }
- }
- $GLOBALS['dyndns']->debug('Host '.$hostname.' does not belong to user '.$user);
- return false;
- }
-
- /**
- * Write cached changes to the status file
- *
- * @access public
- */
- function flush() {
- return $this->_updateBind();
- }
-
- /**
- * Initializes the user and status list from the file
- *
- * @access private
- */
- function _init() {
- if ($this->_initialized) return;
- $this->_readHostsFile();
- if (! is_array($this->_hosts)) {
- $this->_hosts = Array ();
- }
- $this->_initialized = true;
- }
-
- /**
- * Reads the contents of $_hostsFile into $_hosts
- *
- * @access private
- */
- function _readHostsFile() {
- $lines = @file($this->_hostsFile);
- if (is_array($lines)) {
- $this->_hosts = $lines;
- } else {
- $GLOBALS['dyndns']->debug('Empty hosts file: "' . $this->hostsFile . '"');
- }
- }
-
- /**
- * Sends DNS Updates to BIND server
- *
- * @access private
- */
- function _updateBind() {
- $server = $GLOBALS['dyndns']->getConfig('bind.server');
- $zone = $GLOBALS['dyndns']->getConfig('bind.zone');
- $ttl = $GLOBALS['dyndns']->getConfig('bind.ttl') * 1;
- $key = $GLOBALS['dyndns']->getConfig('bind.key');
-
- if (! DyndnsHelper::checkValidHost($server)) {
- $GLOBALS['dyndns']->debug('ERROR: Invalid bind.server config value');
- return false;
- }
- if (! DyndnsHelper::checkValidHost($zone)) {
- $GLOBALS['dyndns']->debug('ERROR: Invalid bind.zone config value');
- return false;
- }
- if (! is_int($ttl)) {
- $GLOBALS['dyndns']->debug('Invalid bind.ttl config value. Setting to default 300.');
- $ttl = 300;
- }
- if ($ttl < 60) {
- $GLOBALS['dyndns']->debug('bind.ttl is too low. Setting to default 300.');
- $ttl = 300;
- }
- if (! eregi('^[a-z0-9.-=/]+$', $key)) {
- $GLOBALS['dyndns']->debug('ERROR: Invalid bind.key config value');
- return false;
- }
-
- /* Create temp file with nsupdate commands */
- $tempfile = tempnam('/tmp', 'Dyndns');
- $fh = @fopen($tempfile, 'w');
- if (! $fh) {
- $GLOBALS['dyndns']->debug('ERROR: Could not open temporary file');
- return false;
- }
- fwrite($fh, "server $server\n");
- fwrite($fh, "zone $zone\n");
- $ttl = $GLOBALS['dyndns']->getConfig('bind.ttl');
- foreach ($this->_updates as $host => $ip) {
- fwrite($fh, "update delete $host A\n");
- fwrite($fh, "update add $host $ttl A $ip\n");
- }
- fwrite($fh, "send\n");
- fclose($fh);
-
- /* Execute nsupdate */
- $result = exec('/usr/bin/nsupdate -y ' . $key . ' ' . $tempfile . ' 2>&1');
- unlink($tempfile);
- if ($result != '') {
- $GLOBALS['dyndns']->debug('ERROR: nsupdate returns: ' . $result);
- return false;
- }
-
- return true;
- }
+class DyndnsHosts
+{
+ /**
+ * Filename of the hosts file (dyndns.hosts)
+ * @var string
+ */
+ private $hostsFile;
+
+ /**
+ * Host/Users array: 'hostname' => array ('user1', 'user2', ...)
+ * @var array
+ */
+ private $hosts;
+
+ /**
+ * List of updates in the format 'hostname' => 'ip'
+ * @var array
+ */
+ private $updates;
+
+ /**
+ * This is true if the status / user files were read
+ * @var boolean
+ */
+ private $initialized;
+
+ /**
+ * Constructor.
+ *
+ * @param string $hostsFile
+ */
+ public function __construct($hostsFile)
+ {
+ $this->hostsFile = $hostsFile;
+ $this->initialized = false;
+ $this->updates = array();
+ }
+
+ /**
+ * Adds an update to the list
+ *
+ * @param string $hostname
+ * @param string $ip
+ */
+ public function update($hostname, $ip)
+ {
+ if (! $this->initialized) {
+ $this->init();
+ }
+
+ $this->debug('Update: ' . $hostname . ':' . $ip);
+ $this->updates[$hostname] = $ip;
+ }
+
+ /**
+ * Checks if the host belongs to the user
+ *
+ * @param string $user
+ * @param string $hostname
+ * @return boolean True if the user is allowed to update the host
+ */
+ function checkUserHost($user, $hostname)
+ {
+ if ($hostname === 'members.dyndns.org') {
+ $this->debug('Cannot change members.dyndns.org');
+ return false;
+ }
+
+ if (! DyndnsHelper::checkValidHost($hostname)) {
+ $this->debug('Invalid host: ' . $hostname);
+ return false;
+ }
+
+ if (! $this->initialized) {
+ $this->init();
+ }
+
+ if (is_array($this->hosts)) {
+ foreach ($this->hosts as $line) {
+ if (preg_match("/^(.*?):(.*)/", $line, $matches)) {
+ if (DyndnsHelper::compareHosts($matches[1], $hostname, '*') &&
+ in_array($user, explode(',', strtolower($matches[2])))) {
+ return true;
+ }
+ }
+ }
+ }
+ $this->debug('Host '.$hostname.' does not belong to user '.$user);
+ return false;
+ }
+
+ /**
+ * Write cached changes to the status file
+ */
+ public function flush()
+ {
+ return $this->updateBind();
+ }
+
+ /**
+ * Initializes the user and status list from the file
+ *
+ * @access private
+ */
+ private function init()
+ {
+ if ($this->initialized) return;
+
+ $this->readHostsFile();
+ if (! is_array($this->hosts)) {
+ $this->hosts = array();
+ }
+
+ $this->initialized = true;
+ }
+
+ function readHostsFile()
+ {
+ $lines = @file($this->hostsFile);
+ if (is_array($lines)) {
+ $this->hosts = $lines;
+ } else {
+ $this->debug('Empty hosts file: "' . $this->hostsFile . '"');
+ }
+ }
+
+ /**
+ * Sends DNS Updates to BIND server
+ *
+ * @access private
+ */
+ private function updateBind()
+ {
+ $server = $this->getConfig('bind.server');
+ $zone = $this->getConfig('bind.zone');
+ $ttl = $this->getConfig('bind.ttl') * 1;
+ $key = $this->getConfig('bind.key');
+
+ // sanitiy checks
+ if (! DyndnsHelper::checkValidHost($server)) {
+ $this->debug('ERROR: Invalid bind.server config value');
+ return false;
+ }
+ if (! DyndnsHelper::checkValidHost($zone)) {
+ $this->debug('ERROR: Invalid bind.zone config value');
+ return false;
+ }
+ if (! is_int($ttl)) {
+ $this->debug('Invalid bind.ttl config value. Setting to default 300.');
+ $ttl = 300;
+ }
+ if ($ttl < 60) {
+ $this->debug('bind.ttl is too low. Setting to default 300.');
+ $ttl = 300;
+ }
+ if (! eregi('^[a-z0-9.-=/]+$', $key)) {
+ $this->debug('ERROR: Invalid bind.key config value');
+ return false;
+ }
+
+ // create temp file with nsupdate commands
+ $tempfile = tempnam('/tmp', 'Dyndns');
+ $fh = @fopen($tempfile, 'w');
+ if (! $fh) {
+ $this->debug('ERROR: Could not open temporary file');
+ return false;
+ }
+ fwrite($fh, "server $server\n");
+ fwrite($fh, "zone $zone\n");
+ $ttl = $this->getConfig('bind.ttl');
+ foreach ($this->updates as $host => $ip) {
+ fwrite($fh, "update delete $host A\n");
+ fwrite($fh, "update add $host $ttl A $ip\n");
+ }
+ fwrite($fh, "send\n");
+ fclose($fh);
+
+ // Execute nsupdate
+ $result = exec('/usr/bin/nsupdate -y ' . $key . ' ' . $tempfile . ' 2>&1');
+ unlink($tempfile);
+ if ($result != '') {
+ $this->debug('ERROR: nsupdate returns: ' . $result);
+ return false;
+ }
+
+ return true;
+ }
+
+ private function getConfig($key)
+ {
+ return $GLOBALS['dyndns']->getConfig($key);
+ }
+
+ private function debug($message)
+ {
+ return $GLOBALS['dyndns']->debug($message);
+ }
}
-?>
\ No newline at end of file
<?php
-/*
- * DynDNS Server Script
- * Copyright (c) 2007 Nico Kaiser
- *
- * http://siriux.net/
- */
/**
- * User database
+ * User database.
*
* @package Dyndns
- * @author Nico Kaiser <nico@siriux.net>
- * @version $Revision: 13 $
+ * @author Nico Kaiser <nico@kaiser.me>
*/
-class DyndnsUsers {
-
- /**
- * Filename of the users file
- * @var string
- * @access private
- */
- var $_userFile;
-
-
- function DyndnsUsers($userFile) {
- $this->_userFile = $userFile;
- }
-
- /**
- * Checks user credentials
- *
- * @param string user
- * @param string password
- * @access private
- */
- function checkCredentials($user, $password) {
- $lines = @file($this->_userFile);
- if (is_array($lines)) {
- foreach ($lines as $line) {
- if (preg_match("/^(.*?):(.*)/", $line, $matches)) {
- if (strtolower($matches[1]) == strtolower($user)) {
- $salt = substr($matches[2], 0, 2);
- if (crypt($password, $salt) == $matches[2]) {
- $GLOBALS['dyndns']->debug('Login successful for user ' . $user);
- return TRUE;
- } else {
- $GLOBALS['dyndns']->debug('Wrong password for user: ' . $user);
- }
- }
- }
- }
- } else {
- $GLOBALS['dyndns']->debug('Empty user file: "' . $this->_userFile . '"');
- }
- $GLOBALS['dyndns']->debug('Unknown user: ' . $user);
- return FALSE;
- }
+class DyndnsUsers
+{
+ private $userFile;
+
+ public function __construct($userFile)
+ {
+ $this->userFile = $userFile;
+ }
+
+ private function checkCredentials($user, $password)
+ {
+ $lines = @file($this->userFile);
+
+ if (is_array($lines)) {
+ foreach ($lines as $line) {
+ if (preg_match("/^(.*?):(.*)/", $line, $matches)) {
+ if (strtolower($matches[1]) == strtolower($user)) {
+ $salt = substr($matches[2], 0, 2);
+ if (crypt($password, $salt) == $matches[2]) {
+ $this->debug('Login successful for user ' . $user);
+ return true;
+ } else {
+ $this->debug('Wrong password for user: ' . $user);
+ return false;
+ }
+ }
+ }
+ }
+ } else {
+ $this->debug('Empty user file: "' . $this->userFile . '"');
+ }
+
+ $this->debug('Unknown user: ' . $user);
+ return false;
+ }
+
+ private function debug($message)
+ {
+ $GLOBALS['dyndns']->debug($message);
+ }
}
-?>
\ No newline at end of file
exit;
}
-
/*
* Location of the hosts database
*/
$dyndns->setConfig('hostsFile', 'conf/dyndns.hosts');
-
/*
* Location of the user database
*/
$dyndns->setConfig('userFile', 'conf/dyndns.user');
-
/*
* Enable debugging?
*/
$dyndns->setConfig('debug', true);
-
/*
* Debug filename
*/
-$dyndns->setConfig('debugFile', '/tmp/dyndns.log');
-
+$dyndns->setConfig('debugFile', '/tmp/dyndns.log');
/*
* Secret Key for BIND nsupdate
*/
$dyndns->setConfig('bind.key', 'dyndns.example.com:bvZfFHkl16wNGL/LuEUAqvlBeue9lw7C8GkHnQucN6jpKDMjOu29zFR6LlO5YlpNzYquDBmDSPVddX9SuFIK5A==');
-
/*
* Address of the BIND server. You can specify any remote DNS server here,
* if the server allows you to update data using bind.key
*/
$dyndns->setConfig('bind.server', 'localhost');
-
/*
* The BIND zone which retrieves the updates
*/
$dyndns->setConfig('bind.zone', 'dyndns.example.com');
-
/*
* Dynamic DNS entries will get this TTL
*/
$dyndns->setConfig('bind.ttl', '300');
-
-?>
\ No newline at end of file
<?php
-/*
- * DynDNS Server Script
- * Copyright (c) 2007 Nico Kaiser
- *
- * http://siriux.net/
- */
/**
* This script takes the same parameters as the original members.dyndns.org
* Remember: This script must be run as
* http://members.dyndns.org/nic/update
*
- * @author Nico Kaiser <nico@siriux.net>
- * @version $Revision: 13 $
+ * @author Nico Kaiser <nico@kaiser.me>
*/
error_reporting(E_ALL);
@include(dirname(__FILE__). '/config.php');
$dyndns->init();
-
-?>
\ No newline at end of file