PageRenderTime 330ms CodeModel.GetById 61ms app.highlight 198ms RepoModel.GetById 54ms app.codeStats 1ms

/code/classes/Daemon/DNSd/DbEngine.class.php

https://github.com/blekkzor/pinetd2
PHP | 423 lines | 292 code | 108 blank | 23 comment | 48 complexity | e1f567f0a19d4fc532dd086e04416a16 MD5 | raw file
  1<?php
  2
  3namespace Daemon\DNSd;
  4
  5use pinetd\Logger;
  6use pinetd\Timer;
  7use pinetd\SQL;
  8
  9class DbEngine {
 10	protected $localConfig;
 11	protected $tcp;
 12	protected $parent;
 13	protected $domainHitCache = array();
 14
 15	function __construct($parent, $localConfig, $IPC) {
 16		$this->localConfig = $localConfig;
 17
 18		// check table struct
 19		$this->sql = SQL::Factory($this->localConfig['Storage']);
 20
 21		$storage = relativeclass($this, 'Storage');
 22		$storage::validateTables($this->sql);
 23
 24		$tcp = $IPC->openPort('DNSd::TCPMaster::'.$this->sql->unique());
 25
 26		$this->tcp = $tcp;
 27		$this->parent = $parent;
 28
 29		Timer::addTimer(array($this, 'processHits'), 30, $extra = null, true);
 30	}
 31
 32	function __call($func, $args) {
 33		return NULL;
 34	}
 35
 36	function unique() {
 37		return $this->sql->unique();
 38	}
 39
 40	public function processHits() {
 41		if (!$this->domainHitCache) return true;
 42
 43		if (isset($this->localConfig['Master'])) {
 44			// we are slave
 45			foreach($this->domainHitCache as $domain => $hits) {
 46				$this->parent->domainHit($domain, $hits);
 47			}
 48			$this->domainHitCache = array();
 49		}
 50
 51		return true;
 52	}
 53
 54	public function getDomainHits() {
 55		$res = $this->domainHitCache;
 56		$this->domainHitCache = array();
 57		return $res;
 58	}
 59
 60	protected function tableKey($table) {
 61		switch($table) {
 62			case 'domains': return 'key';
 63			case 'zone_records': return 'record_id';
 64			case 'zones': return 'zone_id';
 65			case 'deletions': return 'key';
 66			default: return NULL;
 67		}
 68	}
 69
 70	protected function doDelete($table, $key, $value) {
 71		$this->sql->query('BEGIN TRANSACTION');
 72
 73		// delete entry
 74		if (!$this->sql->query('DELETE FROM `'.$table.'` WHERE `'.$key.'` = '.$this->sql->quote_escape($value))) {
 75			$this->sql->query('ROLLBACK');
 76			return false;
 77		}
 78
 79		if ($this->sql->affected_rows < 1) {
 80			$this->sql->query('ROLLBACK');
 81			return false;
 82		}
 83		
 84		// store delete event and dispatch it
 85		$insert = array(
 86			'deletion_table' => $table,
 87			'deletion_id' => $value,
 88			'changed' => $this->sql->now(),
 89		);
 90		if (!$this->sql->insert('deletions', $insert)) {
 91			$this->sql->query('ROLLBACK');
 92			return false;
 93		}
 94
 95		$insert['key'] = $this->sql->insert_id;
 96
 97		$this->sql->query('COMMIT');
 98
 99		$this->tcp->dispatch('deletions', $insert['key'], $insert);
100
101		return true;
102	}
103
104	public function processUpdate($table, $data) {
105		if (!isset($this->localConfig['Master'])) return NULL; // I GOT NO MASTERS!
106
107		$key = $this->tableKey($table);
108		if (is_null($key)) {
109			Logger::log(Logger::LOG_WARN, 'Got update from DNSd master for unknown table '.$table);
110			return;
111		}
112		$key_val = $data[$key];
113
114		if ($this->sql->query('SELECT 1 FROM `'.$table.'` WHERE `'.$key.'` = '.$this->sql->quote_escape($key_val))->fetch_assoc()) {
115			// update
116			unset($data[$key]);
117			$req = '';
118			foreach($data as $var => $val) $req.=($req==''?'':',').'`'.$var.'` = '.$this->sql->quote_escape($val);
119			$req = 'UPDATE `'.$table.'` SET '.$req.' WHERE `'.$key.'` = '.$this->sql->quote_escape($key_val);
120			$this->sql->query($req);
121		} else {
122			$this->sql->insert($table, $data);
123		}
124
125		if ($table == 'deletions') {
126			$delete_key = $this->tableKey($data['deletion_table']);
127			if (!is_null($delete_key)) {
128				$req = 'DELETE FROM `'.$data['deletion_table'].'` WHERE `'.$delete_key.'` = '.$this->sql->quote_escape($data['deletion_id']);
129				$this->sql->query($req);
130			}
131		}
132	}
133
134	public function domainHit($domain, $hit_count = 1) {
135		$this->domainHitCache[strtolower($domain)] += $hit_count;
136	}
137
138	/*****
139	 ** Zones management
140	 *****/
141
142	public function createZone($zone) {
143		// Try to insert zone
144		$zone = strtolower($zone);
145		$now = $this->sql->now();
146
147		$data = array(
148			'zone' => $zone,
149			'created' => $now,
150			'changed' => $now,
151		);
152
153		if (!$this->sql->insert('zones', $data)) return false;
154
155		$id = $this->sql->insert_id;
156		$data['zone_id'] = $id;
157		$this->tcp->dispatch('zones', $id, $data);
158
159		return $id;
160	}
161
162	public function getZone($zone) {
163		$res = $this->sql->query('SELECT `zone_id` FROM `zones` WHERE `zone` = '.$this->sql->quote_escape($zone))->fetch_assoc();
164
165		return $res['zone_id'];
166	}
167
168	public function deleteZone($zone) {
169		if (!is_numeric($zone)) {
170			$zone = $this->getZone($zone);
171		}
172		if (!$zone) return false;
173
174		return $this->doDelete('zones', 'zone_id', $zone);
175	}
176
177	/*****
178	 ** Records management
179	 *****/
180
181	public function addRecord($zone, $host, $type, $value, $ttl = 86400) {
182		if (!is_numeric($zone)) {
183			$zone = $this->getZone($zone);
184		}
185		if (!$zone) return NULL;
186
187		if (!is_array($value)) {
188			$value = array('data' => $value);
189		}
190
191		$allowed = array('mx_priority', 'data', 'resp_person', 'serial', 'refresh', 'retry', 'expire', 'minimum', 'changed');
192
193		$insert = array();
194
195		foreach($allowed as $var) {
196			if (array_key_exists($var, $value)) $insert[$var] = $value[$var];
197		}
198
199		$insert['zone'] = $zone;
200		$insert['host'] = strtolower($host);
201		$insert['type'] = strtoupper($type);
202		$insert['ttl'] = $ttl;
203		$insert['changed'] = $this->sql->now();
204
205		$fields = '';
206		$values = '';
207		foreach($insert as $var => $val) {
208			$fields .= ($fields == ''?'':', ') . '`' . $var . '`';
209			$values .= ($values == ''?'':', ') . $this->sql->quote_escape($val);
210		}
211
212		$res = $this->sql->insert('zone_records', $insert);
213		if (!$res) return false;
214
215		$id = $this->sql->insert_id;
216		$insert['record_id'] = $id;
217
218		$this->tcp->dispatch('zone_records', $id, $insert);
219
220		return $id;
221	}
222
223	public function changeRecord($record, $host, $type, $value, $ttl = NULL) {
224
225		// load this record
226		$found = $this->sql->query('SELECT 1 FROM `zone_records` WHERE `record_id` = '.$this->sql->quote_escape($record))->fetch_assoc();
227		if (!$found) return false;
228
229		$data = array();
230
231		if (is_null($value)) {
232			$value = array();
233		} elseif (!is_array($value)) {
234			$value = array('data' => $value);
235		}
236
237		$allowed = array('mx_priority', 'data', 'resp_person', 'serial', 'refresh', 'retry', 'expire', 'minimum', 'changed');
238
239		foreach($allowed as $var) {
240			if (isset($value[$var])) $data[$var] = $value[$var];
241		}
242
243		if (!is_null($host)) $data['host'] = strtolower($host);
244		if (!is_null($type)) $data['type'] = strtoupper($type);
245		if (!is_null($ttl)) $data['ttl'] = $ttl;
246		$data['changed'] = $this->sql->now();
247
248		$req = '';
249
250		foreach($data as $var => $val) {
251			$req .= ($req == ''?'':', ') . '`' . $var . '` = ' . $this->sql->quote_escape($val);
252		}
253
254		$req = 'UPDATE `zone_records` SET '.$req.' WHERE `record_id` = '.$this->sql->quote_escape($record);
255
256		$data['record_id'] = $record;
257
258		$this->tcp->dispatch('zone_records', $record, $data);
259
260		return (bool)$this->sql->query($req);
261	}
262
263	public function deleteRecord($rid) {
264		if (!$rid) return false;
265
266		return $this->doDelete('zone_records', 'record_id', $rid);
267	}
268
269	public function dumpZone($zone, $start = 0, $limit = 500) {
270		if (!is_numeric($zone)) {
271			$zone = $this->getZone($zone);
272		}
273
274		$req = 'SELECT * FROM `zone_records` WHERE `zone` = '.$this->sql->quote_escape($zone).' LIMIT '.((int)$start).','.((int)$limit);
275		$res = $this->sql->query($req);
276
277		$final_res = array();
278
279		while($row = $res->fetch_assoc()) {
280			foreach($row as $var=>$val) if (is_null($val)) unset($row[$var]);
281			$final_res[] = $row;
282		}
283
284		return $final_res;
285	}
286
287	/*****
288	 ** Domains management
289	 *****/
290
291	public function createDomain($domain, $zone) {
292		// Try to insert domain
293		if (!is_numeric($zone)) {
294			$zone = $this->getZone($zone);
295		}
296		$domain = strtolower($domain);
297		$insert = array(
298			'domain' => $domain,
299			'zone' => $zone,
300			'created' => $this->sql->now(),
301			'changed' => $this->sql->now(),
302		);
303
304		$res = $this->sql->insert('domains', $insert);
305		if (!$res) return false;
306
307		$id = $this->sql->insert_id;
308		$insert['key'] = $id;
309
310		$this->tcp->dispatch('domains', $id, $insert);
311		return $id;
312	}
313
314	// connect a domain to another zone
315	public function changeDomain($domain, $zone) {
316		if (!is_numeric($zone)) {
317			$zone = $this->getZone($zone);
318		}
319
320		if (!is_numeric($domain)) {
321			$domain = $this->getDomain($domain);
322		}
323
324		// load this domain
325		$found = $this->sql->query('SELECT * FROM `domains` WHERE `key` = '.$this->sql->quote_escape($domain))->fetch_assoc();
326		if (!$found) return false;
327
328		$data = $found;
329		unset($data['key']);
330
331		$data['changed'] = $this->sql->now();
332		$data['zone'] = $zone;
333		$data['pzc_zone'] = NULL;
334		$data['pzc_stamp'] = NULL;
335
336		$req = '';
337
338		foreach($data as $var => $val) {
339			$req .= ($req == ''?'':', ') . '`' . $var . '` = ' . $this->sql->quote_escape($val);
340		}
341
342		$req = 'UPDATE `domains` SET '.$req.' WHERE `key` = '.$this->sql->quote_escape($domain);
343
344		$data['key'] = $domain;
345
346		if (!$this->sql->query($req)) return false;
347
348		$this->tcp->dispatch('domains', $domain, $data);
349
350		return true;
351	}
352
353	public function domainPzc($domain, $pzc_zone, $pzc_stamp) {
354		if (!is_numeric($pzc_zone)) {
355			$pzc_zone = $this->getZone($pzc_zone);
356		}
357		if (!is_numeric($domain)) {
358			$domain = $this->getDomain($domain);
359		}
360
361		if ($pzc_stamp < time()) {
362			return $this->changeDomain($domain, $pzc_zone);
363		}
364
365		// load this domain
366		$found = $this->sql->query('SELECT * FROM `domains` WHERE `key` = '.$this->sql->quote_escape($domain))->fetch_assoc();
367		if (!$found) return false;
368		
369		$data = $found;
370		unset($data['key']);
371
372		$data['changed'] = $this->sql->now();
373		$data['pzc_zone'] = $pzc_zone;
374		$data['pzc_stamp'] = $pzc_stamp;
375
376		$req = '';
377		foreach($data as $var => $val) {
378			$req .= ($req == ''?'':', ') . '`' . $var . '` = ' . $this->sql->quote_escape($val);
379		}
380
381		$req = 'UPDATE `domains` SET '.$req.' WHERE `key` = '.$this->sql->quote_escape($domain);
382
383		$data['key'] = $domain;
384
385		if (!$this->sql->query($req)) return false;
386
387		$this->tcp->dispatch('domains', $domain, $data);
388
389		return true;
390	}
391
392	public function getDomain($domain) {
393		$res = $this->sql->query('SELECT `key` FROM `domains` WHERE `domain` = '.$this->sql->quote_escape($domain))->fetch_assoc();
394
395		return $res['key'];
396	}
397
398	public function deleteDomain($domain) {
399		if (!is_numeric($domain)) {
400			$domain = $this->getDomain($domain);
401		}
402		if (!$domain) return false;
403
404		return $this->doDelete('domains', 'key', $domain);
405	}
406
407	/*****
408	 ** Etc
409	 *****/
410
411	public function lastUpdateDate() {
412		$recent = 0;
413
414		foreach(array('deletions', 'domains', 'zone_records', 'zones') as $table) {
415			$req = 'SELECT UNIX_TIMESTAMP(MAX(`changed`)) AS changed FROM `'.$table.'`';
416			$res = $this->sql->query($req)->fetch_assoc();
417			if ($res) $recent = max($recent, $res['changed']);
418		}
419
420		return $recent;
421	}
422}
423