RESTinio

ws_protocol_validator.hpp
Go to the documentation of this file.
1
5#pragma once
6
7#include <restinio/exception.hpp>
8#include <restinio/websocket/impl/utf8.hpp>
9#include <restinio/websocket/impl/ws_parser.hpp>
10
11 namespace restinio
12{
13
14 namespace websocket
15{
16
17 namespace basic
18{
19
20 namespace impl
21{
22
23//
24// validation_state_t
25//
26
28 enum class validation_state_t
29{
30 initial_state,
31 //correct codes
32 frame_header_is_valid,
33 payload_part_is_valid,
34 frame_is_valid,
35 // header validation error codes
36 invalid_opcode,
37 empty_mask_from_client_side,
38 non_final_control_frame,
39 non_zero_rsv_flags,
40 payload_len_is_too_big,
41 // frame order error codes
42 continuation_frame_without_data_frame,
43 new_data_frame_without_finishing_previous,
44 // payload validation error codes
45 invalid_close_code,
46 incorrect_utf8_data
47};
48
49//
50// validation_state_str
51//
52
54 inline const char *
55 validation_state_str( validation_state_t state )
56{
57 static constexpr const char* table[] =
58 {
59 "initial_state",
60 "frame_header_is_valid",
61 "payload_part_is_valid",
62 "frame_is_valid",
63 "invalid_opcode",
64 "empty_mask_from_client_side",
65 "non_final_control_frame",
66 "non_zero_rsv_flags",
67 "payload_len_is_too_big",
68 "continuation_frame_without_data_frame",
69 "new_data_frame_without_finishing_previous",
70 "invalid_close_code",
71 "incorrect_utf8_data"
72 };
73
74 return table[static_cast<unsigned int>(state)];
75}
76
77//
78// is_control_frame
79//
80
82
86 inline bool
87 is_control_frame( opcode_t opcode )
88{
89 return opcode == opcode_t::connection_close_frame ||
90 opcode == opcode_t::ping_frame ||
91 opcode == opcode_t::pong_frame;
92}
93
94//
95// is_data_frame
96//
97
99
103 inline bool
104 is_data_frame( opcode_t opcode )
105{
106 return opcode == opcode_t::text_frame ||
107 opcode == opcode_t::binary_frame;
108}
109
110//
111// unmasker_t
112//
113
119 struct unmasker_t
120{
121 unmasker_t() = default;
122
123 unmasker_t( uint32_t masking_key )
124 : m_mask{
125 {::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 24 >(
126 masking_key),
127 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 16 >(
128 masking_key),
129 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 8 >(
130 masking_key),
131 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 0 >(
132 masking_key)} }
133 {
134 }
135
137
140 uint8_t
141 unmask_byte( uint8_t masked_byte )
142 {
143 return masked_byte ^ m_mask[ (m_processed_bytes_count++) % 4 ];
144 }
145
147 void
148 reset( uint32_t masking_key )
149 {
150 m_processed_bytes_count = 0;
151
152 m_mask = mask_array_t{
153 {::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 24 >(
154 masking_key),
155 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 16 >(
156 masking_key),
157 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 8 >(
158 masking_key),
159 ::restinio::utils::impl::bitops::n_bits_from< std::uint8_t, 0 >(
160 masking_key)} };
161
162 }
163
164 using mask_array_t = std::array< uint8_t, websocket_masking_key_size>;
165
167 mask_array_t m_mask;
168
170
174 size_t m_processed_bytes_count{ 0 };
175};
176
177//
178// ws_protocol_validator_t
179//
180
182
194 class ws_protocol_validator_t
195{
196 public:
197
198 ws_protocol_validator_t() = default;
199
200 ws_protocol_validator_t( bool do_unmask )
201 : m_unmask_flag{ do_unmask }
202 {
203 }
204
206
210 validation_state_t
211 process_new_frame( const message_details_t & frame )
212 {
213 if( m_working_state != working_state_t::empty_state )
214 throw exception_t( "another frame is processing now" );
215
216 if( validate_frame_header( frame ) &&
217 check_previous_frame_type( frame.m_opcode ) )
218 {
219 switch( frame.m_opcode )
220 {
221 case opcode_t::text_frame:
222 if( !frame.m_final_flag )
223 m_previous_data_frame = previous_data_frame_t::text;
224 break;
225
226 case opcode_t::binary_frame:
227 if( !frame.m_final_flag )
228 m_previous_data_frame = previous_data_frame_t::binary;
229 break;
230
231 case opcode_t::connection_close_frame:
232 m_expected_close_code.reset(2);
233 break;
234
235 default:
236 break;
237 }
238
239 m_current_frame = frame;
240 m_working_state = working_state_t::processing_frame;
241
242 if( m_unmask_flag )
243 {
244 m_unmasker.reset( frame.m_masking_key );
245 }
246 }
247
248 return m_validation_state;
249 }
250
252 validation_state_t
253 process_next_payload_part( const char * data, size_t size )
254 {
255 if( m_working_state == working_state_t::empty_state )
256 throw exception_t( "current state is empty" );
257
258 if( is_state_still_valid() )
259 set_validation_state(
260 validation_state_t::payload_part_is_valid );
261 else
262 return m_validation_state;
263
264 for( size_t i = 0; i < size; ++i )
265 {
266 process_payload_byte(
267 static_cast<std::uint8_t>(data[i]) );
268
269 if( m_validation_state != validation_state_t::payload_part_is_valid )
270 break;
271 }
272
273 return m_validation_state;
274 }
275
277 validation_state_t
278 process_and_unmask_next_payload_part( char * data, size_t size )
279 {
280 if( m_working_state == working_state_t::empty_state )
281 throw exception_t( "current state is empty" );
282
283 if( is_state_still_valid() )
284 set_validation_state(
285 validation_state_t::payload_part_is_valid );
286 else
287 return m_validation_state;
288
289 for( size_t i = 0; i < size; ++i )
290 {
291 data[i] = static_cast<char>(process_payload_byte(
292 static_cast<std::uint8_t>(data[i]) ));
293
294 if( m_validation_state != validation_state_t::payload_part_is_valid )
295 break;
296 }
297
298 return m_validation_state;
299 }
300
302 validation_state_t
303 finish_frame()
304 {
305 if( is_state_still_valid() )
306 set_validation_state( validation_state_t::frame_is_valid );
307
308 if( m_current_frame.m_final_flag )
309 {
310 if( !m_utf8_checker.finalized() )
311 set_validation_state(
312 validation_state_t::incorrect_utf8_data );
313
314 m_utf8_checker.reset();
315 }
316
317 m_working_state = working_state_t::empty_state;
318
319 // If continued data frame is present and current processed frame is
320 // continuation frame with final bit set in 1 then reset current continued
321 // data frame type.
322 if( m_previous_data_frame != previous_data_frame_t::none &&
323 !is_control_frame(m_current_frame.m_opcode) &&
324 m_current_frame.m_final_flag )
325 {
326 m_previous_data_frame =
327 previous_data_frame_t::none;
328 }
329
330 // Remember current frame vaidation state and return this value.
331 auto this_frame_validation_state = m_validation_state;
332
333 // Reset validation state for next frame.
334 m_validation_state = validation_state_t::initial_state;
335
336 return this_frame_validation_state;
337 }
338
340 void
341 reset()
342 {
343 m_validation_state = validation_state_t::initial_state;
344 m_working_state = working_state_t::empty_state;
345 m_previous_data_frame =
346 previous_data_frame_t::none;
347
348 m_utf8_checker.reset();
349 }
350
351 private:
352
354
360 bool
361 validate_frame_header( const message_details_t & frame )
362 {
363 set_validation_state(
364 validation_state_t::frame_header_is_valid );
365
366 if( !is_valid_opcode( frame.m_opcode ) )
367 {
368 set_validation_state(
369 validation_state_t::invalid_opcode );
370 }
371 else if( is_control_frame(frame.m_opcode) && !frame.m_final_flag )
372 {
373 set_validation_state(
374 validation_state_t::non_final_control_frame );
375 }
376 else if( !frame.m_mask_flag )
377 {
378 set_validation_state(
379 validation_state_t::empty_mask_from_client_side );
380 }
381 else if( frame.m_rsv1_flag != 0 ||
382 frame.m_rsv2_flag != 0 ||
383 frame.m_rsv3_flag != 0)
384 {
385 set_validation_state(
386 validation_state_t::non_zero_rsv_flags );
387 }
388 else if( is_control_frame(frame.m_opcode) && frame.payload_len() >
389 websocket_max_payload_size_without_ext )
390 {
391 set_validation_state(
392 validation_state_t::payload_len_is_too_big );
393 }
394
395 return validation_state_t::frame_header_is_valid == m_validation_state;
396 }
397
399
405 std::uint8_t
406 process_payload_byte( std::uint8_t byte )
407 {
408 byte = m_unmask_flag?
409 m_unmasker.unmask_byte( byte ): byte;
410
411 if( m_current_frame.m_opcode == opcode_t::text_frame ||
412 (m_current_frame.m_opcode == opcode_t::continuation_frame &&
413 m_previous_data_frame == previous_data_frame_t::text) )
414 {
415 if( !m_utf8_checker.process_byte( byte ) )
416 {
417 set_validation_state(
418 validation_state_t::incorrect_utf8_data );
419 }
420 }
421 else if( m_current_frame.m_opcode == opcode_t::connection_close_frame )
422 {
423 if( !m_expected_close_code.all_bytes_loaded() )
424 {
425 if( m_expected_close_code.add_byte_and_check_size(byte) )
426 {
427 uint16_t status_code{0};
428
429 read_number_from_big_endian_bytes(
430 status_code,m_expected_close_code.m_loaded_data );
431
432 validate_close_code( status_code );
433 }
434 }
435 else
436 {
437 if( !m_utf8_checker.process_byte( byte ) )
438 set_validation_state(
439 validation_state_t::incorrect_utf8_data );
440 }
441 }
442
443 return byte;
444 }
445
447
459 bool
460 check_previous_frame_type( opcode_t opcode )
461 {
462 if( m_previous_data_frame == previous_data_frame_t::none &&
463 opcode == opcode_t::continuation_frame )
464 {
465 set_validation_state(
466 validation_state_t::continuation_frame_without_data_frame );
467
468 return false;
469 }
470 else if( m_previous_data_frame !=
471 previous_data_frame_t::none &&
472 is_data_frame( opcode ) )
473 {
474 set_validation_state(
475 validation_state_t::new_data_frame_without_finishing_previous );
476
477 return false;
478 }
479
480 return true;
481 }
482
484 void
485 validate_close_code( uint16_t close_code )
486 {
487 if( close_code < 1000 ||
488 (close_code > 1011 && close_code < 3000) ||
489 close_code > 4999 )
490 {
491 set_validation_state(
492 validation_state_t::invalid_close_code );
493
494 return;
495 }
496
497 if( close_code == 1004 ||
498 close_code == 1005 ||
499 close_code == 1006 )
500 {
501 set_validation_state(
502 validation_state_t::invalid_close_code );
503
504 return;
505 }
506 }
507
509 bool
510 is_state_still_valid() const
511 {
512 return m_validation_state == validation_state_t::initial_state ||
513 m_validation_state == validation_state_t::frame_header_is_valid ||
514 m_validation_state == validation_state_t::payload_part_is_valid ||
515 m_validation_state == validation_state_t::frame_is_valid;
516 }
517
519
522 void
523 set_validation_state( validation_state_t state )
524 {
525 m_validation_state = state;
526 }
527
529
536 validation_state_t m_validation_state{
537 validation_state_t::initial_state };
538
540 enum class working_state_t
541 {
543 empty_state,
545 processing_frame
546 };
547
549
553 enum class previous_data_frame_t
554 {
555 none,
556 text,
557 binary
558 };
559
561 working_state_t m_working_state{ working_state_t::empty_state };
562
564 previous_data_frame_t m_previous_data_frame{
565 previous_data_frame_t::none };
566
568 message_details_t m_current_frame;
569
571 expected_data_t m_expected_close_code;
572
574 restinio::utils::utf8_checker_t m_utf8_checker;
575
577 bool m_unmask_flag{ false };
578
580 unmasker_t m_unmasker;
581};
582
583} /* namespace impl */
584
585} /* namespace basic */
586
587} /* namespace websocket */
588
589} /* namespace restinio */
restinio::exception_t
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
restinio::utils::utf8_checker_t
Helper class for checking UTF-8 byte sequence during parsing URI or incoming byte stream.
Definition: utf8_checker.hpp:35
restinio::websocket::basic::impl::message_details_t
Websocket message class with more detailed protocol information.
Definition: ws_parser.hpp:63
restinio::websocket::basic::impl::message_details_t::m_rsv2_flag
bool m_rsv2_flag
Definition: ws_parser.hpp:112
restinio::websocket::basic::impl::message_details_t::payload_len
std::uint64_t payload_len() const
Get payload len.
Definition: ws_parser.hpp:92
restinio::websocket::basic::impl::message_details_t::m_rsv1_flag
bool m_rsv1_flag
Reserved flags.
Definition: ws_parser.hpp:111
restinio::websocket::basic::impl::message_details_t::m_rsv3_flag
bool m_rsv3_flag
Definition: ws_parser.hpp:113
restinio::websocket::basic::impl::message_details_t::m_masking_key
std::uint32_t m_masking_key
Masking key.
Definition: ws_parser.hpp:132
restinio::websocket::basic::impl::message_details_t::m_final_flag
bool m_final_flag
Final flag.
Definition: ws_parser.hpp:107
restinio::websocket::basic::impl::message_details_t::m_mask_flag
bool m_mask_flag
Mask flag.
Definition: ws_parser.hpp:120
restinio::websocket::basic::impl::message_details_t::m_opcode
opcode_t m_opcode
Opcode.
Definition: ws_parser.hpp:117
restinio::websocket::basic::impl::ws_protocol_validator_t
Class for websocket protocol validations.
Definition: ws_protocol_validator.hpp:195
restinio::websocket::basic::impl::ws_protocol_validator_t::process_and_unmask_next_payload_part
validation_state_t process_and_unmask_next_payload_part(char *data, size_t size)
Validate next part of current frame and reset source part to unmasked data.
Definition: ws_protocol_validator.hpp:278
restinio::websocket::basic::impl::ws_protocol_validator_t::validate_frame_header
bool validate_frame_header(const message_details_t &frame)
Validate frame header.
Definition: ws_protocol_validator.hpp:361
restinio::websocket::basic::impl::ws_protocol_validator_t::ws_protocol_validator_t
ws_protocol_validator_t()=default
restinio::websocket::basic::impl::ws_protocol_validator_t::process_new_frame
validation_state_t process_new_frame(const message_details_t &frame)
Start work with new frame.
Definition: ws_protocol_validator.hpp:211
restinio::websocket::basic::impl::ws_protocol_validator_t::is_state_still_valid
bool is_state_still_valid() const
Check validation state is still valid.
Definition: ws_protocol_validator.hpp:510
restinio::websocket::basic::impl::ws_protocol_validator_t::working_state_t
working_state_t
Validator's orking states.
Definition: ws_protocol_validator.hpp:541
restinio::websocket::basic::impl::ws_protocol_validator_t::m_unmasker
unmasker_t m_unmasker
Unmask payload coming from client side.
Definition: ws_protocol_validator.hpp:580
restinio::websocket::basic::impl::ws_protocol_validator_t::process_payload_byte
std::uint8_t process_payload_byte(std::uint8_t byte)
Process payload byte.
Definition: ws_protocol_validator.hpp:406
restinio::websocket::basic::impl::ws_protocol_validator_t::previous_data_frame_t
previous_data_frame_t
Previous unfinished data frame type.
Definition: ws_protocol_validator.hpp:554
restinio::websocket::basic::impl::ws_protocol_validator_t::process_next_payload_part
validation_state_t process_next_payload_part(const char *data, size_t size)
Validate next part of current frame.
Definition: ws_protocol_validator.hpp:253
restinio::websocket::basic::impl::ws_protocol_validator_t::m_expected_close_code
expected_data_t m_expected_close_code
Buffer for accumulating 2 bytes of close code.
Definition: ws_protocol_validator.hpp:571
restinio::websocket::basic::impl::ws_protocol_validator_t::validate_close_code
void validate_close_code(uint16_t close_code)
Validate close code.
Definition: ws_protocol_validator.hpp:485
restinio::websocket::basic::impl::ws_protocol_validator_t::check_previous_frame_type
bool check_previous_frame_type(opcode_t opcode)
Check previous frame type.
Definition: ws_protocol_validator.hpp:460
restinio::websocket::basic::impl::ws_protocol_validator_t::ws_protocol_validator_t
ws_protocol_validator_t(bool do_unmask)
Definition: ws_protocol_validator.hpp:200
restinio::websocket::basic::impl::ws_protocol_validator_t::m_current_frame
message_details_t m_current_frame
Current frame details.
Definition: ws_protocol_validator.hpp:568
restinio::websocket::basic::impl::ws_protocol_validator_t::set_validation_state
void set_validation_state(validation_state_t state)
Try to set validation state.
Definition: ws_protocol_validator.hpp:523
restinio::websocket::basic::impl::ws_protocol_validator_t::finish_frame
validation_state_t finish_frame()
Make final checks of payload if it is necessary and reset state.
Definition: ws_protocol_validator.hpp:303
restinio::websocket::basic::impl::ws_protocol_validator_t::m_utf8_checker
restinio::utils::utf8_checker_t m_utf8_checker
UTF-8 checker for text frames and close frames.
Definition: ws_protocol_validator.hpp:574
restinio::websocket::basic::impl::ws_protocol_validator_t::reset
void reset()
Reset to initial state.
Definition: ws_protocol_validator.hpp:341
exception.hpp
restinio::utils::sha1::details::byte
unsigned int byte(digest_t::value_type v)
Definition: sha1.hpp:365
restinio::websocket::basic::impl::is_data_frame
bool is_data_frame(opcode_t opcode)
Check frame is data frame.
Definition: ws_protocol_validator.hpp:104
restinio::websocket::basic::impl::read_number_from_big_endian_bytes
void read_number_from_big_endian_bytes(T &number, const raw_data_t &data)
Read number from buffer with network bytes order.
Definition: ws_parser.hpp:221
restinio::websocket::basic::impl::websocket_max_payload_size_without_ext
constexpr size_t websocket_max_payload_size_without_ext
Definition: ws_parser.hpp:42
restinio::websocket::basic::impl::is_control_frame
bool is_control_frame(opcode_t opcode)
Check frame is control frame.
Definition: ws_protocol_validator.hpp:87
restinio::websocket::basic::impl::validation_state_str
const char * validation_state_str(validation_state_t state)
Helper function for logging validation states.
Definition: ws_protocol_validator.hpp:55
restinio::websocket::basic::impl::validation_state_t
validation_state_t
States of validated frame.
Definition: ws_protocol_validator.hpp:29
restinio::websocket::basic::impl::validation_state_t::payload_part_is_valid
@ payload_part_is_valid
restinio::websocket::basic::impl::validation_state_t::non_final_control_frame
@ non_final_control_frame
restinio::websocket::basic::impl::validation_state_t::payload_len_is_too_big
@ payload_len_is_too_big
restinio::websocket::basic::impl::validation_state_t::non_zero_rsv_flags
@ non_zero_rsv_flags
restinio::websocket::basic::impl::validation_state_t::frame_header_is_valid
@ frame_header_is_valid
restinio::websocket::basic::impl::validation_state_t::incorrect_utf8_data
@ incorrect_utf8_data
restinio::websocket::basic::impl::validation_state_t::continuation_frame_without_data_frame
@ continuation_frame_without_data_frame
restinio::websocket::basic::impl::validation_state_t::empty_mask_from_client_side
@ empty_mask_from_client_side
restinio::websocket::basic::impl::validation_state_t::invalid_close_code
@ invalid_close_code
restinio::websocket::basic::impl::validation_state_t::initial_state
@ initial_state
restinio::websocket::basic::impl::validation_state_t::new_data_frame_without_finishing_previous
@ new_data_frame_without_finishing_previous
restinio::websocket::basic::impl::validation_state_t::frame_is_valid
@ frame_is_valid
restinio::websocket::basic::impl::validation_state_t::invalid_opcode
@ invalid_opcode
restinio::websocket::basic::opcode_t
opcode_t
Definition: message.hpp:37
restinio::websocket::basic::is_valid_opcode
bool is_valid_opcode(opcode_t opcode)
Definition: message.hpp:64
restinio
Definition: asio_include.hpp:21
restinio::websocket::basic::impl::expected_data_t
Data with expected size.
Definition: ws_parser.hpp:166
restinio::websocket::basic::impl::unmasker_t
Definition: ws_protocol_validator.hpp:120
restinio::websocket::basic::impl::unmasker_t::reset
void reset(uint32_t masking_key)
Reset to initial state.
Definition: ws_protocol_validator.hpp:148
restinio::websocket::basic::impl::unmasker_t::unmasker_t
unmasker_t()=default
restinio::websocket::basic::impl::unmasker_t::m_mask
mask_array_t m_mask
Bytes array with masking key.
Definition: ws_protocol_validator.hpp:167
restinio::websocket::basic::impl::unmasker_t::mask_array_t
std::array< uint8_t, websocket_masking_key_size > mask_array_t
Definition: ws_protocol_validator.hpp:164
restinio::websocket::basic::impl::unmasker_t::unmasker_t
unmasker_t(uint32_t masking_key)
Definition: ws_protocol_validator.hpp:123
restinio::websocket::basic::impl::unmasker_t::unmask_byte
uint8_t unmask_byte(uint8_t masked_byte)
Do unmask operation.
Definition: ws_protocol_validator.hpp:141
utf8.hpp
ws_parser.hpp