PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

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