<?php
require_once('.ddns_config.php');
/**
* @author Gernot WALZL
*/
class Authenticator {
protected $config = null;
function __construct($config) {
$this->config = $config;
}
function authenticate() {
header('WWW-Authenticate: Basic');
header('HTTP/1.0 401 Unauthorized');
return false; // if user hits cancel
}
function verify($username, $password) {
$result = false;
if (isset($this->config->pass_hashes[$username])) {
$pass_hash = $this->config->pass_hashes[$username];
$result = password_verify($password, $pass_hash);
}
return $result;
}
}
/**
* @author Gernot WALZL
*/
class NSUpdater {
protected $config = null;
function __construct($config) {
$this->config = $config;
}
function update_record($record) {
$handle = popen('/usr/bin/nsupdate', 'w');
if ($handle) {
fwrite($handle, 'server '.$this->config->server."\n");
fwrite($handle, 'zone '.$this->config->zone."\n");
fwrite($handle, 'key rndc-key '.$this->config->key."\n");
fwrite($handle, 'update delete '.$record['host'].' '.
$record['type']."\n");
fwrite($handle, 'update add '.$record['host'].' '.
$record['ttl'].' '.$record['type'].' '.$record['addr']."\n");
fwrite($handle, 'send');
pclose($handle);
}
}
}
/**
* @author Gernot WALZL
*/
class DDNSHTML {
function println($msg) {
print($msg."\n");
}
function print_head($title='DDNS') {
$this->println('<!DOCTYPE html>');
$this->println('<html lang="en">');
$this->println('<head>');
$this->println('<meta charset="utf-8" />');
$this->println('<title>'.$title.'</title>');
$this->println('</head>');
$this->println('<body>');
}
function print_foot() {
$this->println('</body>');
$this->println('</html>');
}
function print_h1() {
$this->println('<h1>Dynamic DNS</h1>');
}
function print_navigation() {
$this->println('<ul>');
$this->println('<li><a href="'.$_SERVER['PHP_SELF'].'?checkip">'.
'Check current IP</a></li>');
$this->println('<li><a href="'.$_SERVER['PHP_SELF'].'?update">'.
'Update DNS</a></li>');
$this->println('<li><a href="'.$_SERVER['PHP_SELF'].'?client">'.
'Download Client</a></li>');
$this->println('</ul>');
}
function print_unauthorized() {
$this->println('<h1>Unauthorized</h1>');
$this->println('<p>This server could not verify that you are '.
'authorized to access the document requested.</p>');
}
function print_form_update($zone, $hostnames) {
$this->println('<form method="post" '.
'action="'.$_SERVER['PHP_SELF'].'?update">');
$this->println('<fieldset>');
$this->println('<legend>Update</legend>');
$this->println('<table>');
$this->println('<tr>');
$this->println('<td><label for="zone">Zone:</label></td>');
$this->println('<td><input type="text" id="zone" name="zone" '.
'value="'.$zone.'" disabled /></td>');
$this->println('</tr>');
$this->println('<tr>');
$this->println('<td><label for="hostname">Hostname:</label></td>');
$this->println('<td><select id="hostname" name="hostname">');
foreach ($hostnames as $hostname) {
$this->println('<option value="'.$hostname.'">'.$hostname.'</option>');
}
$this->println('</select></td>');
$this->println('</tr>');
$this->println('<tr>');
$this->println('<td><label for="addr">Address:</label></td>');
$this->println('<td><input type="text" id="addr" name="addr" '.
'value="'.$_SERVER['REMOTE_ADDR'].'" /></td>');
$this->println('</tr>');
$this->println('</table>');
$this->println('<input type="submit" value="Submit" />');
$this->println('</fieldset>');
$this->println('</form>');
}
function print_record($record) {
$this->println('<pre>');
$this->println($record['host'].'. '.
$record['ttl'].' '.
$record['class'].' '.
$record['type'].' '.
$record['addr']);
$this->println('</pre>');
}
}
function client () {
header('Content-Type: application/x-sh');
header('Content-Disposition: attachment; filename="ddnsc.sh"');
?>
#!/bin/sh
# ddnsc.sh
# 2020-05-31
# by Gernot Walzl
URL="https://gernot-walzl.at/ddns.php"
USER="username"
PASS="password"
HOST="$(hostname)"
if [ -r /etc/ddnsc.conf ]; then
. /etc/ddnsc.conf
fi
print_usage () {
echo "Usage: $0 {checkip, update [addr], daemon [interval]}"
}
checkip () {
wget -4 -q -O - "${URL}?checkip"
}
update () {
local POST_DATA="hostname=$HOST"
if [ ! -z "$1" ]; then
POST_DATA="${POST_DATA}&addr=$1"
fi
wget -4 -q -O - \
--http-user="$USER" --http-password="$PASS" \
--post-data "$POST_DATA" "${URL}?update"
}
daemon () {
local INTERVAL="$1"
if [ -z "$INTERVAL" ]; then
INTERVAL=600
fi
local CURRENT_ADDR;
local UPDATED_ADDR;
while :; do
CURRENT_ADDR="$(checkip)"
if [ "$CURRENT_ADDR" != "$UPDATED_ADDR" ]; then
if update "$CURRENT_ADDR" > /dev/null ; then
UPDATED_ADDR="$CURRENT_ADDR"
fi
fi
sleep "$INTERVAL";
done
}
case "$1" in
'checkip')
checkip
;;
'update')
update "$2"
;;
'daemon')
daemon "$2" &
;;
*)
print_usage
esac
<?php
}
if (isset($_GET['checkip'])) {
print($_SERVER['REMOTE_ADDR']."\n");
} else if (isset($_GET['update'])) {
$html = new DDNSHTML();
$config = new DDNSConfig();
$auth = new Authenticator($config);
$user = '';
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
if ($auth->verify($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) {
$user = $_SERVER['PHP_AUTH_USER'];
}
}
if (empty($user)) {
if (!$auth->authenticate()) {
$html->print_head('401 Unauthorized');
$html->print_unauthorized();
$html->print_foot();
}
} else {
$hostname = '';
if (isset($_GET['hostname'])) {
$hostname = $_GET['hostname'];
} else if (isset($_POST['hostname'])) {
$hostname = $_POST['hostname'];
}
$html->print_head();
if (empty($hostname)) {
$html->print_h1();
$html->print_form_update($config->zone, $config->hostnames[$user]);
} else {
$arr_hostname = explode('.', $hostname, 2);
$hostname = $arr_hostname[0];
if (!empty($hostname) &&
in_array($hostname, $config->hostnames[$user])) {
$addr = $_SERVER['REMOTE_ADDR'];
if (isset($_GET['addr'])) {
$addr = $_GET['addr'];
} else if (isset($_POST['addr'])) {
$addr = $_POST['addr'];
}
$in_addr = inet_pton($addr);
if ($in_addr) {
$record = array();
$record['host'] = $hostname.'.'.$config->zone;
$record['ttl'] = $config->ttl;
$record['class'] = 'IN';
$record['type'] = 'A';
if (strlen($in_addr) == 16) {
$record['type'] = 'AAAA';
}
$record['addr'] = $addr;
$updater = new NSUpdater($config);
$updater->update_record($record);
$html->print_record($record);
} else {
$html->println('<p>ERROR: Given address is not valid.</p>');
}
} else {
$html->println('<p>ERROR: Hostname not accepted.</p>');
}
}
$html->print_foot();
}
} else if (isset($_GET['client'])) {
client();
} else {
$html = new DDNSHTML();
$html->print_head();
$html->print_h1();
$html->print_navigation();
$html->print_foot();
}
?>