hyper_simple_server/
sanitize.rs1
2
3use crate::prelude::*;
4
5
6
7
8#[ cfg (feature = "hss-sanitize") ]
9pub fn sanitize_scheme (_scheme : &Scheme) -> ServerResult<Option<Scheme>> {
10
11 Ok (None)
14}
15
16
17
18
19#[ cfg (feature = "hss-sanitize") ]
20pub fn sanitize_authority (_authority : &Authority) -> ServerResult<Option<Authority>> {
21
22 let _authority = _authority.as_str ();
23
24 if let Some (_offset) = _authority.find ('@') {
25
26 let _authority = Authority::try_from (&_authority[_offset + 1 ..]) .or_wrap (0xda6f459d) ?;
28
29 Ok (Some (_authority))
30
31 } else {
32 Ok (None)
33 }
34}
35
36
37
38
39#[ cfg (feature = "hss-sanitize") ]
40pub fn sanitize_path (_path : &str) -> ServerResult<Option<String>> {
41
42 let _path_as_str = _path;
43 let _path_as_bytes = _path_as_str.as_bytes ();
44 drop (_path);
45
46 match _path_as_bytes {
47
48 b"" =>
49 Ok (Some (String::from ("/"))),
51 b"/" | b"*" =>
52 Ok (None),
53
54 _ if _path_as_bytes[0] != b'/' =>
55 return Err (error_with_code (0x0705e550)),
56
57 _ => {
58
59 let mut _normalize = false;
60 if !_normalize && _path_as_str.contains ("//") {
61 _normalize = true;
62 }
63 if !_normalize && (_path_as_str.contains ("/./") || _path_as_bytes.ends_with (b"/.")) {
64 _normalize = true;
65 }
66 if !_normalize && (_path_as_str.contains ("/../") || _path_as_bytes.ends_with (b"/..")) {
67 _normalize = true;
68 }
69 if !_normalize && _path_as_bytes.contains (&b'%') {
70 _normalize = true;
71 }
72
73 if _normalize {
74
75 let mut _buffer = Vec::with_capacity (_path_as_bytes.len ());
76 enum State {
77 Normal,
78 Percent0,
79 Percent1 (u8),
80 }
81 let mut _rest = _path_as_bytes;
82 let mut _last_push = None;
83 let mut _last_state = State::Normal;
84
85 loop {
86
87 let _current = if let [_head, _tail @ ..] = _rest {
88 _rest = _tail;
89 *_head
90 } else {
91 break;
92 };
93
94 let (mut _next_push, _next_state) = match _last_state {
95 State::Normal =>
96 match _current {
98 b'?' | b'#' =>
100 (Some (_current), State::Normal),
101 b'%' =>
102 (None, State::Percent0),
103 0x21 | 0x24 ..= 0x3B | 0x3D | 0x40 ..= 0x5F | 0x61 ..= 0x7A | 0x7C | 0x7E =>
104 (Some (_current), State::Normal),
105 _ =>
106 return Err (error_with_code (0x9c6c8644)),
107 },
108 State::Percent0 | State::Percent1 (_) => {
109 let _digit_2 = match _current {
110 b'0' ..= b'9' => _current - b'0',
111 b'A' ..= b'F' => _current - b'A' + 10,
112 b'a' ..= b'f' => _current - b'a' + 10,
113 _ =>
114 return Err (error_with_code (0x563f825c)),
115 };
116 match _last_state {
117 State::Percent0 =>
118 (None, State::Percent1 (_digit_2)),
119 State::Percent1 (_digit_1) => {
120 let _byte = (_digit_1 << 4) | _digit_2;
121 (Some (_byte), State::Normal)
122 }
123 _ =>
124 panic_with_code (0xacb15742),
125 }
126 }
127 };
128
129 if (_next_push == Some (b'/')) && (_last_push == Some (b'/')) {
130 _next_push = None;
131 }
132
133 if let Some (_next_push) = _next_push {
134 let _percent = match _next_push {
135 b'?' | b'#' | b'%' =>
136 true,
137 0x21 | 0x24 ..= 0x3B | 0x3D | 0x40 ..= 0x5F | 0x61 ..= 0x7A | 0x7C | 0x7E =>
138 false,
139 _ =>
140 true,
141 };
142 if _percent {
143 let _digit_1 = _next_push >> 4;
144 let _digit_2 = _next_push & 0x0f;
145 _buffer.push (b'%');
146 _buffer.push (if _digit_1 <= 9 { _digit_1 + b'0' } else { _digit_1 - 10 + b'A' });
147 _buffer.push (if _digit_2 <= 9 { _digit_2 + b'0' } else { _digit_2 - 10 + b'A' });
148 } else {
149 _buffer.push (_next_push);
150 }
151 }
152
153 _last_push = _next_push;
154 _last_state = _next_state;
155 }
156
157 match _last_state {
158 State::Normal => (),
159 State::Percent0 | State::Percent1 (_) =>
160 return Err (error_with_code (0x574c1224)),
161 }
162
163 let mut _buffer = String::from_utf8 (_buffer) .or_wrap (0x88642ea3) ?;
164
165 while let Some (_offset) = _buffer.rfind ("//") {
166 _buffer.remove (_offset);
167 }
168 if _buffer.ends_with ("/.") {
169 _buffer.push ('/');
170 }
171 while let Some (_offset) = _buffer.rfind ("/./") {
172 _buffer.replace_range (_offset .. _offset + 3, "/");
173 }
174 if _buffer.ends_with ("/..") {
175 _buffer.push ('/');
176 }
177 while let Some (_offset_2) = _buffer.find ("/../") {
178 let _offset_1 = if _offset_2 > 0 { _buffer[0.._offset_2].rfind ('/') .or_panic (0x3693e145) } else { 0 };
179 _buffer.replace_range (_offset_1 .. _offset_2 + 4, "/");
180 }
181
182 Ok (Some (_buffer))
183
184 } else {
185 Ok (None)
186 }
187 }
188 }
189}
190
191
192
193
194#[ cfg (feature = "hss-sanitize") ]
195pub fn sanitize_query (_query : &str) -> ServerResult<Option<String>> {
196
197 match _query {
198
199 "" =>
200 Ok (Some (String::new ())),
201
202 _ =>
203 Ok (None),
205 }
206}
207
208
209
210
211#[ cfg (feature = "hss-sanitize") ]
212pub fn sanitize_path_and_query (_path_and_query : &PathAndQuery) -> ServerResult<Option<PathAndQuery>> {
213
214 let _path = sanitize_path (_path_and_query.path ()) ?;
215
216 let _query = if let Some (_query) = _path_and_query.query () {
217 sanitize_query (_query) ?
218 } else {
219 None
220 };
221
222 if _path.is_none () && _query.is_none () {
223 return Ok (None);
224 }
225
226 let _path = _path.as_ref () .map (String::as_str) .unwrap_or_else (|| _path_and_query.path ());
227 let _query = _query.as_ref () .map (String::as_str) .or_else (|| _path_and_query.query ()) .unwrap_or ("");
228
229 let mut _buffer = String::with_capacity (_path.len () + _query.len () + 1);
230 _buffer.push_str (_path);
231 if ! _query.is_empty () {
232 _buffer.push ('?');
233 _buffer.push_str (_query);
234 }
235
236 let _path_and_query = PathAndQuery::try_from (_buffer) .or_wrap (0x7d1433ad) ?;
237
238 Ok (Some (_path_and_query))
239}
240
241
242
243
244#[ cfg (feature = "hss-sanitize") ]
245pub fn sanitize_uri (_uri : &Uri) -> ServerResult<Option<Uri>> {
246
247let _scheme = if let Some (_scheme) = _uri.scheme () {
250 sanitize_scheme (_scheme) ?
251 } else {
252 None
253 };
254
255 let _authority = if let Some (_authority) = _uri.authority () {
256 sanitize_authority (_authority) ?
257 } else {
258 None
259 };
260
261 let _path_and_query = if let Some (_path_and_query) = _uri.path_and_query () {
262 sanitize_path_and_query (_path_and_query) ?
263 } else {
264 Some (PathAndQuery::from_static ("/"))
266 };
267
268 if _scheme.is_none () && _authority.is_none () && _path_and_query.is_none () {
269 return Ok (None);
270 }
271
272 let _uri_parts = _uri.clone ();
275
276 #[ allow (unsafe_code) ]
277 let mut _uri_parts : (Scheme, Authority, PathAndQuery) = unsafe { mem::transmute (_uri_parts) };
278
279 if let Some (_scheme) = _scheme {
280 _uri_parts.0 = _scheme;
281 }
282 if let Some (_authority) = _authority {
283 _uri_parts.1 = _authority;
284 }
285 if let Some (_path_and_query) = _path_and_query {
286 _uri_parts.2 = _path_and_query;
287 }
288
289 #[ allow (unsafe_code) ]
290 let _uri : Uri = unsafe { mem::transmute (_uri_parts) };
291
292 return Ok (Some (_uri));
293}
294