websocketpp  0.3.0
C++/Boost Asio based websocket client/server library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
frame.hpp
1 /*
2  * Copyright (c) 2013, Peter Thorson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution.
11  * * Neither the name of the WebSocket++ Project nor the
12  * names of its contributors may be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #ifndef WEBSOCKETPP_FRAME_HPP
29 #define WEBSOCKETPP_FRAME_HPP
30 
31 #include <algorithm>
32 
33 #include <websocketpp/common/system_error.hpp>
34 #include <websocketpp/common/network.hpp>
35 
36 #include <websocketpp/utilities.hpp>
37 
38 namespace websocketpp {
40 
44 namespace frame {
45 
47 static unsigned int const BASIC_HEADER_LENGTH = 2;
49 static unsigned int const MAX_HEADER_LENGTH = 14;
51 static unsigned int const MAX_EXTENDED_HEADER_LENGTH = 12;
52 
55  uint16_t i;
56  uint8_t c[2];
57 };
58 
61  uint32_t i;
62  uint8_t c[4];
63 };
64 
67  uint64_t i;
68  uint8_t c[8];
69 };
70 
72 
75 namespace opcode {
76  enum value {
77  continuation = 0x0,
78  text = 0x1,
79  binary = 0x2,
80  rsv3 = 0x3,
81  rsv4 = 0x4,
82  rsv5 = 0x5,
83  rsv6 = 0x6,
84  rsv7 = 0x7,
85  close = 0x8,
86  ping = 0x9,
87  pong = 0xA,
88  control_rsvb = 0xB,
89  control_rsvc = 0xC,
90  control_rsvd = 0xD,
91  control_rsve = 0xE,
92  control_rsvf = 0xF,
93 
94  CONTINUATION = 0x0,
95  TEXT = 0x1,
96  BINARY = 0x2,
97  RSV3 = 0x3,
98  RSV4 = 0x4,
99  RSV5 = 0x5,
100  RSV6 = 0x6,
101  RSV7 = 0x7,
102  CLOSE = 0x8,
103  PING = 0x9,
104  PONG = 0xA,
105  CONTROL_RSVB = 0xB,
106  CONTROL_RSVC = 0xC,
107  CONTROL_RSVD = 0xD,
108  CONTROL_RSVE = 0xE,
109  CONTROL_RSVF = 0xF
110  };
111 
113 
117  inline bool reserved(value v) {
118  return (v >= rsv3 && v <= rsv7) ||
119  (v >= control_rsvb && v <= control_rsvf);
120  }
121 
123 
129  inline bool invalid(value v) {
130  return (v > 0xF || v < 0);
131  }
132 
134 
138  inline bool is_control(value v) {
139  return v >= 0x8;
140  }
141 }
142 
144 namespace limits {
146  static unsigned int const basic_header_length = 2;
147 
149  static unsigned int const max_header_length = 14;
150 
152  static unsigned int const max_extended_header_length = 12;
153 
155  static uint8_t const payload_size_basic = 125;
156 
158  static uint16_t const payload_size_extended = 0xFFFF; // 2^16, 65535
159 
161  static uint64_t const payload_size_jumbo = 0x7FFFFFFFFFFFFFFFLL;//2^63
162 
164 
168  static uint8_t const close_reason_size = 123;
169 }
170 
171 
172 // masks for fields in the basic header
173 static uint8_t const BHB0_OPCODE = 0x0F;
174 static uint8_t const BHB0_RSV3 = 0x10;
175 static uint8_t const BHB0_RSV2 = 0x20;
176 static uint8_t const BHB0_RSV1 = 0x40;
177 static uint8_t const BHB0_FIN = 0x80;
178 
179 static uint8_t const BHB1_PAYLOAD = 0x7F;
180 static uint8_t const BHB1_MASK = 0x80;
181 
182 static uint8_t const payload_size_code_16bit = 0x7E; // 126
183 static uint8_t const payload_size_code_64bit = 0x7F; // 127
184 
186 
188 struct basic_header {
189  basic_header() : b0(0x00),b1(0x00) {}
190 
191  basic_header(uint8_t p0, uint8_t p1) : b0(p0), b1(p1) {}
192 
193  basic_header(opcode::value op, uint64_t size, bool fin, bool mask,
194  bool rsv1 = false, bool rsv2 = false, bool rsv3 = false) : b0(0x00),
195  b1(0x00)
196  {
197  if (fin) {
198  b0 |= BHB0_FIN;
199  }
200  if (rsv1) {
201  b0 |= BHB0_RSV1;
202  }
203  if (rsv2) {
204  b0 |= BHB0_RSV2;
205  }
206  if (rsv3) {
207  b0 |= BHB0_RSV3;
208  }
209  b0 |= (op & BHB0_OPCODE);
210 
211  if (mask) {
212  b1 |= BHB1_MASK;
213  }
214 
215  uint8_t basic_value;
216 
217  if (size <= limits::payload_size_basic) {
218  basic_value = static_cast<uint8_t>(size);
219  } else if (size <= limits::payload_size_extended) {
220  basic_value = payload_size_code_16bit;
221  } else {
222  basic_value = payload_size_code_64bit;
223  }
224 
225 
226  b1 |= basic_value;
227  }
228 
229  uint8_t b0;
230  uint8_t b1;
231 };
232 
235  extended_header() {
236  std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
237  }
238 
239  extended_header(uint64_t payload_size) {
240  std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
241 
242  copy_payload(payload_size);
243  }
244 
245  extended_header(uint64_t payload_size, uint32_t masking_key) {
246  std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
247 
248  // Copy payload size
249  int offset = copy_payload(payload_size);
250 
251  // Copy Masking Key
252  uint32_converter temp32;
253  temp32.i = masking_key;
254  std::copy(temp32.c,temp32.c+4,bytes+offset);
255  }
256 
257  uint8_t bytes[MAX_EXTENDED_HEADER_LENGTH];
258 private:
259  int copy_payload(uint64_t payload_size) {
260  int payload_offset = 0;
261 
262  if (payload_size <= limits::payload_size_basic) {
263  payload_offset = 8;
264  } else if (payload_size <= limits::payload_size_extended) {
265  payload_offset = 6;
266  }
267 
268  uint64_converter temp64;
269  temp64.i = lib::net::htonll(payload_size);
270  std::copy(temp64.c+payload_offset,temp64.c+8,bytes);
271 
272  return 8-payload_offset;
273  }
274 };
275 
276 bool get_fin(basic_header const &h);
277 void set_fin(basic_header &h, bool value);
278 bool get_rsv1(basic_header const &h);
279 void set_rsv1(basic_header &h, bool value);
280 bool get_rsv2(basic_header const &h);
281 void set_rsv2(basic_header &h, bool value);
282 bool get_rsv3(basic_header const &h);
283 void set_rsv3(basic_header &h, bool value);
284 opcode::value get_opcode(basic_header const &h);
285 bool get_masked(basic_header const &h);
286 void set_masked(basic_header &h, bool value);
287 uint8_t get_basic_size(basic_header const &);
288 size_t get_header_len(basic_header const &);
289 unsigned int get_masking_key_offset(basic_header const &);
290 
291 std::string write_header(basic_header const &, extended_header const &);
292 masking_key_type get_masking_key(basic_header const &, extended_header const &);
293 uint16_t get_extended_size(extended_header const &);
294 uint64_t get_jumbo_size(extended_header const &);
295 uint64_t get_payload_size(basic_header const &, extended_header const &);
296 
297 size_t prepare_masking_key(masking_key_type const & key);
298 size_t circshift_prepared_key(size_t prepared_key, size_t offset);
299 
300 // Functions for performing xor based masking and unmasking
301 template <typename input_iter, typename output_iter>
302 void byte_mask(input_iter b, input_iter e, output_iter o, masking_key_type
303  const & key, size_t key_offset = 0);
304 template <typename iter_type>
305 void byte_mask(iter_type b, iter_type e, masking_key_type const & key,
306  size_t key_offset = 0);
307 void word_mask_exact(uint8_t * input, uint8_t * output, size_t length,
308  masking_key_type const & key);
309 void word_mask_exact(uint8_t * data, size_t length, masking_key_type const &
310  key);
311 size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length,
312  size_t prepared_key);
313 size_t word_mask_circ(uint8_t * data, size_t length, size_t prepared_key);
314 
316 
320 inline bool get_fin(basic_header const & h) {
321  return ((h.b0 & BHB0_FIN) == BHB0_FIN);
322 }
323 
325 
329 inline void set_fin(basic_header & h, bool value) {
330  h.b0 = (value ? h.b0 | BHB0_FIN : h.b0 & ~BHB0_FIN);
331 }
332 
334 
338 inline bool get_rsv1(const basic_header &h) {
339  return ((h.b0 & BHB0_RSV1) == BHB0_RSV1);
340 }
341 
343 
347 inline void set_rsv1(basic_header &h, bool value) {
348  h.b0 = (value ? h.b0 | BHB0_RSV1 : h.b0 & ~BHB0_RSV1);
349 }
350 
352 
356 inline bool get_rsv2(const basic_header &h) {
357  return ((h.b0 & BHB0_RSV2) == BHB0_RSV2);
358 }
359 
361 
365 inline void set_rsv2(basic_header &h, bool value) {
366  h.b0 = (value ? h.b0 | BHB0_RSV2 : h.b0 & ~BHB0_RSV2);
367 }
368 
370 
374 inline bool get_rsv3(const basic_header &h) {
375  return ((h.b0 & BHB0_RSV3) == BHB0_RSV3);
376 }
377 
379 
383 inline void set_rsv3(basic_header &h, bool value) {
384  h.b0 = (value ? h.b0 | BHB0_RSV3 : h.b0 & ~BHB0_RSV3);
385 }
386 
388 
392 inline opcode::value get_opcode(const basic_header &h) {
393  return opcode::value(h.b0 & BHB0_OPCODE);
394 }
395 
397 
401 inline bool get_masked(basic_header const & h) {
402  return ((h.b1 & BHB1_MASK) == BHB1_MASK);
403 }
404 
406 
410 inline void set_masked(basic_header & h, bool value) {
411  h.b1 = (value ? h.b1 | BHB1_MASK : h.b1 & ~BHB1_MASK);
412 }
413 
415 
430 inline uint8_t get_basic_size(const basic_header &h) {
431  return h.b1 & BHB1_PAYLOAD;
432 }
433 
435 
444 inline size_t get_header_len(basic_header const & h) {
445  // TODO: check extensions?
446 
447  // masking key offset represents the space used for the extended length
448  // fields
449  size_t size = BASIC_HEADER_LENGTH + get_masking_key_offset(h);
450 
451  // If the header is masked there is a 4 byte masking key
452  if (get_masked(h)) {
453  size += 4;
454  }
455 
456  return size;
457 }
458 
460 
466 inline lib::error_code set_size(basic_header & h, extended_header & eh, uint64_t
467  size)
468 {
469  // make sure value isn't too big
470  uint8_t basic_value;
471 
472  if (size <= limits::payload_size_basic) {
473  basic_value = static_cast<uint8_t>(size);
474  } else if (size <= limits::payload_size_extended) {
475  basic_value = payload_size_code_16bit;
476  } else if (size <= limits::payload_size_jumbo) {
477  basic_value = payload_size_code_64bit;
478  } else {
479  // error
480  return lib::error_code();
481  }
482 
483  h.b1 = (basic_value & BHB1_PAYLOAD) | (h.b1 & BHB1_MASK);
484 
485  return lib::error_code();
486 }
487 
489 
497 inline unsigned int get_masking_key_offset(const basic_header &h) {
498  if (get_basic_size(h) == payload_size_code_16bit) {
499  return 2;
500  } else if (get_basic_size(h) == payload_size_code_64bit) {
501  return 8;
502  } else {
503  return 0;
504  }
505 }
506 
508 
517 inline std::string prepare_header(const basic_header &h, const
518  extended_header &e)
519 {
520  std::string ret;
521 
522  ret.push_back(char(h.b0));
523  ret.push_back(char(h.b1));
524  ret.append(
525  reinterpret_cast<const char*>(e.bytes),
526  get_header_len(h)-BASIC_HEADER_LENGTH
527  );
528 
529  return ret;
530 }
531 
533 
544 inline masking_key_type get_masking_key(const basic_header &h, const
545  extended_header &e)
546 {
547  masking_key_type temp32;
548 
549  if (!get_masked(h)) {
550  temp32.i = 0;
551  } else {
552  unsigned int offset = get_masking_key_offset(h);
553  std::copy(e.bytes+offset,e.bytes+offset+4,temp32.c);
554  }
555 
556  return temp32;
557 }
558 
560 
568 inline uint16_t get_extended_size(const extended_header &e) {
569  uint16_converter temp16;
570  std::copy(e.bytes,e.bytes+2,temp16.c);
571  return ntohs(temp16.i);
572 }
573 
575 
583 inline uint64_t get_jumbo_size(const extended_header &e) {
584  uint64_converter temp64;
585  std::copy(e.bytes,e.bytes+8,temp64.c);
586  return lib::net::ntohll(temp64.i);
587 }
588 
590 
601 inline uint64_t get_payload_size(const basic_header &h, const
602  extended_header &e)
603 {
604  uint8_t val = get_basic_size(h);
605 
606  if (val <= limits::payload_size_basic) {
607  return val;
608  } else if (val == payload_size_code_16bit) {
609  return get_extended_size(e);
610  } else {
611  return get_jumbo_size(e);
612  }
613 }
614 
616 
623 inline size_t prepare_masking_key(const masking_key_type& key) {
624  size_t low_bits = static_cast<size_t>(key.i);
625 
626  if (sizeof(size_t) == 8) {
627  uint64_t high_bits = static_cast<size_t>(key.i);
628  return static_cast<size_t>((high_bits << 32) | low_bits);
629  } else {
630  return low_bits;
631  }
632 }
633 
635 
640 inline size_t circshift_prepared_key(size_t prepared_key, size_t offset) {
641  if (lib::net::is_little_endian()) {
642  size_t temp = prepared_key << (sizeof(size_t)-offset)*8;
643  return (prepared_key >> offset*8) | temp;
644  } else {
645  size_t temp = prepared_key >> (sizeof(size_t)-offset)*8;
646  return (prepared_key << offset*8) | temp;
647  }
648 }
649 
651 
669 template <typename input_iter, typename output_iter>
670 void byte_mask(input_iter first, input_iter last, output_iter result,
671  masking_key_type const & key, size_t key_offset)
672 {
673  size_t key_index = key_offset%4;
674  while (first != last) {
675  *result = *first ^ key.c[key_index++];
676  key_index %= 4;
677  ++result;
678  ++first;
679  }
680 }
681 
683 
699 template <typename iter_type>
700 void byte_mask(iter_type b, iter_type e, masking_key_type const & key,
701  size_t key_offset)
702 {
703  byte_mask(b,e,b,key,key_offset);
704 }
705 
707 
727 inline void word_mask_exact(uint8_t* input, uint8_t* output, size_t length,
728  const masking_key_type& key)
729 {
730  size_t prepared_key = prepare_masking_key(key);
731  size_t n = length/sizeof(size_t);
732  size_t* input_word = reinterpret_cast<size_t*>(input);
733  size_t* output_word = reinterpret_cast<size_t*>(output);
734 
735  for (size_t i = 0; i < n; i++) {
736  output_word[i] = input_word[i] ^ prepared_key;
737  }
738 
739  for (size_t i = n*sizeof(size_t); i < length; i++) {
740  output[i] = input[i] ^ key.c[i%4];
741  }
742 }
743 
745 
756 inline void word_mask_exact(uint8_t* data, size_t length, const
757  masking_key_type& key)
758 {
759  word_mask_exact(data,data,length,key);
760 }
761 
763 
793 inline size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length,
794  size_t prepared_key)
795 {
796  size_t n = length / sizeof(size_t); // whole words
797  size_t l = length - (n * sizeof(size_t)); // remaining bytes
798  size_t * input_word = reinterpret_cast<size_t *>(input);
799  size_t * output_word = reinterpret_cast<size_t *>(output);
800 
801  // mask word by word
802  for (size_t i = 0; i < n; i++) {
803  output_word[i] = input_word[i] ^ prepared_key;
804  }
805 
806  // mask partial word at the end
807  size_t start = length - l;
808  uint8_t * byte_key = reinterpret_cast<uint8_t *>(&prepared_key);
809  for (size_t i = 0; i < l; ++i) {
810  output[start+i] = input[start+i] ^ byte_key[i];
811  }
812 
813  return circshift_prepared_key(prepared_key,l);
814 }
815 
817 
830 inline size_t word_mask_circ(uint8_t* data, size_t length, size_t prepared_key){
831  return word_mask_circ(data,data,length,prepared_key);
832 }
833 
835 
855 inline size_t byte_mask_circ(uint8_t * input, uint8_t * output, size_t length,
856  size_t prepared_key)
857 {
858  uint32_converter key;
859  key.i = prepared_key;
860 
861  for (size_t i = 0; i < length; ++i) {
862  output[i] = input[i] ^ key.c[i % 4];
863  }
864 
865  return circshift_prepared_key(prepared_key,length % 4);
866 }
867 
869 
882 inline size_t byte_mask_circ(uint8_t* data, size_t length, size_t prepared_key){
883  return byte_mask_circ(data,data,length,prepared_key);
884 }
885 
886 } // namespace frame
887 } // namespace websocketpp
888 
889 #endif //WEBSOCKETPP_FRAME_HPP
bool is_control(value v)
Check if an opcode is for a control frame.
Definition: frame.hpp:138
bool invalid(value v)
Check if an opcode is invalid.
Definition: frame.hpp:129
bool get_rsv1(basic_header const &h)
check whether the frame's RSV1 bit is set
Definition: frame.hpp:338
uint16_t value
The type of a close code value.
Definition: close.hpp:49
void set_rsv1(basic_header &h, bool value)
Set the frame's RSV1 bit.
Definition: frame.hpp:347
bool get_rsv3(basic_header const &h)
check whether the frame's RSV3 bit is set
Definition: frame.hpp:374
bool get_masked(basic_header const &h)
check whether the frame is masked
Definition: frame.hpp:401
uint16_t get_extended_size(extended_header const &)
Extract the extended size field from an extended header.
Definition: frame.hpp:568
bool get_rsv2(basic_header const &h)
check whether the frame's RSV2 bit is set
Definition: frame.hpp:356
static unsigned int const max_header_length
Maximum length of a WebSocket header.
Definition: frame.hpp:149
opcode::value get_opcode(basic_header const &h)
Extract opcode from basic header.
Definition: frame.hpp:392
static uint8_t const payload_size_basic
Maximum size of a basic WebSocket payload.
Definition: frame.hpp:155
masking_key_type get_masking_key(basic_header const &, extended_header const &)
Extract the masking key from a frame header.
Definition: frame.hpp:544
Eight byte conversion union.
Definition: frame.hpp:66
Two byte conversion union.
Definition: frame.hpp:54
Four byte conversion union.
Definition: frame.hpp:60
lib::error_code set_size(basic_header &h, extended_header &eh, uint64_t size)
Set the frame's size.
Definition: frame.hpp:466
void byte_mask(input_iter b, input_iter e, output_iter o, masking_key_type const &key, size_t key_offset=0)
Byte by byte mask/unmask.
Definition: frame.hpp:670
static uint64_t const payload_size_jumbo
Maximum size of a jumbo WebSocket payload (basic payload = 127)
Definition: frame.hpp:161
size_t get_header_len(basic_header const &)
Calculates the full length of the header based on the first bytes.
Definition: frame.hpp:444
size_t circshift_prepared_key(size_t prepared_key, size_t offset)
circularly shifts the supplied prepared masking key by offset bytes
Definition: frame.hpp:640
static unsigned int const basic_header_length
Minimum length of a WebSocket frame header.
Definition: frame.hpp:146
size_t word_mask_circ(uint8_t *input, uint8_t *output, size_t length, size_t prepared_key)
Circular word aligned mask/unmask.
Definition: frame.hpp:793
The constant size component of a WebSocket frame header.
Definition: frame.hpp:188
void word_mask_exact(uint8_t *input, uint8_t *output, size_t length, masking_key_type const &key)
Exact word aligned mask/unmask.
Definition: frame.hpp:727
size_t prepare_masking_key(masking_key_type const &key)
Extract a masking key into a value the size of a machine word.
Definition: frame.hpp:623
uint8_t get_basic_size(basic_header const &)
Extracts the raw payload length specified in the basic header.
Definition: frame.hpp:430
uint64_t get_jumbo_size(extended_header const &)
Extract the jumbo size field from an extended header.
Definition: frame.hpp:583
Namespace for the WebSocket++ project.
Definition: base64.hpp:41
uint64_t get_payload_size(basic_header const &, extended_header const &)
Extract the full payload size field from a WebSocket header.
Definition: frame.hpp:601
static unsigned int const MAX_EXTENDED_HEADER_LENGTH
Maximum length of the variable portion of the WebSocket header.
Definition: frame.hpp:51
unsigned int get_masking_key_offset(basic_header const &)
Calculate the offset location of the masking key within the extended header.
Definition: frame.hpp:497
The variable size component of a WebSocket frame header.
Definition: frame.hpp:234
static unsigned int const max_extended_header_length
Maximum length of the variable portion of the WebSocket header.
Definition: frame.hpp:152
static unsigned int const MAX_HEADER_LENGTH
Maximum length of a WebSocket header.
Definition: frame.hpp:49
void set_masked(basic_header &h, bool value)
Set the frame's MASK bit.
Definition: frame.hpp:410
std::string prepare_header(const basic_header &h, const extended_header &e)
Generate a properly sized contiguous string that encodes a full frame header.
Definition: frame.hpp:517
static uint16_t const payload_size_extended
Maximum size of an extended WebSocket payload (basic payload = 126)
Definition: frame.hpp:158
size_t byte_mask_circ(uint8_t *input, uint8_t *output, size_t length, size_t prepared_key)
Circular byte aligned mask/unmask.
Definition: frame.hpp:855
static uint8_t const close_reason_size
Maximum size of close frame reason.
Definition: frame.hpp:168
void set_fin(basic_header &h, bool value)
Set the frame's FIN bit.
Definition: frame.hpp:329
bool reserved(value v)
Check if an opcode is reserved.
Definition: frame.hpp:117
bool get_fin(basic_header const &h)
Check whether the frame's FIN bit is set.
Definition: frame.hpp:320
void set_rsv2(basic_header &h, bool value)
Set the frame's RSV2 bit.
Definition: frame.hpp:365
static unsigned int const BASIC_HEADER_LENGTH
Minimum length of a WebSocket frame header.
Definition: frame.hpp:47
void set_rsv3(basic_header &h, bool value)
Set the frame's RSV3 bit.
Definition: frame.hpp:383