From 7bdb8d1c0e360c967fa3ff4808f2ba5bebfc3fe6 Mon Sep 17 00:00:00 2001 From: ftimme <ft@falkotimme.com> Date: Sat, 3 Dec 2005 10:56:08 +0000 Subject: [PATCH] --- interface/lib/classes/validate_dns.inc.php | 260 +++++++++++++++++++++ interface/web/dns/form/rr.tform.php | 4 + interface/web/dns/form/soa.tform.php | 22 +- interface/web/dns/lib/lang/en_rr.lng | 28 +++ interface/web/dns/lib/lang/en_soa.lng | 21 ++ interface/web/dns/rr_edit.php | 3 + interface/web/dns/soa_edit.php | 3 + 7 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 interface/lib/classes/validate_dns.inc.php diff --git a/interface/lib/classes/validate_dns.inc.php b/interface/lib/classes/validate_dns.inc.php new file mode 100644 index 000000000..bca195fe3 --- /dev/null +++ b/interface/lib/classes/validate_dns.inc.php @@ -0,0 +1,260 @@ +<?php + +/* +Copyright (c) 2005, Till Brehm, Falko Timme, projektfarm Gmbh +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of ISPConfig nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/** +* DNS validation +* +* @author Falko Timme <ft@falkotimme.com> +* @copyright Copyright © 2005, Falko Timme +*/ + +class validate_dns { + +function validate_field($field, $area, $zoneid, $wildcard_allowed = 1){ + //$desc: Name, Data, RP mbox, RP txtref, SRV target, Zone origin, Name server, Admin email + global $app, $conf; + + switch ($area) { + case "Name": + $desc = $app->tform->wordbook['name_txt']; + break; + case "Data": + $desc = $app->tform->wordbook['data_txt']; + break; + case "RP mbox": + $desc = $app->tform->wordbook['rp_mbox_txt']; + break; + case "RP txtref": + $desc = $app->tform->wordbook['rp_txtref_txt']; + break; + case "SRV target": + $desc = $app->tform->wordbook['srv_target_txt']; + break; + case "Zone origin": + $desc = $app->tform->wordbook['zone_origin_txt']; + break; + case "Name server": + $desc = $app->tform->wordbook['ns_txt']; + break; + case "Admin email": + $desc = $app->tform->wordbook['mbox_txt']; + break; + } + + $error = ''; + + $valid_characters = "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_"; + + if(strlen($field) > 255) $error .= $desc." ".$app->tform->wordbook['error_255_characters']."<br>\r\n"; + + $parts = explode(".", $field); + $i = 0; + foreach ($parts as $part){ + $i++; + + if(strlen($part) > 63) $error .= $desc." ".$app->tform->wordbook['error_63_characters']."<br>\r\n"; + + if(strspn($part, $valid_characters) != strlen($part)) $error .= $desc." ".$app->tform->wordbook['error_invalid_characters']."<br>\r\n"; + + if(substr($part, 0, 1) == '-') $error .= $desc." ".$app->tform->wordbook['error_hyphen_begin']."<br>\r\n"; + if(substr($part, -1) == '-') $error .= $desc." ".$app->tform->wordbook['error_hyphen_end']."<br>\r\n"; + + if(strstr($part, "*")){ + if($wildcard_allowed){ + if($i != 1) $error .= $desc." ".$app->tform->wordbook['error_wildcard_non_initial_part']."<br>\r\n"; + + if($part != "*") $error .= $desc." ".$app->tform->wordbook['error_wildcard_mix']."<br>\r\n"; + } else { + $error .= $desc." ".$app->tform->wordbook['error_no_wildcard_allowed']."<br>\r\n"; + } + } + } + + if(substr($field, -1) == '.' && $area == 'Name'){ + $soa = $app->db->queryOneRecord("SELECT * FROM soa WHERE id = ".$zoneid); + if(substr($field, (strlen($field) - strlen($soa['origin']))) != $soa['origin']) $error .= $desc." ".$app->tform->wordbook['error_out_of_zone']."<br>\r\n"; + } + + return $error; +} + +function validate_rp_data(&$data, $zoneid){ + global $app, $conf; + $error = ''; + $fields = explode(" ", trim($data)); + if(count($fields) != 2) return $error .= $app->tform->wordbook['data_txt']." ".$app->tform->wordbook['error_invalid_rp']."<br>\r\n"; + $mbox = $fields[0]; + $txtref = $fields[1]; + + $error .= $this->validate_field($mbox, 'RP mbox', $zoneid, 0); + $error .= $this->validate_field($txtref, 'RP txtref', $zoneid, 0); + + $data = $mbox." ".$txtref; + return $error; +} + +function validate_srv_data(&$data, $zoneid){ + global $app, $conf; + $error = ''; + + $fields = explode(" ", trim($data)); + if(count($fields) != 3) return $error .= $app->tform->wordbook['data_txt']." ".$app->tform->wordbook['error_invalid_srv']."<br>\r\n"; + + $weight = $fields[0]; + $port = $fields[1]; + $target = $fields[2]; + if($weight < 0 || $weight > 65535) $error .= $app->tform->wordbook['weight_txt']." (\"<i>" . htmlentities($weight)."</i>\") ".$app->tform->wordbook['error_srv_out_of_range']."<br>\r\n"; + if ($port < 0 || $port > 65535) $error .= $app->tform->wordbook['port_txt']." (\"<i>".htmlentities($port)."</i>\") ".$app->tform->wordbook['error_srv_out_of_range']."<br>\r\n"; + + $error .= $this->validate_field($target, "SRV target", $zoneid, 0); + + $data = (int)$weight." ".(int)$port." ".$target; + return $error; +} + +function is_integer($value, $fieldname, $zero_allowed = 0){ + global $app, $conf; + + $error = ''; + + if(intval($value) != $value || !is_numeric($value)) $error .= $fieldname." ".$app->tform->wordbook['error_must_be_integer']."<br>\r\n"; + if($value > 2147483647) $error .= $fieldname." ".$app->tform->wordbook['error_must_not_be_greater_than_2147483647']."<br>\r\n"; + if(!$zero_allowed){ + if($value <= 0) $error .= $fieldname." ".$app->tform->wordbook['error_must_be_positive']."<br>\r\n"; + } else { + if($value < 0) $error .= $fieldname." ".$app->tform->wordbook['error_must_not_be_negative']."<br>\r\n"; + } + + return $error; +} + +function validate_rr(&$rr){ + global $app, $conf; + + $error = ''; + + $tmp_rr = $rr; + foreach($tmp_rr as $key => $val){ + $rr[$key] = trim($val); + } + unset($tmp_rr); + + $error .= $this->validate_field($rr['name'], 'Name', $rr['zone'], 1); + + switch ($rr['type']) { + case "A": + $ip_parts = explode(".", $rr['data']); + if(count($ip_parts) != 4){ + $error .= $app->tform->wordbook['data_txt']." ".$app->tform->wordbook['error_a']."<br>\r\n"; + } else { + for($n = 0; $n < 4; $n++){ + $q = $ip_parts[$n]; + if(!is_numeric($q) || (int)$q < 0 || (int)$q > 255) $error .= $app->tform->wordbook['data_txt']." ".$app->tform->wordbook['error_a']."<br>\r\n"; + } + } + $rr['data'] = (int)$ip_parts[0].".".(int)$ip_parts[1].".".(int)$ip_parts[2].".".(int)$ip_parts[3]; + break; + case "AAAA": + $valid_chars = "ABCDEFabcdef1234567890:"; + + if(strspn($rr['data'], $valid_chars) != strlen($rr['data'])) $error .= $app->tform->wordbook['data_txt']." ".$app->tform->wordbook['error_aaaa']."<br>\r\n"; + break; + case "ALIAS": + $error .= $this->validate_field($rr['data'], 'Data', $rr['zone'], 0); + break; + case "CNAME": + $error .= $this->validate_field($rr['data'], 'Data', $rr['zone'], 0); + break; + case "HINFO": + if(!strchr($rr['data'], ' ')) $error .= $app->tform->wordbook['data_txt']." ".$app->tform->wordbook['error_hinfo']."<br>\r\n"; + break; + case "MX": + $error .= $this->validate_field($rr['data'], 'Data', $rr['zone'], 0); + break; + case "NS": + $error .= $this->validate_field($rr['data'], 'Data', $rr['zone'], 0); + break; + case "PTR": + $error .= $this->validate_field($rr['data'], 'Data', $rr['zone'], 0); + if(substr($rr['data'], -1) != '.') $error .= $app->tform->wordbook['data_txt']." ".$app->tform->wordbook['error_ptr']."<br>\r\n"; + break; + case "RP": + $error .= $this->validate_rp_data($rr['data'], $rr['zone']); + break; + case "SRV": + $error .= $this->validate_srv_data($rr['data'], $rr['zone']); + break; + case "TXT": + break; + } + + $error .= $this->is_integer($rr['aux'], $app->tform->wordbook['aux_txt'], 1); + + $error .= $this->is_integer($rr['ttl'], $app->tform->wordbook['ttl_txt']); + + + return $error; +} + +function validate_soa(&$soa){ + global $app, $conf; + + $error = ''; + + $tmp_soa = $soa; + foreach($tmp_soa as $key => $val){ + if($key != 'active') $soa[$key] = trim($val); + } + unset($tmp_soa); + + if($soa['origin'] == '') $error .= $app->tform->wordbook['origin_txt']." ".$app->tform->wordbook['error_empty']."<br>\r\n"; + if(substr($soa['origin'], -1) != '.') $error .= $app->tform->wordbook['origin_txt']." ".$app->tform->wordbook['error_dot']."<br>\r\n"; + $error .= $this->validate_field($soa['origin'], "Zone origin", $soa['id'], 0); + + $error .= $this->is_integer($soa['ttl'], $app->tform->wordbook['ttl_txt']); + + if($soa['ns'] == '') $error .= $app->tform->wordbook['ns_txt']." ".$app->tform->wordbook['error_empty']."<br>\r\n"; + $error .= $this->validate_field($soa['ns'], "Name server", $soa['id'], 0); + + if($soa['mbox'] == '') $error .= $app->tform->wordbook['mbox_txt']." ".$app->tform->wordbook['error_empty']."<br>\r\n"; + $error .= $this->validate_field($soa['mbox'], "Admin email", $soa['id'], 0); + + $error .= $this->is_integer($soa['refresh'], $app->tform->wordbook['refresh_txt']); + + $error .= $this->is_integer($soa['retry'], $app->tform->wordbook['retry_txt']); + + $error .= $this->is_integer($soa['expire'], $app->tform->wordbook['expire_txt']); + + $error .= $this->is_integer($soa['minimum'], $app->tform->wordbook['minimum_txt']); + + return $error; +} + +} \ No newline at end of file diff --git a/interface/web/dns/form/rr.tform.php b/interface/web/dns/form/rr.tform.php index 1f299260b..e9af9906b 100644 --- a/interface/web/dns/form/rr.tform.php +++ b/interface/web/dns/form/rr.tform.php @@ -110,9 +110,11 @@ $form["tabs"]['rr'] = array ( 'aux' => array ( 'datatype' => 'INTEGER', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ('type' => 'ISINT', 'errmsg'=> 'rr_aux_error_noint'), ), + */ 'default' => '', 'value' => '', 'width' => '30', @@ -121,9 +123,11 @@ $form["tabs"]['rr'] = array ( 'ttl' => array ( 'datatype' => 'INTEGER', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ('type' => 'ISPOSITIVE', 'errmsg'=> 'rr_ttl_error_notpositive'), ), + */ 'default' => '86400', 'value' => '86400', 'width' => '30', diff --git a/interface/web/dns/form/soa.tform.php b/interface/web/dns/form/soa.tform.php index 9b39b6ad1..ca23e195d 100644 --- a/interface/web/dns/form/soa.tform.php +++ b/interface/web/dns/form/soa.tform.php @@ -74,11 +74,9 @@ $form["tabs"]['soa'] = array ( 'origin' => array ( 'datatype' => 'VARCHAR', 'formtype' => 'TEXT', - 'validators' => array (0 => array ( 'type' => 'NOTEMPTY', - 'errmsg'=> 'soa_error_empty'), - 1 => array ( 'type' => 'UNIQUE', + 'validators' => array (0 => array ( 'type' => 'UNIQUE', 'errmsg'=> 'soa_error_unique'), - ), + ), 'default' => '', 'value' => '', 'width' => '30', @@ -87,9 +85,11 @@ $form["tabs"]['soa'] = array ( 'ns' => array ( 'datatype' => 'VARCHAR', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ( 'type' => 'NOTEMPTY', 'errmsg'=> 'ns_error_empty'), ), + */ 'default' => $conf['default_ns'], 'value' => $conf['default_ns'], 'width' => '30', @@ -98,9 +98,11 @@ $form["tabs"]['soa'] = array ( 'mbox' => array ( 'datatype' => 'VARCHAR', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ( 'type' => 'NOTEMPTY', 'errmsg'=> 'mbox_error_empty'), ), + */ 'default' => $conf['default_mbox'], 'value' => $conf['default_mbox'], 'width' => '30', @@ -109,9 +111,11 @@ $form["tabs"]['soa'] = array ( 'serial' => array ( 'datatype' => 'INTEGER', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ( 'type' => 'ISPOSITIVE', 'errmsg'=> 'serial_error_notpositive'), ), + */ 'default' => date("Ymd").'01', 'value' => date("Ymd").'01', 'width' => '30', @@ -120,9 +124,11 @@ $form["tabs"]['soa'] = array ( 'refresh' => array ( 'datatype' => 'INTEGER', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ( 'type' => 'ISPOSITIVE', 'errmsg'=> 'refresh_error_notpositive'), ), + */ 'default' => $conf['default_refresh'], 'value' => $conf['default_refresh'], 'width' => '30', @@ -131,9 +137,11 @@ $form["tabs"]['soa'] = array ( 'retry' => array ( 'datatype' => 'INTEGER', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ( 'type' => 'ISPOSITIVE', 'errmsg'=> 'retry_error_notpositive'), ), + */ 'default' => $conf['default_retry'], 'value' => $conf['default_retry'], 'width' => '30', @@ -142,9 +150,11 @@ $form["tabs"]['soa'] = array ( 'expire' => array ( 'datatype' => 'INTEGER', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ( 'type' => 'ISPOSITIVE', 'errmsg'=> 'expire_error_notpositive'), ), + */ 'default' => $conf['default_expire'], 'value' => $conf['default_expire'], 'width' => '30', @@ -153,9 +163,11 @@ $form["tabs"]['soa'] = array ( 'minimum' => array ( 'datatype' => 'INTEGER', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ( 'type' => 'ISPOSITIVE', 'errmsg'=> 'minimum_error_notpositive'), ), + */ 'default' => $conf['default_minimum_ttl'], 'value' => $conf['default_minimum_ttl'], 'width' => '30', @@ -164,9 +176,11 @@ $form["tabs"]['soa'] = array ( 'ttl' => array ( 'datatype' => 'INTEGER', 'formtype' => 'TEXT', + /* 'validators' => array (0 => array ( 'type' => 'ISPOSITIVE', 'errmsg'=> 'ttl_error_notpositive'), ), + */ 'default' => $conf['default_ttl'], 'value' => $conf['default_ttl'], 'width' => '30', diff --git a/interface/web/dns/lib/lang/en_rr.lng b/interface/web/dns/lib/lang/en_rr.lng index a835ac60d..33c4d34aa 100644 --- a/interface/web/dns/lib/lang/en_rr.lng +++ b/interface/web/dns/lib/lang/en_rr.lng @@ -5,9 +5,37 @@ $wb["type_txt"] = 'Type'; $wb["data_txt"] = 'Data'; $wb["aux_txt"] = 'Preference/Priority'; $wb["ttl_txt"] = 'TTL'; +$wb["weight_txt"] = 'Weight'; +$wb["port_txt"] = 'Port'; +$wb["rp_mbox_txt"] = 'RP Mailbox'; +$wb["rp_txtref_txt"] = 'RP TXTREF'; +$wb["srv_target_txt"] = 'SRV TARGET'; +$wb["zone_origin_txt"] = 'Zone Origin'; +$wb["ns_txt"] = 'Name Server'; +$wb["mbox_txt"] = 'Admin Email'; $wb["btn_save_txt"] = 'Save'; $wb["btn_cancel_txt"] = 'Cancel'; $wb["rr_data_error_empty"] = 'The data field must not be empty!'; $wb["rr_aux_error_noint"] = 'Preference/priority must be an integer!'; $wb["rr_ttl_error_notpositive"] = 'The TTL must be positive!'; +$wb["error_ptr"] = 'must contain a fully qualified domain name, ending with a dot!'; +$wb["error_hinfo"] = 'must contain the CPU type, then a space, then the OS type!'; +$wb["error_aaaa"] = 'must contain a valid IPv6 address!'; +$wb["error_a"] = 'must contain a valid IP address!'; +$wb["error_must_be_integer"] = 'must be an integer!'; +$wb["error_must_not_be_greater_than_2147483647"] = 'must not be greater than 2147483647!'; +$wb["error_must_be_positive"] = 'must be positive!'; +$wb["error_must_not_be_negative"] = 'must not be negative!'; +$wb["error_255_characters"] = 'contains more than 255 characters!'; +$wb["error_63_characters"] = 'contains a part with more than 63 characters!'; +$wb["error_invalid_characters"] = 'contains a part with invalid characters!'; +$wb["error_hyphen_begin"] = 'contains a part that begins with a hyphen!'; +$wb["error_hyphen_end"] = 'contains a part that ends with a hyphen!'; +$wb["error_wildcard_non_initial_part"] = 'contains a wildcard in a non-initial part!'; +$wb["error_wildcard_mix"] = 'contains a part mixing a wildcard character with other data!'; +$wb["error_no_wildcard_allowed"] = 'must not contain wildcards!'; +$wb["error_out_of_zone"] = 'is out of zone!'; +$wb["error_invalid_rp"] = 'has invalid format. The correct format is the <i>mbox</i> (a DNS-encoded email address), then a space, then the <i>txtref</i>, which should contain either a host for TXT lookup or a dot!'; +$wb["error_invalid_srv"] = 'has invalid format. The correct format is the <i>weight</i> (0-65535), then a space, then the <i>port</i> (0-65535), then a space, then the <i>target</i>!'; +$wb["error_srv_out_of_range"] = 'for SRV record is out of range!'; ?> \ No newline at end of file diff --git a/interface/web/dns/lib/lang/en_soa.lng b/interface/web/dns/lib/lang/en_soa.lng index e5bb800dc..7e8b1ebc7 100644 --- a/interface/web/dns/lib/lang/en_soa.lng +++ b/interface/web/dns/lib/lang/en_soa.lng @@ -10,6 +10,12 @@ $wb["minimum_txt"] = 'Minimum TTL'; $wb["ttl_txt"] = 'TTL'; $wb["active_txt"] = 'Active'; $wb["xfer_txt"] = 'Zone Transfers'; +$wb["name_txt"] = 'Name'; +$wb["data_txt"] = 'Data'; +$wb["rp_mbox_txt"] = 'RP Mailbox'; +$wb["rp_txtref_txt"] = 'RP TXTREF'; +$wb["srv_target_txt"] = 'SRV TARGET'; +$wb["zone_origin_txt"] = 'Zone Origin'; $wb["btn_save_txt"] = 'Save'; $wb["btn_cancel_txt"] = 'Cancel'; $wb["soa_error_empty"] = 'The origin must not be empty!'; @@ -22,4 +28,19 @@ $wb["retry_error_notpositive"] = 'The retry must be positive!'; $wb["expire_error_notpositive"] = 'The expire must be positive!'; $wb["minimum_error_notpositive"] = 'The minimum TTL must be positive!'; $wb["ttl_error_notpositive"] = 'The TTL must be positive!'; +$wb["error_empty"] = 'must not be empty!'; +$wb["error_dot"] = 'must end with a dot!'; +$wb["error_must_be_integer"] = 'must be an integer!'; +$wb["error_must_not_be_greater_than_2147483647"] = 'must not be greater than 2147483647!'; +$wb["error_must_be_positive"] = 'must be positive!'; +$wb["error_must_not_be_negative"] = 'must not be negative!'; +$wb["error_255_characters"] = 'contains more than 255 characters!'; +$wb["error_63_characters"] = 'contains a part with more than 63 characters!'; +$wb["error_invalid_characters"] = 'contains a part with invalid characters!'; +$wb["error_hyphen_begin"] = 'contains a part that begins with a hyphen!'; +$wb["error_hyphen_end"] = 'contains a part that ends with a hyphen!'; +$wb["error_wildcard_non_initial_part"] = 'contains a wildcard in a non-initial part!'; +$wb["error_wildcard_mix"] = 'contains a part mixing a wildcard character with other data!'; +$wb["error_no_wildcard_allowed"] = 'must not contain wildcards!'; +$wb["error_out_of_zone"] = 'is out of zone!'; ?> \ No newline at end of file diff --git a/interface/web/dns/rr_edit.php b/interface/web/dns/rr_edit.php index 582fec8e3..ff8590d93 100644 --- a/interface/web/dns/rr_edit.php +++ b/interface/web/dns/rr_edit.php @@ -58,6 +58,9 @@ class page_action extends tform_actions { $this->dataRecord["zone"] = $_SESSION['s']['list']['rr']['parent_id']; + $app->uses('validate_dns'); + $app->tform->errorMessage .= $app->validate_dns->validate_rr($this->dataRecord); + // update serial $soa = $app->db->queryOneRecord("SELECT * FROM soa WHERE id = ".$this->dataRecord["zone"]); $serial = $soa['serial']; diff --git a/interface/web/dns/soa_edit.php b/interface/web/dns/soa_edit.php index 4f3179b9f..77fc41785 100644 --- a/interface/web/dns/soa_edit.php +++ b/interface/web/dns/soa_edit.php @@ -56,6 +56,9 @@ class page_action extends tform_actions { function onSubmit() { global $app, $conf; + $app->uses('validate_dns'); + $app->tform->errorMessage .= $app->validate_dns->validate_soa($this->dataRecord); + // update serial $soa = $app->db->queryOneRecord("SELECT * FROM soa WHERE id = ".$this->dataRecord["id"]); $serial = $soa['serial']; -- GitLab