dns.class.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <?php
  2. class dns {
  3. private $resolver = false;
  4. private $dns_servers = [];
  5. public $error = null;
  6. public $local_zones = [];
  7. public $clone_zones = [];
  8. public $local_zone_mappings = [];
  9. public $custom_config = "";
  10. public $needs_update = false;
  11. private function getDb() {
  12. $db = new SQLite3('../data/unbound.sqlite3');
  13. @$db->query("CREATE TABLE IF NOT EXISTS local_zones (id INTEGER PRIMARY KEY ASC, zone text, type varchar, active INTEGER default 1,primary_zone integer default 0,UNIQUE(zone))");
  14. @$db->query("CREATE TABLE IF NOT EXISTS local_zone_mapping (id INTEGER PRIMARY KEY ASC, hostname text, zone text, ip text, add_time TEXT,active INTEGER default 1,api_added INTEGER default 0,UNIQUE(hostname,ip))");
  15. @$db->query("CREATE TABLE clone_zones (zone text primary key, target_zone text,UNIQUE (zone))");
  16. return $db;
  17. }
  18. function load() {
  19. $db = $this->getDb();
  20. //Load up local zones
  21. $res = $db->query("select * from local_zones order by primary_zone desc, zone");
  22. while ($row = $res->fetchArray()) {
  23. $zone = [
  24. 'id'=>$row['id'],
  25. 'zone' =>$row['zone'],
  26. 'type' =>$row['type'],
  27. 'active' => $row['active'],
  28. 'primary_zone' => $row['primary_zone'],
  29. ];
  30. $this->local_zones[] = $zone;
  31. }
  32. $res = $db->query("select * from local_zone_mapping order by zone,hostname,ip");
  33. while ($row = $res->fetchArray()) {
  34. $mapping = [
  35. 'id'=>$row['id'],
  36. 'hostname' =>$row['hostname'],
  37. 'ip' =>$row['ip'],
  38. 'active' => $row['active'],
  39. 'add_time' => $row['add_time'],
  40. ];
  41. $this->local_zone_mappings[$row['zone']][] = $mapping;
  42. }
  43. $res = $db->query("select * from clone_zones");
  44. while ($row = $res->fetchArray()){
  45. $this->clone_zones[$row['target_zone']][] = $row['zone'];
  46. }
  47. //Get custom config file
  48. $this->custom_config = file_get_contents("../data/unbound.conf.d/custom.conf");
  49. if (file_exists("../data/ubound_needs_refresh.lck")) {
  50. $this->needs_update = true;
  51. }
  52. }
  53. private function updateCustomConfig($config) {
  54. file_put_contents("../data/unbound.conf.d/custom.conf",$config);
  55. touch ("../data/ubound_needs_refresh.lck");
  56. }
  57. private function addLocalZone($zone, $type) {
  58. $db = $this->getDb();
  59. $stmt = $db->prepare("insert into local_zones (zone,type) values (:zone,:type)");
  60. $stmt->bindValue(':zone', $zone, SQLITE3_TEXT);
  61. $stmt->bindValue(':type', $type, SQLITE3_TEXT);
  62. @$stmt->execute();
  63. touch ("../data/ubound_needs_refresh.lck");
  64. }
  65. private function addCloneZone($zone, $target_zone) {
  66. $db = $this->getDb();
  67. $stmt = $db->prepare("insert into clone_zones (zone,target_zone) values (:zone,:target_zone)");
  68. $stmt->bindValue(':zone', $zone, SQLITE3_TEXT);
  69. $stmt->bindValue(':target_zone', $target_zone, SQLITE3_TEXT);
  70. @$stmt->execute();
  71. touch ("../data/ubound_needs_refresh.lck");
  72. }
  73. public function deleteLocalZoneMapping($id) {
  74. $db = $this->getDb();
  75. $stmt = $db->prepare("delete from local_zone_mapping where id=:id");
  76. $stmt->bindValue(':id',$id,SQLITE3_INTEGER);
  77. @$stmt->execute();
  78. touch ("../data/ubound_needs_refresh.lck");
  79. }
  80. private function addLocalZoneMapping($hostname,$ip,$zone,$api_added=0) {
  81. if (!filter_var($ip, FILTER_VALIDATE_IP)) {
  82. return "{$ip} is an invalid IP address";
  83. }
  84. $db = $this->getDb();
  85. if ($api_added) {
  86. $str = "";
  87. if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
  88. $str = " and ip like '%:%' ";
  89. } else if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
  90. $str = " and ip like '%.%' ";
  91. }
  92. $stmt = $db->prepare("delete from local_zone_mapping where hostname=:hostname and zone=:zone {$str}");
  93. $stmt->bindValue(':hostname', $hostname, SQLITE3_TEXT);
  94. $stmt->bindValue(':zone', $zone, SQLITE3_TEXT);
  95. @$stmt->execute();
  96. }
  97. error_reporting(E_ALL);
  98. ini_set("display_errors", 1);
  99. $hostname = str_replace(".{$zone}","",$hostname);
  100. $stmt = $db->prepare("insert into local_zone_mapping (hostname,zone,ip,add_time,active,api_added) values (:hostname,:zone,:ip,:add_time,1,:api_added)");
  101. $stmt->bindValue(':hostname', $hostname, SQLITE3_TEXT);
  102. $stmt->bindValue(':ip', $ip, SQLITE3_TEXT);
  103. $stmt->bindValue(':add_time', time(), SQLITE3_INTEGER);
  104. $stmt->bindValue(':zone', $zone, SQLITE3_TEXT);
  105. $stmt->bindValue(':api_added', $api_added, SQLITE3_INTEGER);
  106. $stmt->execute();
  107. touch ("../data/ubound_needs_refresh.lck");
  108. return null;
  109. }
  110. private function generateZoneFile() {
  111. $fp = fopen("../data/unbound.conf.d/custom_hosts.conf","w");
  112. fwrite($fp,"server:\n");
  113. foreach ($this->local_zones as $zone=>$mapping) {
  114. if ($mapping['active'] == 1) {
  115. fwrite($fp,"\nlocal-zone: \"{$mapping['zone']}\" {$mapping['type']}\n");
  116. if ($mapping['primary_zone'] == 1) {
  117. fwrite($fp,"local-data-ptr: \"127.0.0.1 localhost\"\n");
  118. fwrite($fp,"local-data: \"localhost A 127.0.0.1\"\n");
  119. fwrite($fp,"local-data-ptr: \"::1 localhost\"\n");
  120. fwrite($fp,"local-data: \"localhost AAAA ::1\"\n");
  121. }
  122. $all_zones = [$mapping['zone']];
  123. if (isset($this->clone_zones[$mapping['zone']]) && count($this->clone_zones[$mapping['zone']])) {
  124. $all_zones = array_merge($all_zones,$this->clone_zones[$mapping['zone']]);
  125. }
  126. foreach ($all_zones as $processing_zone) {
  127. //Start a new zone
  128. if ($processing_zone != $mapping['zone']) {
  129. fwrite($fp,"\nlocal-zone: \"{$processing_zone}\" {$mapping['type']}\n");
  130. }
  131. if (isset($this->local_zone_mappings[$mapping['zone']]) && count($this->local_zone_mappings[$mapping['zone']])) {
  132. foreach ($this->local_zone_mappings[$mapping['zone']] as $lzone=>$lzone_mapping) {
  133. if ($lzone_mapping['active'] == 1) {
  134. $record_type = "A";
  135. if (strstr($lzone_mapping['ip'],":")) {
  136. $record_type = "AAAA";
  137. }
  138. fwrite($fp,"local-data: \"{$lzone_mapping['hostname']}.{$processing_zone} {$record_type} {$lzone_mapping['ip']}\"\n");
  139. if ($mapping['zone'] == $processing_zone && $mapping['primary_zone'] == 1) {
  140. fwrite($fp,"local-data-ptr: \"{$lzone_mapping['ip']} {$lzone_mapping['hostname']}.{$processing_zone}\"\n");
  141. }
  142. }
  143. }
  144. }
  145. }
  146. /*
  147. if (count($this->local_zone_mappings[$mapping['zone']])) {
  148. foreach ($this->local_zone_mappings[$mapping['zone']] as $lzone=>$lzone_mapping) {
  149. $host = $lzone_mapping['hostname'];
  150. $current_zone =
  151. fwrite($ub_fp,"local-data: \"{$host}.{$zone} {$record_type} {$ip}\"\n");
  152. fwrite($ub_fp,"local-data-ptr: \"{$ip} {$host}.{$zone}\"\n");
  153. }
  154. }
  155. */
  156. }
  157. }
  158. fclose($fp);
  159. }
  160. private function applyChanges() {
  161. $this->load();
  162. $this->generateZoneFile();
  163. @unlink("../data/ubound_needs_refresh.lck");
  164. touch("../data/ubound_force_reload.lck");
  165. }
  166. public function doPost($post) {
  167. foreach ($post as $key=>$val) {
  168. $post[$key] = $val;
  169. }
  170. if (!empty($post['new_local_zone']) && strlen($post['new_local_zone'])>0 && strlen($post['new_local_zone_type'])>0) {
  171. $this->addLocalZone($post['new_local_zone'],$post['new_local_zone_type']);
  172. }
  173. if (!empty($post['clone_zone']) && strlen($post['target_zone'])>0) {
  174. $this->addCloneZone($post['clone_zone'],$post['target_zone']);
  175. }
  176. if (!empty($post['custom_config']) && strlen($post['custom_config'])>0) {
  177. $this->updateCustomConfig($post['custom_config']);
  178. }
  179. if (!empty($post['new_local_zone_mapping']) && strlen($post['new_local_zone_mapping'])>0 && strlen($post['new_local_zone_mapping_ip'])>0 && strlen($post['zone'])>0) {
  180. $api_added = isset($post['api_added']) ? 1:0;
  181. $post['new_local_zone_mapping_ip'] = trim($post['new_local_zone_mapping_ip']);
  182. $this->error = $this->addLocalZoneMapping($post['new_local_zone_mapping'],$post['new_local_zone_mapping_ip'],$post['zone'],$api_added);
  183. if (isset($post['force_update']) && $post['force_update'] == 1) {
  184. $this->applyChanges();
  185. }
  186. }
  187. if (!empty($post['applyChanges'])) {
  188. $this->applyChanges();
  189. }
  190. }
  191. public function __construct($postvars=null) {
  192. $this->load();
  193. if ($postvars != null) {
  194. $this->doPost($postvars);
  195. }
  196. }
  197. }