RESTinio
ws_protocol_validator.hpp
Go to the documentation of this file.
1 /*!
2  Protocol header validator .
3 */
4 
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 
27 //! States of validated frame.
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 
53 //! Helper function for logging validation states.
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 
81 //! Check frame is control frame.
82 /*!
83  \return true if frame is control frame.
84  \return false otherwise.
85 */
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 
98 //! Check frame is data frame.
99 /*!
100  \return true if frame is data frame.
101  \return false otherwise.
102 */
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 
114 /*!
115  This class is need to unmask byte sequences.
116 
117  Mask is 32 bit key.
118 */
120 {
121  unmasker_t() = default;
122 
123  unmasker_t( uint32_t masking_key )
124  : m_mask{
126  masking_key),
128  masking_key),
130  masking_key),
132  masking_key)} }
133  {
134  }
135 
136  //! Do unmask operation.
137  /*!
138  \return unmasked value.
139  */
140  uint8_t
142  {
143  return masked_byte ^ m_mask[ (m_processed_bytes_count++) % 4 ];
144  }
145 
146  //! Reset to initial state.
147  void
148  reset( uint32_t masking_key )
149  {
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 
165 
166  //! Bytes array with masking key.
168 
169  //! Processed bytes counter.
170  /*!
171  It needs for taking remainder after division on 4. Result of this operation
172  is index of value in mask array for next unmask operation.
173  */
175 };
176 
177 //
178 // ws_protocol_validator_t
179 //
180 
181 //! Class for websocket protocol validations.
182 /*!
183  This class checks:
184 
185  text frame and close frame are with valid uff-8 payload;
186  close frame has a valid close code;
187  continuation chunks of text frame have valid utf-8 text;
188  there is invalid situation when continuation frame came without any data frame.
189  continuation frame or control frame is wating after data frame with fin flag set in 0.
190  opcode has a valid code
191  control frame can't be fragmented.
192 
193 */
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 
205  //! Start work with new frame.
206  /*!
207  \attention methods finish_frame() or reset() should be called before
208  processing a new frame.
209  */
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;
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 
251  //! Validate next part of current frame.
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 
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 
270  break;
271  }
272 
273  return m_validation_state;
274  }
275 
276  //! Validate next part of current frame and reset source part to unmasked data.
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 
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 
295  break;
296  }
297 
298  return m_validation_state;
299  }
300 
301  //! Make final checks of payload if it is necessary and reset state.
304  {
307 
308  if( m_current_frame.m_final_flag )
309  {
310  if( !m_utf8_checker.final() )
313 
314  m_utf8_checker.reset();
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.
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  {
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.
335 
336  return this_frame_validation_state;
337  }
338 
339  //! Reset to initial state.
340  void
342  {
347 
348  m_utf8_checker.reset();
349  }
350 
351  private:
352 
353  //! Validate frame header.
354  /*!
355  \return true if current validation state is 'frame_header_is_valid' after
356  all validation operations.
357 
358  \return false otherwise.
359  */
360  bool
361  validate_frame_header( const message_details_t & frame )
362  {
365 
366  if( !is_valid_opcode( frame.m_opcode ) )
367  {
370  }
371  else if( is_control_frame(frame.m_opcode) && frame.m_final_flag == false )
372  {
375  }
376  else if( frame.m_mask_flag == false )
377  {
380  }
381  else if( frame.m_rsv1_flag != 0 ||
382  frame.m_rsv2_flag != 0 ||
383  frame.m_rsv3_flag != 0)
384  {
387  }
388  else if( is_control_frame(frame.m_opcode) && frame.payload_len() >
389  websocket_max_payload_size_without_ext )
390  {
393  }
394 
396  }
397 
398  //! Process payload byte.
399  /*!
400  Do all necessary validations with payload byte.
401 
402  \return unmasked byte if unmask flag is set.
403  \return copy of original byte if unmask flag isn't set.
404  */
405  std::uint8_t
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  {
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 ) )
440  }
441  }
442 
443  return byte;
444  }
445 
446  //! Check previous frame type.
447  /*!
448  Need for following cases:
449 
450  1) check current frame is not continuation frame withot
451  any data frame before.
452  2) check current frame is not new data frame with unfinished
453  other data frame before.
454 
455  \return true if previous and current frames are without any conflicts.
456 
457  \return false otherwise.
458  */
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  {
467 
468  return false;
469  }
470  else if( m_previous_data_frame !=
471  previous_data_frame_t::none &&
472  is_data_frame( opcode ) )
473  {
476 
477  return false;
478  }
479 
480  return true;
481  }
482 
483  //! Validate close code.
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  {
493 
494  return;
495  }
496 
497  if( close_code == 1004 ||
498  close_code == 1005 ||
499  close_code == 1006 )
500  {
503 
504  return;
505  }
506  }
507 
508  //! Check validation state is still valid.
509  bool
511  {
516  }
517 
518  //! Try to set validation state.
519  /*!
520  Set validation state with new value.
521  */
522  void
524  {
525  m_validation_state = state;
526  }
527 
528  //! Current validation state.
529  /*!
530  Normal case is sequence of states:
531  frame_header_is_valid -> payload_part_is_valid -> frame_is_valid.
532 
533  After set in invalid state validator will save this state until frame
534  will be finished or validator will be reset.
535  */
538 
539  //! Validator's orking states.
540  enum class working_state_t
541  {
542  //! Waiting for new frame.
543  empty_state,
544  //! Frame is processing now.
546  };
547 
548  //! Previous unfinished data frame type.
549  /*!
550  It needs for understanding what kind of data is present in
551  current continued frames.
552  */
554  {
555  none,
556  text,
557  binary
558  };
559 
560  //! Working state.
562 
563  //! Previous unfinished data frame.
566 
567  //! Current frame details.
569 
570  //! Buffer for accumulating 2 bytes of close code.
572 
573  //! UTF-8 checker for text frames and close frames.
575 
576  //! This flag set if it's need to unmask payload parts.
577  bool m_unmask_flag{ false };
578 
579  //! Unmask payload coming from client side.
581 };
582 
583 } /* namespace impl */
584 
585 } /* namespace basic */
586 
587 } /* namespace websocket */
588 
589 } /* namespace restinio */
void set_validation_state(validation_state_t state)
Try to set validation state.
unmasker_t m_unmasker
Unmask payload coming from client side.
void validate_close_code(uint16_t close_code)
Validate close code.
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.
uint8_t unmask_byte(uint8_t masked_byte)
Do unmask operation.
bool is_data_frame(opcode_t opcode)
Check frame is data frame.
const char * validation_state_str(validation_state_t state)
Helper function for logging validation states.
bool validate_frame_header(const message_details_t &frame)
Validate frame header.
size_t m_processed_bytes_count
Processed bytes counter.
bool is_state_still_valid() const
Check validation state is still valid.
utf8_checker_t m_utf8_checker
UTF-8 checker for text frames and close frames.
validation_state_t process_new_frame(const message_details_t &frame)
Start work with new frame.
std::uint8_t process_payload_byte(std::uint8_t byte)
Process payload byte.
bool m_unmask_flag
This flag set if it&#39;s need to unmask payload parts.
validation_state_t
States of validated frame.
void reset(uint32_t masking_key)
Reset to initial state.
expected_data_t m_expected_close_code
Buffer for accumulating 2 bytes of close code.
validation_state_t process_next_payload_part(const char *data, size_t size)
Validate next part of current frame.
bool is_control_frame(opcode_t opcode)
Check frame is control frame.
validation_state_t finish_frame()
Make final checks of payload if it is necessary and reset state.
previous_data_frame_t m_previous_data_frame
Previous unfinished data frame.
validation_state_t m_validation_state
Current validation state.
bool check_previous_frame_type(opcode_t opcode)
Check previous frame type.
std::enable_if< std::is_same< Parameter_Container, query_string_params_t >::value||std::is_same< Parameter_Container, router::route_params_t >::value, optional_t< Value_Type > >::type opt_value(const Parameter_Container &params, string_view_t key)
Gets the value of a parameter specified by key wrapped in optional_t<Value_Type> if parameter exists ...
Definition: value_or.hpp:64
mask_array_t m_mask
Bytes array with masking key.