sddns/include/zone.php

228 lines
5.7 KiB
PHP
Executable file

<?php
/**
* Zone class
*
* @copyright 2013 Steffen Vogel
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <post@steffenvogel.de>
* @link http://www.steffenvogel.de
*/
/*
* This file is part of sddns
*
* sddns is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* sddns is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sddns. If not, see <http://www.gnu.org/licenses/>.
*/
class Zone extends NameServer implements Object {
public $name;
private $key;
function __construct($nserver, $name, $key, $nsport = 53) {
parent::__construct($nserver, $nsport);
$this->name = $name;
$this->key = $key;
}
protected function initQueue() {
parent::initQueue();
$this->queueCommand('zone ' . $this->name);
$this->queueCommand('key ' . $this->key['name'] . ' ' . $this->key['hmac']);
}
/*
* Maintenance
*/
function cleanup(Database $db) {
global $config;
global $output;
// expired records & records without host
$sql = 'DELETE r FROM ' . $config['db']['tbl']['records'] . ' AS r
LEFT JOIN ' . $config['db']['tbl']['hosts'] . ' AS h
ON h.id = r.host_id
WHERE (
(r.last_accessed + INTERVAL r.lifetime SECOND) < NOW()
&& h.zone = \'' . $db->escape($this->name) . '\'
&& r.lifetime != 0
) || h.id IS NULL';
$db->execute($sql);
if ($db->affectedRows() > 0) {
$output->add('records deleted from db', 'success', $db->affectedRows(), $this);
}
// expired urls & uris without host
$sql = 'DELETE u FROM ' . $config['db']['tbl']['uris'] . ' AS u
LEFT JOIN ' . $config['db']['tbl']['hosts'] . ' AS h
ON h.id = u.host_id
WHERE (
(u.last_accessed + INTERVAL u.lifetime SECOND) < NOW()
&& h.zone = \'' . $db->escape($this->name) . '\'
&& u.lifetime != 0
) || h.id IS NULL';
if ($db->affectedRows() > 0) {
$output->add('uris deleted from db', 'success', $db->affectedRows(), $this);
}
// hosts without records or url
$sql = 'DELETE h
FROM ' . $config['db']['tbl']['hosts'] . ' AS h
LEFT JOIN ' . $config['db']['tbl']['records'] . ' AS r
ON h.id = r.host_id
LEFT JOIN ' . $config['db']['tbl']['uris'] . ' AS u
ON h.id = u.host_id
WHERE
(r.id IS NULL && u.id IS NULL) &&
h.zone = \'' . $db->escape($this->name) . '\'';
$db->execute($sql);
if ($db->affectedRows() > 0) {
$output->add('hosts deleted from db', 'success', $db->affectedRows(), $this);
}
}
public function sync(Database $db) {
global $output;
$nsRecords = $this->getRecordsFromNS();
$dbRecords = $this->getRecordsFromDB($db);
$delete = array_diff($nsRecords, $dbRecords);
$add = array_diff($dbRecords, $nsRecords);
if (empty($delete) && empty($add)) {
$output->add('ns in sync', 'success');
return true;
}
$this->initQueue();
foreach ($add as $record) {
$this->add($record);
$output->add('record added to ns', 'success', $record);
}
foreach ($delete as $record) {
$this->delete($record);
$output->add('record deleted from ns', 'success', $record);
}
$result = $this->commitQueue();
if ($result['code']) {
throw new NameServerException('error during nameserver update', $result);
}
if (isAuthentificated()) {
$output->add('ns response', 'debug', 7, $result); // includes key!
}
$output->add('ns synced', 'success');
return true;
}
public function add(Record $record) {
global $config;
if ($record->host->zone->name != $this->name) {
throw new NameServerException('zone mismatch: trying to add record ' . $record . ' to zone ' . $this);
}
parent::add($record);
}
public function delete(Record $record) {
global $config;
if ($record->host->zone->name != $this->name) {
throw new NameServerException('zone mismatch: trying to delete record ' . $record . ' from zone ' . $this);
}
parent::delete($record);
}
/*
* Getter
*/
public function getRecordsFromNS() {
global $config;
global $output;
$records = array();
foreach (parent::query($this->name, 'AXFR') as $result) {
if (in_array($result[3], $config['sddns']['types']) && strlen($result[0]) > strlen($this->name) + 1) {
$hostname = substr($result[0], 0, -(strlen($this->name) + 2));
switch ($result[3]) {
case 'NS':
case 'MX':
case 'CNAME':
$rdata = substr($result[4], 0, -1);
break;
case 'TXT':
$rdata = trim($result[4], '"');
break;
default:
$rdata = $result[4];
}
try {
$host = new Host($hostname, $this);
$records[] = new Record($host, (int) $result[1], $result[2], $result[3], $rdata);
} catch (UserException $e) {
$output->add('error during parsing', 'error', $e);
}
}
}
return $records;
}
public function getRecordsFromDB(Database $db) {
return DBRecord::get($db, array('zone' => $this));
}
public function getUrisFromDB(Database $db) {
return DBUri::get($db, array('zone' => $this));
}
public function getHostsFromDB(Database $db) {
return DBHost::get($db, array('zone' => $this));
}
/*
* Output
*/
public function __toString() {
return parent::__toString() . '/' . $this->name;
}
public function toXml(DOMDocument $doc) {
$xmlZone = parent::toXml($doc, 'zone');
$xmlZone->appendChild($doc->createElement('zone', $this->name));
return $xmlZone;
}
public function toHtml() {
return $this;
}
}