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.

dns.inc.php 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  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-2009 Rejo Zenger <rejo@zenger.nl>
  6. * Copyright 2010-2014 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. * DNS functions
  24. *
  25. * @package Poweradmin
  26. * @copyright 2007-2010 Rejo Zenger <rejo@zenger.nl>
  27. * @copyright 2010-2014 Poweradmin Development Team
  28. * @license http://opensource.org/licenses/GPL-3.0 GPL
  29. */
  30. /** Validate DNS record input
  31. *
  32. * @param int $rid Record ID
  33. * @param int $zid Zone ID
  34. * @param string $type Record Type
  35. * @param mixed $content content part of record
  36. * @param mixed $name Name part of record
  37. * @param mixed $prio Priority
  38. * @param mixed $ttl TTL
  39. *
  40. * @return boolean true on success, false otherwise
  41. */
  42. function validate_input($rid, $zid, $type, &$content, &$name, &$prio, &$ttl) {
  43. $zone = get_zone_name_from_id($zid); // TODO check for return
  44. if (!(preg_match("/$zone$/i", $name))) {
  45. if (isset($name) && $name != "") {
  46. $name = $name . "." . $zone;
  47. } else {
  48. $name = $zone;
  49. }
  50. }
  51. switch ($type) {
  52. case "A":
  53. if (!is_valid_ipv4($content)) {
  54. return false;
  55. }
  56. if (!is_valid_rr_cname_exists($name, $rid)) {
  57. return false;
  58. }
  59. if (!is_valid_hostname_fqdn($name, 1)) {
  60. return false;
  61. }
  62. break;
  63. case "AAAA":
  64. if (!is_valid_ipv6($content)) {
  65. return false;
  66. }
  67. if (!is_valid_rr_cname_exists($name, $rid)) {
  68. return false;
  69. }
  70. if (!is_valid_hostname_fqdn($name, 1)) {
  71. return false;
  72. }
  73. break;
  74. case "AFSDB": // TODO: implement validation.
  75. break;
  76. case "CERT": // TODO: implement validation.
  77. break;
  78. case "CNAME":
  79. if (!is_valid_rr_cname_name($name)) {
  80. return false;
  81. }
  82. if (!is_valid_rr_cname_unique($name, $rid)) {
  83. return false;
  84. }
  85. if (!is_valid_hostname_fqdn($name, 1)) {
  86. return false;
  87. }
  88. if (!is_valid_hostname_fqdn($content, 0)) {
  89. return false;
  90. }
  91. if (!is_not_empty_cname_rr($name, $zone)) {
  92. return false;
  93. }
  94. break;
  95. case 'DHCID': // TODO: implement validation
  96. break;
  97. case 'DLV': // TODO: implement validation
  98. break;
  99. case 'DNSKEY': // TODO: implement validation
  100. break;
  101. case 'DS': // TODO: implement validation
  102. break;
  103. case 'EUI48': // TODO: implement validation
  104. break;
  105. case 'EUI64': // TODO: implement validation
  106. break;
  107. case "HINFO":
  108. if (!is_valid_rr_hinfo_content($content)) {
  109. return false;
  110. }
  111. if (!is_valid_hostname_fqdn($name, 1)) {
  112. return false;
  113. }
  114. break;
  115. case 'IPSECKEY': // TODO: implement validation
  116. break;
  117. case 'KEY': // TODO: implement validation
  118. break;
  119. case 'KX': // TODO: implement validation
  120. break;
  121. case "LOC":
  122. if (!is_valid_loc($content)) {
  123. return false;
  124. }
  125. if (!is_valid_hostname_fqdn($name, 1)) {
  126. return false;
  127. }
  128. break;
  129. case 'MINFO': // TODO: implement validation
  130. break;
  131. case 'MR': // TODO: implement validation
  132. break;
  133. case "MX":
  134. if (!is_valid_hostname_fqdn($content, 0)) {
  135. return false;
  136. }
  137. if (!is_valid_hostname_fqdn($name, 1)) {
  138. return false;
  139. }
  140. if (!is_valid_non_alias_target($content)) {
  141. return false;
  142. }
  143. break;
  144. case 'NAPTR': // TODO: implement validation
  145. break;
  146. case "NS":
  147. if (!is_valid_hostname_fqdn($content, 0)) {
  148. return false;
  149. }
  150. if (!is_valid_hostname_fqdn($name, 1)) {
  151. return false;
  152. }
  153. if (!is_valid_non_alias_target($content)) {
  154. return false;
  155. }
  156. break;
  157. case 'NSEC': // TODO: implement validation
  158. break;
  159. case 'NSEC3': // TODO: implement validation
  160. break;
  161. case 'NSEC3PARAM': // TODO: implement validation
  162. break;
  163. case 'OPT': // TODO: implement validation
  164. break;
  165. case "PTR":
  166. if (!is_valid_hostname_fqdn($content, 0)) {
  167. return false;
  168. }
  169. if (!is_valid_hostname_fqdn($name, 1)) {
  170. return false;
  171. }
  172. break;
  173. case 'RKEY': // TODO: implement validation
  174. break;
  175. case 'RP': // TODO: implement validation
  176. break;
  177. case 'RRSIG': // TODO: implement validation
  178. break;
  179. case "SOA":
  180. if (!is_valid_rr_soa_name($name, $zone)) {
  181. return false;
  182. }
  183. if (!is_valid_hostname_fqdn($name, 1)) {
  184. return false;
  185. }
  186. if (!is_valid_rr_soa_content($content)) {
  187. error(ERR_DNS_CONTENT);
  188. return false;
  189. }
  190. break;
  191. case "SPF":
  192. if (!is_valid_spf($content)) {
  193. return false;
  194. }
  195. break;
  196. case "SRV":
  197. if (!is_valid_rr_srv_name($name)) {
  198. return false;
  199. }
  200. if (!is_valid_rr_srv_content($content)) {
  201. return false;
  202. }
  203. break;
  204. case 'SSHFP': // TODO: implement validation
  205. break;
  206. case 'TLSA': // TODO: implement validation
  207. break;
  208. case 'TSIG': // TODO: implement validation
  209. break;
  210. case "TXT":
  211. if (!is_valid_printable($name)) {
  212. return false;
  213. }
  214. if (!is_valid_printable($content)) {
  215. return false;
  216. }
  217. break;
  218. case 'WKS': // TODO: implement validation
  219. break;
  220. case "CURL":
  221. case "MBOXFW":
  222. case "URL":
  223. // TODO: implement validation?
  224. // Fancy types are not supported anymore in PowerDNS
  225. break;
  226. default:
  227. error(ERR_DNS_RR_TYPE);
  228. return false;
  229. }
  230. if (!is_valid_rr_prio($prio, $type)) {
  231. return false;
  232. }
  233. if (!is_valid_rr_ttl($ttl)) {
  234. return false;
  235. }
  236. return true;
  237. }
  238. /** Test if hostname is valid FQDN
  239. *
  240. * @param mixed $hostname Hostname string
  241. * @param string $wildcard Hostname includes wildcard '*'
  242. *
  243. * @return boolean true if valid, false otherwise
  244. */
  245. function is_valid_hostname_fqdn(&$hostname, $wildcard) {
  246. global $dns_top_level_tld_check;
  247. global $dns_strict_tld_check;
  248. global $valid_tlds;
  249. $hostname = preg_replace("/\.$/", "", $hostname);
  250. # The full domain name may not exceed a total length of 253 characters.
  251. if (strlen($hostname) > 253) {
  252. error(ERR_DNS_HN_TOO_LONG);
  253. return false;
  254. }
  255. $hostname_labels = explode('.', $hostname);
  256. $label_count = count($hostname_labels);
  257. if ($dns_top_level_tld_check && $label_count == 1) {
  258. return false;
  259. }
  260. foreach ($hostname_labels as $hostname_label) {
  261. if ($wildcard == 1 && !isset($first)) {
  262. if (!preg_match('/^(\*|[\w-\/]+)$/', $hostname_label)) {
  263. error(ERR_DNS_HN_INV_CHARS);
  264. return false;
  265. }
  266. $first = 1;
  267. } else {
  268. if (!preg_match('/^[\w-\/]+$/', $hostname_label)) {
  269. error(ERR_DNS_HN_INV_CHARS);
  270. return false;
  271. }
  272. }
  273. if (substr($hostname_label, 0, 1) == "-") {
  274. error(ERR_DNS_HN_DASH);
  275. return false;
  276. }
  277. if (substr($hostname_label, -1, 1) == "-") {
  278. error(ERR_DNS_HN_DASH);
  279. return false;
  280. }
  281. if (strlen($hostname_label) < 1 || strlen($hostname_label) > 63) {
  282. error(ERR_DNS_HN_LENGTH);
  283. return false;
  284. }
  285. }
  286. if ($hostname_labels[$label_count - 1] == "arpa" && (substr_count($hostname_labels[0], "/") == 1 XOR substr_count($hostname_labels[1], "/") == 1)) {
  287. if (substr_count($hostname_labels[0], "/") == 1) {
  288. $array = explode("/", $hostname_labels[0]);
  289. } else {
  290. $array = explode("/", $hostname_labels[1]);
  291. }
  292. if (count($array) != 2) {
  293. error(ERR_DNS_HOSTNAME);
  294. return false;
  295. }
  296. if (!is_numeric($array[0]) || $array[0] < 0 || $array[0] > 255) {
  297. error(ERR_DNS_HOSTNAME);
  298. return false;
  299. }
  300. if (!is_numeric($array[1]) || $array[1] < 25 || $array[1] > 31) {
  301. error(ERR_DNS_HOSTNAME);
  302. return false;
  303. }
  304. } else {
  305. if (substr_count($hostname, "/") > 0) {
  306. error(ERR_DNS_HN_SLASH);
  307. return false;
  308. }
  309. }
  310. if ($dns_strict_tld_check && !in_array(strtolower($hostname_labels[$label_count - 1]), $valid_tlds)) {
  311. error(ERR_DNS_INV_TLD);
  312. return false;
  313. }
  314. return true;
  315. }
  316. /** Test if IPv4 address is valid
  317. *
  318. * @param string $ipv4 IPv4 address string
  319. * @param boolean $answer print error if true
  320. * [default=true]
  321. *
  322. * @return boolean true if valid, false otherwise
  323. */
  324. function is_valid_ipv4($ipv4, $answer = true) {
  325. // 20080424/RZ: The current code may be replaced by the following if()
  326. // statement, but it will raise the required PHP version to ">= 5.2.0".
  327. // Not sure if we want that now.
  328. //
  329. // if(filter_var($ipv4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === FALSE) {
  330. // error(ERR_DNS_IPV4); return false;
  331. // }
  332. if (!preg_match("/^[0-9\.]{7,15}$/", $ipv4)) {
  333. if ($answer) {
  334. error(ERR_DNS_IPV4);
  335. }
  336. return false;
  337. }
  338. $quads = explode('.', $ipv4);
  339. $numquads = count($quads);
  340. if ($numquads != 4) {
  341. if ($answer) {
  342. error(ERR_DNS_IPV4);
  343. }
  344. return false;
  345. }
  346. for ($i = 0; $i < 4; $i++) {
  347. if ($quads[$i] > 255) {
  348. if ($answer) {
  349. error(ERR_DNS_IPV4);
  350. }
  351. return false;
  352. }
  353. }
  354. return true;
  355. }
  356. /** Test if IPv6 address is valid
  357. *
  358. * @param string $ipv6 IPv6 address string
  359. * @param boolean $answer print error if true
  360. * [default=true]
  361. *
  362. * @return boolean true if valid, false otherwise
  363. */
  364. function is_valid_ipv6($ipv6, $answer = true) {
  365. // 20080424/RZ: The current code may be replaced by the following if()
  366. // statement, but it will raise the required PHP version to ">= 5.2.0".
  367. // Not sure if we want that now.
  368. //
  369. // if(filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === FALSE) {
  370. // error(ERR_DNS_IPV6); return false;
  371. // }
  372. if (!preg_match("/^[0-9a-f]{0,4}:([0-9a-f]{0,4}:){0,6}[0-9a-f]{0,4}$/i", $ipv6)) {
  373. if ($answer) {
  374. error(ERR_DNS_IPV6);
  375. }
  376. return false;
  377. }
  378. $quads = explode(':', $ipv6);
  379. $numquads = count($quads);
  380. if ($numquads > 8 || $numquads < 3) {
  381. if ($answer) {
  382. error(ERR_DNS_IPV6);
  383. }
  384. return false;
  385. }
  386. $emptyquads = 0;
  387. for ($i = 1; $i < $numquads - 1; $i++) {
  388. if ($quads[$i] == "")
  389. $emptyquads++;
  390. }
  391. if ($emptyquads > 1) {
  392. if ($answer) {
  393. error(ERR_DNS_IPV6);
  394. }
  395. return false;
  396. }
  397. if ($emptyquads == 0 && $numquads != 8) {
  398. if ($answer) {
  399. error(ERR_DNS_IPV6);
  400. }
  401. return false;
  402. }
  403. return true;
  404. }
  405. /** Test if multiple IP addresses are valid
  406. *
  407. * Takes a string of comma seperated IP addresses and tests validity
  408. *
  409. * @param string $ips Comma seperated IP addresses
  410. *
  411. * @return boolean true if valid, false otherwise
  412. */
  413. function are_multipe_valid_ips($ips) {
  414. // multiple master NS-records are permitted and must be separated by ,
  415. // eg. "192.0.0.1, 192.0.0.2, 2001:1::1"
  416. $are_valid = false;
  417. $multiple_ips = explode(",", $ips);
  418. if (is_array($multiple_ips)) {
  419. foreach ($multiple_ips as $m_ip) {
  420. $trimmed_ip = trim($m_ip);
  421. if (is_valid_ipv4($trimmed_ip, false) || is_valid_ipv6($trimmed_ip, true)) {
  422. $are_valid = true;
  423. } else {
  424. // as soon there is an invalid ip-addr
  425. // exit and return false
  426. echo "hin:=$trimmed_ip=";
  427. return false;
  428. }
  429. }
  430. } elseif (is_valid_ipv4($ips) || is_valid_ipv6($ips)) {
  431. $are_valid = true;
  432. }
  433. if ($are_valid) {
  434. return true;
  435. } else {
  436. return false;
  437. }
  438. }
  439. /** Test if string is printable
  440. *
  441. * @param string $string string
  442. *
  443. * @return boolean true if valid, false otherwise
  444. */
  445. function is_valid_printable($string) {
  446. if (!preg_match('/^[[:print:]]+$/', trim($string))) {
  447. error(ERR_DNS_PRINTABLE);
  448. return false;
  449. }
  450. return true;
  451. }
  452. /** Test if CNAME is valid
  453. *
  454. * Check if any MX or NS entries exist which invalidated CNAME
  455. *
  456. * @param string $name CNAME to lookup
  457. *
  458. * @return boolean true if valid, false otherwise
  459. */
  460. function is_valid_rr_cname_name($name) {
  461. global $db;
  462. $query = "SELECT id FROM records
  463. WHERE content = " . $db->quote($name, 'text') . "
  464. AND (type = " . $db->quote('MX', 'text') . " OR type = " . $db->quote('NS', 'text') . ")";
  465. $response = $db->queryOne($query);
  466. if (!empty($response)) {
  467. error(ERR_DNS_CNAME);
  468. return false;
  469. }
  470. return true;
  471. }
  472. /** Check if CNAME already exists
  473. *
  474. * @param string $name CNAME
  475. * @param int $rid Record ID
  476. *
  477. * @return boolean true if non-existant, false if exists
  478. */
  479. function is_valid_rr_cname_exists($name, $rid) {
  480. global $db;
  481. $where = ($rid > 0 ? " AND id != " . $db->quote($rid, 'integer') : '');
  482. $query = "SELECT id FROM records
  483. WHERE name = " . $db->quote($name, 'text') . $where . "
  484. AND TYPE = 'CNAME'";
  485. $response = $db->queryOne($query);
  486. if ($response) {
  487. error(ERR_DNS_CNAME_EXISTS);
  488. return false;
  489. }
  490. return true;
  491. }
  492. /** Check if CNAME is unique (doesn't overlap A/AAAA)
  493. *
  494. * @param string $name CNAME
  495. * @param string $rid Record ID
  496. *
  497. * @return boolean true if unique, false if duplicate
  498. */
  499. function is_valid_rr_cname_unique($name, $rid) {
  500. global $db;
  501. $where = ($rid > 0 ? " AND id != " . $db->quote($rid, 'integer') : '');
  502. $query = "SELECT id FROM records
  503. WHERE name = " . $db->quote($name, 'text') . $where . "
  504. AND TYPE IN ('A', 'AAAA', 'CNAME')";
  505. $response = $db->queryOne($query);
  506. if ($response) {
  507. error(ERR_DNS_CNAME_UNIQUE);
  508. return false;
  509. }
  510. return true;
  511. }
  512. /**
  513. * Check that the zone does not have a empty CNAME RR
  514. *
  515. * @param string $name
  516. * @param string $zone
  517. */
  518. function is_not_empty_cname_rr($name, $zone) {
  519. if ($name == $zone) {
  520. error(ERR_DNS_CNAME_EMPTY);
  521. return false;
  522. }
  523. return true;
  524. }
  525. /** Check if target is not a CNAME
  526. *
  527. * @param string $target target to check
  528. *
  529. * @return boolean true if not alias, false if CNAME exists
  530. */
  531. function is_valid_non_alias_target($target) {
  532. global $db;
  533. $query = "SELECT id FROM records
  534. WHERE name = " . $db->quote($target, 'text') . "
  535. AND TYPE = " . $db->quote('CNAME', 'text');
  536. $response = $db->queryOne($query);
  537. if ($response) {
  538. error(ERR_DNS_NON_ALIAS_TARGET);
  539. return false;
  540. }
  541. return true;
  542. }
  543. /** Check if HINFO content is valid
  544. *
  545. * @param string $content HINFO record content
  546. *
  547. * @return boolean true if valid, false otherwise
  548. */
  549. function is_valid_rr_hinfo_content($content) {
  550. if ($content[0] == "\"") {
  551. $fields = preg_split('/(?<=") /', $content, 2);
  552. } else {
  553. $fields = preg_split('/ /', $content, 2);
  554. }
  555. for ($i = 0; ($i < 2); $i++) {
  556. if (!preg_match("/^([^\s]{1,1000})|\"([^\"]{1,998}\")$/i", $fields[$i])) {
  557. error(ERR_DNS_HINFO_INV_CONTENT);
  558. return false;
  559. }
  560. }
  561. return true;
  562. }
  563. /** Check if SOA content is valid
  564. *
  565. * @param mixed $content SOA record content
  566. *
  567. * @return boolean true if valid, false otherwise
  568. */
  569. function is_valid_rr_soa_content(&$content) {
  570. $fields = preg_split("/\s+/", trim($content));
  571. $field_count = count($fields);
  572. if ($field_count == 0 || $field_count > 7) {
  573. return false;
  574. } else {
  575. if (!is_valid_hostname_fqdn($fields[0], 0) || preg_match('/\.arpa\.?$/', $fields[0])) {
  576. return false;
  577. }
  578. $final_soa = $fields[0];
  579. if (isset($fields[1])) {
  580. $addr_input = $fields[1];
  581. } else {
  582. global $dns_hostmaster;
  583. $addr_input = $dns_hostmaster;
  584. }
  585. if (!preg_match("/@/", $addr_input)) {
  586. $addr_input = preg_split('/(?<!\\\)\./', $addr_input, 2);
  587. $addr_to_check = str_replace("\\", "", $addr_input[0]) . "@" . $addr_input[1];
  588. } else {
  589. $addr_to_check = $addr_input;
  590. }
  591. if (!is_valid_email($addr_to_check)) {
  592. return false;
  593. } else {
  594. $addr_final = explode('@', $addr_to_check, 2);
  595. $final_soa .= " " . str_replace(".", "\\.", $addr_final[0]) . "." . $addr_final[1];
  596. }
  597. if (isset($fields[2])) {
  598. if (!is_numeric($fields[2])) {
  599. return false;
  600. }
  601. $final_soa .= " " . $fields[2];
  602. } else {
  603. $final_soa .= " 0";
  604. }
  605. if ($field_count != 7) {
  606. return false;
  607. } else {
  608. for ($i = 3; ($i < 7); $i++) {
  609. if (!is_numeric($fields[$i])) {
  610. return false;
  611. } else {
  612. $final_soa .= " " . $fields[$i];
  613. }
  614. }
  615. }
  616. }
  617. $content = $final_soa;
  618. return true;
  619. }
  620. /** Check if SOA name is valid
  621. *
  622. * Checks if SOA name = zone name
  623. *
  624. * @param string $name SOA name
  625. * @param string $zone Zone name
  626. *
  627. * @return boolean true if valid, false otherwise
  628. */
  629. function is_valid_rr_soa_name($name, $zone) {
  630. if ($name != $zone) {
  631. error(ERR_DNS_SOA_NAME);
  632. return false;
  633. }
  634. return true;
  635. }
  636. /** Check if Priority is valid
  637. *
  638. * Check if MX or SRV priority is within range, otherwise set to 0
  639. *
  640. * @param mixed $prio Priority
  641. * @param string $type Record type
  642. *
  643. * @return boolean true if valid, false otherwise
  644. */
  645. function is_valid_rr_prio(&$prio, $type) {
  646. if ($type == "MX" || $type == "SRV") {
  647. if (!is_numeric($prio) || $prio < 0 || $prio > 65535) {
  648. error(ERR_DNS_INV_PRIO);
  649. return false;
  650. }
  651. } else {
  652. $prio = 0;
  653. }
  654. return true;
  655. }
  656. /** Check if SRV name is valid
  657. *
  658. * @param mixed $name SRV name
  659. *
  660. * @return boolean true if valid, false otherwise
  661. */
  662. function is_valid_rr_srv_name(&$name) {
  663. if (strlen($name) > 255) {
  664. error(ERR_DNS_HN_TOO_LONG);
  665. return false;
  666. }
  667. $fields = explode('.', $name, 3);
  668. if (!preg_match('/^_[\w-]+$/i', $fields[0])) {
  669. error(ERR_DNS_SRV_NAME);
  670. return false;
  671. }
  672. if (!preg_match('/^_[\w]+$/i', $fields[1])) {
  673. error(ERR_DNS_SRV_NAME);
  674. return false;
  675. }
  676. if (!is_valid_hostname_fqdn($fields[2], 0)) {
  677. error(ERR_DNS_SRV_NAME);
  678. return false;
  679. }
  680. $name = join('.', $fields);
  681. return true;
  682. }
  683. /** Check if SRV content is valid
  684. *
  685. * @param mixed $content SRV content
  686. *
  687. * @return boolean true if valid, false otherwise
  688. */
  689. function is_valid_rr_srv_content(&$content) {
  690. $fields = preg_split("/\s+/", trim($content), 3);
  691. if (!is_numeric($fields[0]) || $fields[0] < 0 || $fields[0] > 65535) {
  692. error(ERR_DNS_SRV_WGHT);
  693. return false;
  694. }
  695. if (!is_numeric($fields[1]) || $fields[1] < 0 || $fields[1] > 65535) {
  696. error(ERR_DNS_SRV_PORT);
  697. return false;
  698. }
  699. if ($fields[2] == "" || ($fields[2] != "." && !is_valid_hostname_fqdn($fields[2], 0))) {
  700. error(ERR_DNS_SRV_TRGT);
  701. return false;
  702. }
  703. $content = join(' ', $fields);
  704. return true;
  705. }
  706. /** Check if TTL is valid and within range
  707. *
  708. * @param int $ttl TTL
  709. *
  710. * @return boolean true if valid,false otherwise
  711. */
  712. function is_valid_rr_ttl(&$ttl) {
  713. if (!isset($ttl) || $ttl == "") {
  714. global $dns_ttl;
  715. $ttl = $dns_ttl;
  716. }
  717. if (!is_numeric($ttl) || $ttl < 0 || $ttl > 2147483647) {
  718. error(ERR_DNS_INV_TTL);
  719. return false;
  720. }
  721. return true;
  722. }
  723. /** Check if search string is valid
  724. *
  725. * @param string $search_string search string
  726. *
  727. * @return boolean true if valid, false otherwise
  728. */
  729. function is_valid_search($search_string) {
  730. // Only allow for alphanumeric, numeric, dot, dash, underscore and
  731. // percent in search string. The last two are wildcards for SQL.
  732. // Needs extension probably for more usual record types.
  733. return preg_match('/^[a-z0-9.\-%_]+$/i', $search_string);
  734. }
  735. /** Check if SPF content is valid
  736. *
  737. * @param string $content SPF content
  738. *
  739. * @return boolean true if valid, false otherwise
  740. */
  741. function is_valid_spf($content) {
  742. //Regex from http://www.schlitt.net/spf/tests/spf_record_regexp-03.txt
  743. $regex = "^[Vv]=[Ss][Pp][Ff]1( +([-+?~]?([Aa][Ll][Ll]|[Ii][Nn][Cc][Ll][Uu][Dd][Ee]:(%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}|%%|%_|%-|[!-$&-~])*(\.([A-Za-z]|[A-Za-z]([-0-9A-Za-z]?)*[0-9A-Za-z])|%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\})|[Aa](:(%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}|%%|%_|%-|[!-$&-~])*(\.([A-Za-z]|[A-Za-z]([-0-9A-Za-z]?)*[0-9A-Za-z])|%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}))?((/([1-9]|1[0-9]|2[0-9]|3[0-2]))?(//([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8]))?)?|[Mm][Xx](:(%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}|%%|%_|%-|[!-$&-~])*(\.([A-Za-z]|[A-Za-z]([-0-9A-Za-z]?)*[0-9A-Za-z])|%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}))?((/([1-9]|1[0-9]|2[0-9]|3[0-2]))?(//([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8]))?)?|[Pp][Tt][Rr](:(%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}|%%|%_|%-|[!-$&-~])*(\.([A-Za-z]|[A-Za-z]([-0-9A-Za-z]?)*[0-9A-Za-z])|%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}))?|[Ii][Pp]4:([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/([1-9]|1[0-9]|2[0-9]|3[0-2]))?|[Ii][Pp]6:(::|([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|([0-9A-Fa-f]{1,4}:){1,8}:|([0-9A-Fa-f]{1,4}:){7}:[0-9A-Fa-f]{1,4}|([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}){1,2}|([0-9A-Fa-f]{1,4}:){5}(:[0-9A-Fa-f]{1,4}){1,3}|([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){1,4}|([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){1,5}|([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){1,6}|[0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){1,7}|:(:[0-9A-Fa-f]{1,4}){1,8}|([0-9A-Fa-f]{1,4}:){6}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|([0-9A-Fa-f]{1,4}:){6}:([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|[0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|::([0-9A-Fa-f]{1,4}:){0,6}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(/([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8]))?|[Ee][Xx][Ii][Ss][Tt][Ss]:(%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}|%%|%_|%-|[!-$&-~])*(\.([A-Za-z]|[A-Za-z]([-0-9A-Za-z]?)*[0-9A-Za-z])|%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}))|[Rr][Ee][Dd][Ii][Rr][Ee][Cc][Tt]=(%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}|%%|%_|%-|[!-$&-~])*(\.([A-Za-z]|[A-Za-z]([-0-9A-Za-z]?)*[0-9A-Za-z])|%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\})|[Ee][Xx][Pp]=(%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}|%%|%_|%-|[!-$&-~])*(\.([A-Za-z]|[A-Za-z]([-0-9A-Za-z]?)*[0-9A-Za-z])|%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\})|[A-Za-z][-.0-9A-Z_a-z]*=(%\{[CDHILOPR-Tcdhilopr-t]([1-9][0-9]?|10[0-9]|11[0-9]|12[0-8])?[Rr]?[+-/=_]*\}|%%|%_|%-|[!-$&-~])*))* *$^";
  744. if (!preg_match($regex, $content)) {
  745. return false;
  746. } else {
  747. return true;
  748. }
  749. }
  750. /** Check if LOC content is valid
  751. *
  752. * @param string $content LOC content
  753. *
  754. * @return boolean true if valid, false otherwise
  755. */
  756. function is_valid_loc($content) {
  757. $regex = "^(90|[1-8]\d|0?\d)( ([1-5]\d|0?\d)( ([1-5]\d|0?\d)(\.\d{1,3})?)?)? [NS] (180|1[0-7]\d|[1-9]\d|0?\d)( ([1-5]\d|0?\d)( ([1-5]\d|0?\d)(\.\d{1,3})?)?)? [EW] (-(100000(\.00)?|\d{1,5}(\.\d\d)?)|([1-3]?\d{1,7}(\.\d\d)?|4([01][0-9]{6}|2([0-7][0-9]{5}|8([0-3][0-9]{4}|4([0-8][0-9]{3}|9([0-5][0-9]{2}|6([0-6][0-9]|7[01]))))))(\.\d\d)?|42849672(\.([0-8]\d|9[0-5]))?))[m]?( (\d{1,7}|[1-8]\d{7})(\.\d\d)?[m]?){0,3}$^";
  758. if (!preg_match($regex, $content)) {
  759. return false;
  760. } else {
  761. return true;
  762. }
  763. }