RESTinio
ws_protocol_validator.hpp
Go to the documentation of this file.
1 
5 #pragma once
6 
7 #include <restinio/exception.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 
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,
47 };
48 
49 //
50 // validation_state_str
51 //
52 
54 inline 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 
86 inline 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 
103 inline 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  {
151 
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 
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  {
214  throw exception_t( "another frame is processing now" );
215 
216  if( validate_frame_header( frame ) &&
218  {
219  switch( frame.m_opcode )
220  {
221  case opcode_t::text_frame:
222  if( !frame.m_final_flag )
224  break;
225 
226  case opcode_t::binary_frame:
227  if( !frame.m_final_flag )
229  break;
230 
231  case opcode_t::connection_close_frame:
233  break;
234 
235  default:
236  break;
237  }
238 
239  m_current_frame = 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  {
256  throw exception_t( "current state is empty" );
257 
258  if( is_state_still_valid() )
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  {
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  {
281  throw exception_t( "current state is empty" );
282 
283  if( is_state_still_valid() )
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 
309  {
310  if( !m_utf8_checker.finalized() )
313 
315  }
316 
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.
325  {
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;
347 
349  }
350 
351  private:
352 
354 
360  bool
362  {
364  validation_state_t::frame_header_is_valid );
365 
366  if( !is_valid_opcode( frame.m_opcode ) )
367  {
369  validation_state_t::invalid_opcode );
370  }
371  else if( is_control_frame(frame.m_opcode) && !frame.m_final_flag )
372  {
374  validation_state_t::non_final_control_frame );
375  }
376  else if( !frame.m_mask_flag )
377  {
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  {
386  validation_state_t::non_zero_rsv_flags );
387  }
388  else if( is_control_frame(frame.m_opcode) && frame.payload_len() >
390  {
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 &&
414  {
415  if( !m_utf8_checker.process_byte( byte ) )
416  {
419  }
420  }
421  else if( m_current_frame.m_opcode == opcode_t::connection_close_frame )
422  {
424  {
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 ) )
440  }
441  }
442 
443  return byte;
444  }
445 
447 
459  bool
461  {
463  opcode == opcode_t::continuation_frame )
464  {
466  validation_state_t::continuation_frame_without_data_frame );
467 
468  return false;
469  }
470  else if( m_previous_data_frame !=
472  is_data_frame( opcode ) )
473  {
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  {
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  {
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 
537  validation_state_t::initial_state };
538 
540  enum class working_state_t
541  {
543  empty_state,
546  };
547 
549 
554  {
555  none,
556  text,
557  binary
558  };
559 
562 
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 */
restinio::exception_t
Exception class for all exceptions thrown by RESTinio.
Definition: exception.hpp:26
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::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::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::unmasker_t::unmasker_t
unmasker_t()=default
restinio::websocket::basic::impl::expected_data_t::m_loaded_data
raw_data_t m_loaded_data
Buffer for accumulating data.
Definition: ws_parser.hpp:179
utf8.hpp
restinio::websocket::basic::impl::message_details_t
Websocket message class with more detailed protocol information.
Definition: ws_parser.hpp:63
restinio::websocket::basic::is_valid_opcode
bool is_valid_opcode(opcode_t opcode)
Definition: message.hpp:64
restinio::websocket::basic::impl::ws_protocol_validator_t::previous_data_frame_t::binary
@ binary
restinio::websocket::basic::impl::ws_protocol_validator_t::previous_data_frame_t::text
@ text
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
ws_parser.hpp
restinio::websocket::basic::opcode_t
opcode_t
Definition: message.hpp:37
restinio::websocket::basic::impl::message_details_t::m_rsv2_flag
bool m_rsv2_flag
Definition: ws_parser.hpp:112
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::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
Definition: ws_protocol_validator.hpp:120
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::utils::utf8_checker_t::process_byte
RESTINIO_NODISCARD bool process_byte(std::uint8_t byte) noexcept
Definition: utf8_checker.hpp:41
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::utils::utf8_checker_t::finalized
RESTINIO_NODISCARD bool finalized() const noexcept
Definition: utf8_checker.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::m_processed_bytes_count
size_t m_processed_bytes_count
Processed bytes counter.
Definition: ws_protocol_validator.hpp:174
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::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::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::expected_data_t::reset
void reset(size_t expected_size)
Reset internal state on next expected data size.
Definition: ws_parser.hpp:206
restinio::websocket::basic::impl::ws_protocol_validator_t::reset
void reset()
Reset to initial state.
Definition: ws_protocol_validator.hpp:341
restinio::websocket::basic::impl::expected_data_t::all_bytes_loaded
bool all_bytes_loaded() const
Check all bytes are loaded.
Definition: ws_parser.hpp:183
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::message_details_t::m_masking_key
std::uint32_t m_masking_key
Masking key.
Definition: ws_parser.hpp:132
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
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::m_unmask_flag
bool m_unmask_flag
This flag set if it's need to unmask payload parts.
Definition: ws_protocol_validator.hpp:577
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::utils::utf8_checker_t::reset
void reset() noexcept
Definition: utf8_checker.hpp:126
restinio::websocket::basic::impl::ws_protocol_validator_t::previous_data_frame_t::none
@ none
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_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::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::working_state_t::processing_frame
@ processing_frame
Frame is processing now.
restinio::utils::sha1::details::byte
unsigned int byte(digest_t::value_type v)
Definition: sha1.hpp:365
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::m_validation_state
validation_state_t m_validation_state
Current validation state.
Definition: ws_protocol_validator.hpp:536
restinio
Definition: asio_include.hpp:21
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::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::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::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::ws_protocol_validator_t::m_working_state
working_state_t m_working_state
Working state.
Definition: ws_protocol_validator.hpp:561
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
exception.hpp
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::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::ws_protocol_validator_t
ws_protocol_validator_t(bool do_unmask)
Definition: ws_protocol_validator.hpp:200
restinio::websocket::basic::impl::message_details_t::m_opcode
opcode_t m_opcode
Opcode.
Definition: ws_parser.hpp:117
restinio::websocket::basic::impl::validation_state_t
validation_state_t
States of validated frame.
Definition: ws_protocol_validator.hpp:29
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::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::working_state_t::empty_state
@ empty_state
Waiting for new frame.
restinio::websocket::basic::impl::validation_state_t::initial_state
@ initial_state
restinio::websocket::basic::impl::message_details_t::m_rsv3_flag
bool m_rsv3_flag
Definition: ws_parser.hpp:113
restinio::websocket::basic::impl::expected_data_t
Data with expected size.
Definition: ws_parser.hpp:166
restinio::websocket::basic::impl::expected_data_t::add_byte_and_check_size
bool add_byte_and_check_size(byte_t byte)
Try to add one more byte to loaded data and check loaded data size.
Definition: ws_parser.hpp:194
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::ws_protocol_validator_t::m_previous_data_frame
previous_data_frame_t m_previous_data_frame
Previous unfinished data frame.
Definition: ws_protocol_validator.hpp:564