You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dynamic_update.php 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <?php
  2. /* Poweradmin, a friendly web-based admin tool for PowerDNS.
  3. * See <http://www.poweradmin.org> for more details.
  4. *
  5. * Copyright 2007-2010 Rejo Zenger <rejo@zenger.nl>
  6. * Copyright 2010-2017 Poweradmin Development Team
  7. * <http://www.poweradmin.org/credits.html>
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. /**
  23. * Script that handles requests to update DNS records, required for clients
  24. * with dynamic ip addresses
  25. *
  26. * @package Poweradmin
  27. * @copyright 2007-2010 Rejo Zenger <rejo@zenger.nl>
  28. * @copyright 2010-2016 Poweradmin Development Team
  29. * @license http://opensource.org/licenses/GPL-3.0 GPL
  30. */
  31. require('inc/config.inc.php');
  32. require('inc/database.inc.php');
  33. require('inc/record.inc.php');
  34. $db = dbConnect();
  35. /** Make sql query safe
  36. *
  37. * @param mixed $value Unsafe Value
  38. *
  39. * @return mixed $value Safe Value
  40. */
  41. function safe($value)
  42. {
  43. global $db, $db_type;
  44. if ($db_type == 'mysql' || $db_type == 'sqlite') {
  45. $value = $db->quote($value, 'text');
  46. $value = substr($value, 1, -1); // remove quotes
  47. } elseif ($db_type == 'pgsql') {
  48. $value = pg_escape_string($value);
  49. } else {
  50. return status_exit('baddbtype');
  51. }
  52. return $value;
  53. }
  54. /** Get exit status message
  55. *
  56. * Print verbose status message for request
  57. *
  58. * @param string $status Short status message
  59. *
  60. * @return boolean false
  61. */
  62. function status_exit($status)
  63. {
  64. $verbose_codes = array(
  65. 'badagent' => 'Your user agent is not valid.',
  66. 'badauth' => 'No username available.',
  67. 'badauth2' => 'Invalid username or password. Authentication failed.',
  68. 'notfqdn' => 'The hostname you specified was not valid.',
  69. 'dnserr' => 'A DNS error has occurred on our end. We apologize for any inconvenience.',
  70. '!yours' => 'The specified hostname does not belong to you.',
  71. 'nohost' => 'The specified hostname does not exist.',
  72. 'good' => 'Your hostname has been updated.',
  73. '911' => 'A critical error has occurred on our end. We apologize for any inconvenience.',
  74. 'nochg' => 'This update was identical to your last update, so no changes were made to your hostname configuration.',
  75. 'baddbtype' => 'Unsupported database type',
  76. );
  77. if (isset($_REQUEST['verbose'])) {
  78. $pieces = preg_split('/\s/', $status);
  79. $status = $verbose_codes[$pieces[0]];
  80. }
  81. echo "$status\n";
  82. return false;
  83. }
  84. /** Check whether the given address is an IP address
  85. *
  86. * @param string $ip Given IP address
  87. *
  88. * @return string A if IPv4, AAAA if IPv6 or 0 if invalid
  89. */
  90. function valid_ip_address($ip)
  91. {
  92. if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
  93. $value = 'A';
  94. } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
  95. $value = 'AAAA';
  96. } else {
  97. $value = 0;
  98. }
  99. return $value;
  100. }
  101. if (!(isset($_SERVER)) && !$_SERVER['HTTP_USER_AGENT']) {
  102. return status_exit('badagent');
  103. }
  104. // Grab username & password based on HTTP auth, alternatively the query string
  105. if (isset($_SERVER['PHP_AUTH_USER'])) {
  106. $auth_username = $_SERVER['PHP_AUTH_USER'];
  107. } elseif (isset($_REQUEST['username'])) {
  108. $auth_username = $_REQUEST['username'];
  109. }
  110. if (isset($_SERVER['PHP_AUTH_PW'])) {
  111. $auth_password = $_SERVER['PHP_AUTH_PW'];
  112. } elseif (isset($_REQUEST['password'])) {
  113. $auth_password = $_REQUEST['password'];
  114. }
  115. // If we still don't have a username, throw up
  116. if (!isset($auth_username)) {
  117. header('WWW-Authenticate: Basic realm="DNS Update"');
  118. header('HTTP/1.0 401 Unauthorized');
  119. return status_exit('badauth');
  120. }
  121. $username = safe($auth_username);
  122. // FIXME: supports only md5 hashes
  123. $password = md5(safe($auth_password));
  124. $hostname = safe($_REQUEST['hostname']);
  125. // Grab IP to use
  126. $given_ip = "";
  127. $given_ip6 = "";
  128. if (!empty($_REQUEST['myip'])) {
  129. $given_ip = $_REQUEST['myip'];
  130. } elseif (!empty($_REQUEST['ip'])) {
  131. $given_ip = $_REQUEST['ip'];
  132. }
  133. if (!empty($_REQUEST['myip6'])) {
  134. $given_ip6 = $_REQUEST['myip6'];
  135. } elseif (!empty($_REQUEST['ip6'])) {
  136. $given_ip6 = $_REQUEST['ip6'];
  137. }
  138. if (valid_ip_address($given_ip) === 'AAAA') {
  139. $given_ip6 = $given_ip;
  140. }
  141. // Look for tag to grab the IP we coming from
  142. if (($given_ip6 == "whatismyip") && (valid_ip_address($_SERVER['REMOTE_ADDR']) === 'AAAA')) {
  143. $given_ip6 = $_SERVER['REMOTE_ADDR'];
  144. }
  145. if (($given_ip == "whatismyip") && (valid_ip_address($_SERVER['REMOTE_ADDR']) === 'A')) {
  146. $given_ip = $_SERVER['REMOTE_ADDR'];
  147. } elseif (($given_ip == "whatismyip") && (valid_ip_address($_SERVER['REMOTE_ADDR']) === 'AAAA') && (!(valid_ip_address($given_ip6) === 'AAAA'))) {
  148. $given_ip6 = $_SERVER['REMOTE_ADDR'];
  149. }
  150. // Finally get safe version of the IP
  151. $ip = safe($given_ip);
  152. $ip6 = safe($given_ip6);
  153. // Check its ok...
  154. if ((!valid_ip_address($ip)) && (!valid_ip_address($ip6))) {
  155. return status_exit('dnserr');
  156. }
  157. if (!strlen($hostname)) {
  158. return status_exit('notfqdn');
  159. }
  160. $user_query = "
  161. SELECT
  162. users.id
  163. FROM
  164. users, perm_templ, perm_templ_items, perm_items
  165. WHERE
  166. users.username = '$username'
  167. AND users.password = '$password'
  168. AND users.active = 1
  169. AND perm_templ.id = users.perm_templ
  170. AND perm_templ_items.templ_id = perm_templ.id
  171. AND perm_items.id = perm_templ_items.perm_id
  172. AND (
  173. perm_items.name = 'zone_content_edit_own'
  174. OR perm_items.name = 'zone_content_edit_others'
  175. )
  176. ";
  177. $user = $db->queryRow($user_query);
  178. if (!$user) {
  179. return status_exit('badauth2');
  180. }
  181. $zones_query = "SELECT domain_id FROM zones WHERE owner='{$user["id"]}'";
  182. $zones_result = $db->query($zones_query);
  183. $was_updated = false;
  184. $no_update_necessary = false;
  185. while ($zone = $zones_result->fetchRow()) {
  186. $zone_updated = false;
  187. $name_query = "SELECT name, type, content FROM records WHERE domain_id='{$zone["domain_id"]}' and (type = 'A' OR type = 'AAAA') ";
  188. $result = $db->query($name_query);
  189. while ($record = $result->fetchRow()) {
  190. if ($hostname == $record['name']) {
  191. if (($record['type'] == 'A') && (valid_ip_address($ip) === 'A')) {
  192. if ($ip == $record['content']) {
  193. $no_update_necessary = true;
  194. } else {
  195. $update_query = "UPDATE records SET content ='{$ip}' where name='{$record["name"]}' and type='A'";
  196. $update_result = $db->query($update_query);
  197. $zone_updated = true;
  198. $was_updated = true;
  199. }
  200. } elseif (($record['type'] == 'AAAA') && (valid_ip_address($ip6) === 'AAAA')) {
  201. if ($ip6 == $record['content']) {
  202. $no_update_necessary = true;
  203. } else {
  204. $update_query = "UPDATE records SET content ='{$ip6}' where name='{$record["name"]}' and type='AAAA'";
  205. $update_result = $db->query($update_query);
  206. $zone_updated = true;
  207. $was_updated = true;
  208. }
  209. }
  210. }
  211. }
  212. if ($zone_updated) {
  213. update_soa_serial($zone['domain_id']);
  214. }
  215. }
  216. return (($was_updated || $no_update_necessary) ? status_exit('good') : status_exit('!yours'));