RESTinio
ws_protocol_validator.hpp
Go to the documentation of this file.
1
5#pragma once
6
10
11namespace restinio
12{
13
14namespace websocket
15{
16
17namespace basic
18{
19
20namespace impl
21{
22
23//
24// validation_state_t
25//
26
29{
31 //correct codes
35 // header validation error codes
41 // frame order error codes
44 // payload validation error codes
47};
48
49//
50// validation_state_str
51//
52
54inline const char *
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
86inline bool
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
103inline bool
105{
106 return opcode == opcode_t::text_frame ||
107 opcode == opcode_t::binary_frame;
108}
109
110//
111// unmasker_t
112//
113
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
168
170
174 size_t m_processed_bytes_count{ 0 };
175};
176
177//
178// ws_protocol_validator_t
179//
180
182
195{
196 public:
197
199
200 ws_protocol_validator_t( bool do_unmask )
201 : m_unmask_flag{ do_unmask }
202 {
203 }
204
206
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
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
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
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
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
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() >
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
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
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
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
524 {
525 m_validation_state = state;
526 }
527
529
536 validation_state_t m_validation_state{
537 validation_state_t::initial_state };
538
541 {
543 empty_state,
545 processing_frame
546 };
547
549
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
569
572
575
577 bool m_unmask_flag{ false };
578
581};
582
583} /* namespace impl */
584
585} /* namespace basic */
586
587} /* namespace websocket */
588
589} /* namespace restinio */
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
Helper class for checking UTF-8 byte sequence during parsing URI or incoming byte stream.
Websocket message class with more detailed protocol information.
Definition: ws_parser.hpp:63
std::uint64_t payload_len() const
Get payload len.
Definition: ws_parser.hpp:92
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.
bool validate_frame_header(const message_details_t &frame)
Validate frame header.
validation_state_t process_new_frame(const message_details_t &frame)
Start work with new frame.
bool is_state_still_valid() const
Check validation state is still valid.
unmasker_t m_unmasker
Unmask payload coming from client side.
std::uint8_t process_payload_byte(std::uint8_t byte)
Process payload byte.
validation_state_t process_next_payload_part(const char *data, size_t size)
Validate next part of current frame.
expected_data_t m_expected_close_code
Buffer for accumulating 2 bytes of close code.
void validate_close_code(uint16_t close_code)
Validate close code.
bool check_previous_frame_type(opcode_t opcode)
Check previous frame type.
void set_validation_state(validation_state_t state)
Try to set validation state.
validation_state_t finish_frame()
Make final checks of payload if it is necessary and reset state.
restinio::utils::utf8_checker_t m_utf8_checker
UTF-8 checker for text frames and close frames.
unsigned int byte(digest_t::value_type v)
Definition: sha1.hpp:365
bool is_data_frame(opcode_t opcode)
Check frame is data frame.
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
constexpr size_t websocket_max_payload_size_without_ext
Definition: ws_parser.hpp:42
bool is_control_frame(opcode_t opcode)
Check frame is control frame.
const char * validation_state_str(validation_state_t state)
Helper function for logging validation states.
validation_state_t
States of validated frame.
bool is_valid_opcode(opcode_t opcode)
Definition: message.hpp:64
void reset(uint32_t masking_key)
Reset to initial state.
mask_array_t m_mask
Bytes array with masking key.
std::array< uint8_t, websocket_masking_key_size > mask_array_t
uint8_t unmask_byte(uint8_t masked_byte)
Do unmask operation.