1+ <?php
2+
3+ /*
4+ Copyright (c) 2005, Till Brehm, Falko Timme, projektfarm Gmbh
5+ All rights reserved.
6+
7+ Redistribution and use in source and binary forms, with or without modification,
8+ are permitted provided that the following conditions are met:
9+
10+ * Redistributions of source code must retain the above copyright notice,
11+ this list of conditions and the following disclaimer.
12+ * Redistributions in binary form must reproduce the above copyright notice,
13+ this list of conditions and the following disclaimer in the documentation
14+ and/or other materials provided with the distribution.
15+ * Neither the name of ISPConfig nor the names of its contributors
16+ may be used to endorse or promote products derived from this software without
17+ specific prior written permission.
18+
19+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22+ IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+ */
30+
31+ /**
32+ * DNS validation
33+ *
34+ * @author Falko Timme <ft@falkotimme.com>
35+ * @copyright Copyright © 2005, Falko Timme
36+ */
37+
38+ class validate_dns {
39+
40+ function validate_field ($ field , $ area , $ zoneid , $ wildcard_allowed = 1 ){
41+ //$desc: Name, Data, RP mbox, RP txtref, SRV target, Zone origin, Name server, Admin email
42+ global $ app , $ conf ;
43+
44+ switch ($ area ) {
45+ case "Name " :
46+ $ desc = $ app ->tform ->wordbook ['name_txt ' ];
47+ break ;
48+ case "Data " :
49+ $ desc = $ app ->tform ->wordbook ['data_txt ' ];
50+ break ;
51+ case "RP mbox " :
52+ $ desc = $ app ->tform ->wordbook ['rp_mbox_txt ' ];
53+ break ;
54+ case "RP txtref " :
55+ $ desc = $ app ->tform ->wordbook ['rp_txtref_txt ' ];
56+ break ;
57+ case "SRV target " :
58+ $ desc = $ app ->tform ->wordbook ['srv_target_txt ' ];
59+ break ;
60+ case "Zone origin " :
61+ $ desc = $ app ->tform ->wordbook ['zone_origin_txt ' ];
62+ break ;
63+ case "Name server " :
64+ $ desc = $ app ->tform ->wordbook ['ns_txt ' ];
65+ break ;
66+ case "Admin email " :
67+ $ desc = $ app ->tform ->wordbook ['mbox_txt ' ];
68+ break ;
69+ }
70+
71+ $ error = '' ;
72+
73+ $ valid_characters = "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_ " ;
74+
75+ if (strlen ($ field ) > 255 ) $ error .= $ desc ." " .$ app ->tform ->wordbook ['error_255_characters ' ]."<br> \r\n" ;
76+
77+ $ parts = explode (". " , $ field );
78+ $ i = 0 ;
79+ foreach ($ parts as $ part ){
80+ $ i ++;
81+
82+ if (strlen ($ part ) > 63 ) $ error .= $ desc ." " .$ app ->tform ->wordbook ['error_63_characters ' ]."<br> \r\n" ;
83+
84+ if (strspn ($ part , $ valid_characters ) != strlen ($ part )) $ error .= $ desc ." " .$ app ->tform ->wordbook ['error_invalid_characters ' ]."<br> \r\n" ;
85+
86+ if (substr ($ part , 0 , 1 ) == '- ' ) $ error .= $ desc ." " .$ app ->tform ->wordbook ['error_hyphen_begin ' ]."<br> \r\n" ;
87+ if (substr ($ part , -1 ) == '- ' ) $ error .= $ desc ." " .$ app ->tform ->wordbook ['error_hyphen_end ' ]."<br> \r\n" ;
88+
89+ if (strstr ($ part , "* " )){
90+ if ($ wildcard_allowed ){
91+ if ($ i != 1 ) $ error .= $ desc ." " .$ app ->tform ->wordbook ['error_wildcard_non_initial_part ' ]."<br> \r\n" ;
92+
93+ if ($ part != "* " ) $ error .= $ desc ." " .$ app ->tform ->wordbook ['error_wildcard_mix ' ]."<br> \r\n" ;
94+ } else {
95+ $ error .= $ desc ." " .$ app ->tform ->wordbook ['error_no_wildcard_allowed ' ]."<br> \r\n" ;
96+ }
97+ }
98+ }
99+
100+ if (substr ($ field , -1 ) == '. ' && $ area == 'Name ' ){
101+ $ soa = $ app ->db ->queryOneRecord ("SELECT * FROM soa WHERE id = " .$ zoneid );
102+ if (substr ($ field , (strlen ($ field ) - strlen ($ soa ['origin ' ]))) != $ soa ['origin ' ]) $ error .= $ desc ." " .$ app ->tform ->wordbook ['error_out_of_zone ' ]."<br> \r\n" ;
103+ }
104+
105+ return $ error ;
106+ }
107+
108+ function validate_rp_data (&$ data , $ zoneid ){
109+ global $ app , $ conf ;
110+ $ error = '' ;
111+ $ fields = explode (" " , trim ($ data ));
112+ if (count ($ fields ) != 2 ) return $ error .= $ app ->tform ->wordbook ['data_txt ' ]." " .$ app ->tform ->wordbook ['error_invalid_rp ' ]."<br> \r\n" ;
113+ $ mbox = $ fields [0 ];
114+ $ txtref = $ fields [1 ];
115+
116+ $ error .= $ this ->validate_field ($ mbox , 'RP mbox ' , $ zoneid , 0 );
117+ $ error .= $ this ->validate_field ($ txtref , 'RP txtref ' , $ zoneid , 0 );
118+
119+ $ data = $ mbox ." " .$ txtref ;
120+ return $ error ;
121+ }
122+
123+ function validate_srv_data (&$ data , $ zoneid ){
124+ global $ app , $ conf ;
125+ $ error = '' ;
126+
127+ $ fields = explode (" " , trim ($ data ));
128+ if (count ($ fields ) != 3 ) return $ error .= $ app ->tform ->wordbook ['data_txt ' ]." " .$ app ->tform ->wordbook ['error_invalid_srv ' ]."<br> \r\n" ;
129+
130+ $ weight = $ fields [0 ];
131+ $ port = $ fields [1 ];
132+ $ target = $ fields [2 ];
133+ 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" ;
134+ 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" ;
135+
136+ $ error .= $ this ->validate_field ($ target , "SRV target " , $ zoneid , 0 );
137+
138+ $ data = (int )$ weight ." " .(int )$ port ." " .$ target ;
139+ return $ error ;
140+ }
141+
142+ function is_integer ($ value , $ fieldname , $ zero_allowed = 0 ){
143+ global $ app , $ conf ;
144+
145+ $ error = '' ;
146+
147+ if (intval ($ value ) != $ value || !is_numeric ($ value )) $ error .= $ fieldname ." " .$ app ->tform ->wordbook ['error_must_be_integer ' ]."<br> \r\n" ;
148+ if ($ value > 2147483647 ) $ error .= $ fieldname ." " .$ app ->tform ->wordbook ['error_must_not_be_greater_than_2147483647 ' ]."<br> \r\n" ;
149+ if (!$ zero_allowed ){
150+ if ($ value <= 0 ) $ error .= $ fieldname ." " .$ app ->tform ->wordbook ['error_must_be_positive ' ]."<br> \r\n" ;
151+ } else {
152+ if ($ value < 0 ) $ error .= $ fieldname ." " .$ app ->tform ->wordbook ['error_must_not_be_negative ' ]."<br> \r\n" ;
153+ }
154+
155+ return $ error ;
156+ }
157+
158+ function validate_rr (&$ rr ){
159+ global $ app , $ conf ;
160+
161+ $ error = '' ;
162+
163+ $ tmp_rr = $ rr ;
164+ foreach ($ tmp_rr as $ key => $ val ){
165+ $ rr [$ key ] = trim ($ val );
166+ }
167+ unset($ tmp_rr );
168+
169+ $ error .= $ this ->validate_field ($ rr ['name ' ], 'Name ' , $ rr ['zone ' ], 1 );
170+
171+ switch ($ rr ['type ' ]) {
172+ case "A " :
173+ $ ip_parts = explode (". " , $ rr ['data ' ]);
174+ if (count ($ ip_parts ) != 4 ){
175+ $ error .= $ app ->tform ->wordbook ['data_txt ' ]." " .$ app ->tform ->wordbook ['error_a ' ]."<br> \r\n" ;
176+ } else {
177+ for ($ n = 0 ; $ n < 4 ; $ n ++){
178+ $ q = $ ip_parts [$ n ];
179+ if (!is_numeric ($ q ) || (int )$ q < 0 || (int )$ q > 255 ) $ error .= $ app ->tform ->wordbook ['data_txt ' ]." " .$ app ->tform ->wordbook ['error_a ' ]."<br> \r\n" ;
180+ }
181+ }
182+ $ rr ['data ' ] = (int )$ ip_parts [0 ].". " .(int )$ ip_parts [1 ].". " .(int )$ ip_parts [2 ].". " .(int )$ ip_parts [3 ];
183+ break ;
184+ case "AAAA " :
185+ $ valid_chars = "ABCDEFabcdef1234567890: " ;
186+
187+ if (strspn ($ rr ['data ' ], $ valid_chars ) != strlen ($ rr ['data ' ])) $ error .= $ app ->tform ->wordbook ['data_txt ' ]." " .$ app ->tform ->wordbook ['error_aaaa ' ]."<br> \r\n" ;
188+ break ;
189+ case "ALIAS " :
190+ $ error .= $ this ->validate_field ($ rr ['data ' ], 'Data ' , $ rr ['zone ' ], 0 );
191+ break ;
192+ case "CNAME " :
193+ $ error .= $ this ->validate_field ($ rr ['data ' ], 'Data ' , $ rr ['zone ' ], 0 );
194+ break ;
195+ case "HINFO " :
196+ if (!strchr ($ rr ['data ' ], ' ' )) $ error .= $ app ->tform ->wordbook ['data_txt ' ]." " .$ app ->tform ->wordbook ['error_hinfo ' ]."<br> \r\n" ;
197+ break ;
198+ case "MX " :
199+ $ error .= $ this ->validate_field ($ rr ['data ' ], 'Data ' , $ rr ['zone ' ], 0 );
200+ break ;
201+ case "NS " :
202+ $ error .= $ this ->validate_field ($ rr ['data ' ], 'Data ' , $ rr ['zone ' ], 0 );
203+ break ;
204+ case "PTR " :
205+ $ error .= $ this ->validate_field ($ rr ['data ' ], 'Data ' , $ rr ['zone ' ], 0 );
206+ if (substr ($ rr ['data ' ], -1 ) != '. ' ) $ error .= $ app ->tform ->wordbook ['data_txt ' ]." " .$ app ->tform ->wordbook ['error_ptr ' ]."<br> \r\n" ;
207+ break ;
208+ case "RP " :
209+ $ error .= $ this ->validate_rp_data ($ rr ['data ' ], $ rr ['zone ' ]);
210+ break ;
211+ case "SRV " :
212+ $ error .= $ this ->validate_srv_data ($ rr ['data ' ], $ rr ['zone ' ]);
213+ break ;
214+ case "TXT " :
215+ break ;
216+ }
217+
218+ $ error .= $ this ->is_integer ($ rr ['aux ' ], $ app ->tform ->wordbook ['aux_txt ' ], 1 );
219+
220+ $ error .= $ this ->is_integer ($ rr ['ttl ' ], $ app ->tform ->wordbook ['ttl_txt ' ]);
221+
222+
223+ return $ error ;
224+ }
225+
226+ function validate_soa (&$ soa ){
227+ global $ app , $ conf ;
228+
229+ $ error = '' ;
230+
231+ $ tmp_soa = $ soa ;
232+ foreach ($ tmp_soa as $ key => $ val ){
233+ if ($ key != 'active ' ) $ soa [$ key ] = trim ($ val );
234+ }
235+ unset($ tmp_soa );
236+
237+ if ($ soa ['origin ' ] == '' ) $ error .= $ app ->tform ->wordbook ['origin_txt ' ]." " .$ app ->tform ->wordbook ['error_empty ' ]."<br> \r\n" ;
238+ if (substr ($ soa ['origin ' ], -1 ) != '. ' ) $ error .= $ app ->tform ->wordbook ['origin_txt ' ]." " .$ app ->tform ->wordbook ['error_dot ' ]."<br> \r\n" ;
239+ $ error .= $ this ->validate_field ($ soa ['origin ' ], "Zone origin " , $ soa ['id ' ], 0 );
240+
241+ $ error .= $ this ->is_integer ($ soa ['ttl ' ], $ app ->tform ->wordbook ['ttl_txt ' ]);
242+
243+ if ($ soa ['ns ' ] == '' ) $ error .= $ app ->tform ->wordbook ['ns_txt ' ]." " .$ app ->tform ->wordbook ['error_empty ' ]."<br> \r\n" ;
244+ $ error .= $ this ->validate_field ($ soa ['ns ' ], "Name server " , $ soa ['id ' ], 0 );
245+
246+ if ($ soa ['mbox ' ] == '' ) $ error .= $ app ->tform ->wordbook ['mbox_txt ' ]." " .$ app ->tform ->wordbook ['error_empty ' ]."<br> \r\n" ;
247+ $ error .= $ this ->validate_field ($ soa ['mbox ' ], "Admin email " , $ soa ['id ' ], 0 );
248+
249+ $ error .= $ this ->is_integer ($ soa ['refresh ' ], $ app ->tform ->wordbook ['refresh_txt ' ]);
250+
251+ $ error .= $ this ->is_integer ($ soa ['retry ' ], $ app ->tform ->wordbook ['retry_txt ' ]);
252+
253+ $ error .= $ this ->is_integer ($ soa ['expire ' ], $ app ->tform ->wordbook ['expire_txt ' ]);
254+
255+ $ error .= $ this ->is_integer ($ soa ['minimum ' ], $ app ->tform ->wordbook ['minimum_txt ' ]);
256+
257+ return $ error ;
258+ }
259+
260+ }
0 commit comments