]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // |
92f5a8d4 | 2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) |
7c673cae FG |
3 | // |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
b32b8144 FG |
7 | // Official repository: https://github.com/boostorg/beast |
8 | // | |
7c673cae | 9 | |
b32b8144 FG |
10 | #ifndef BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP |
11 | #define BOOST_BEAST_WEBSOCKET_DETAIL_FRAME_HPP | |
7c673cae | 12 | |
92f5a8d4 | 13 | #include <boost/beast/core/buffer_traits.hpp> |
11fdf7f2 | 14 | #include <boost/beast/websocket/error.hpp> |
b32b8144 FG |
15 | #include <boost/beast/websocket/rfc6455.hpp> |
16 | #include <boost/beast/websocket/detail/utf8_checker.hpp> | |
b32b8144 | 17 | #include <boost/beast/core/flat_static_buffer.hpp> |
7c673cae FG |
18 | #include <boost/asio/buffer.hpp> |
19 | #include <boost/assert.hpp> | |
92f5a8d4 | 20 | #include <boost/endian/conversion.hpp> |
7c673cae FG |
21 | #include <cstdint> |
22 | ||
b32b8144 | 23 | namespace boost { |
7c673cae FG |
24 | namespace beast { |
25 | namespace websocket { | |
26 | namespace detail { | |
27 | ||
11fdf7f2 | 28 | // frame header opcodes |
b32b8144 FG |
29 | enum class opcode : std::uint8_t |
30 | { | |
31 | cont = 0, | |
32 | text = 1, | |
33 | binary = 2, | |
34 | rsv3 = 3, | |
35 | rsv4 = 4, | |
36 | rsv5 = 5, | |
37 | rsv6 = 6, | |
38 | rsv7 = 7, | |
39 | close = 8, | |
40 | ping = 9, | |
41 | pong = 10, | |
42 | crsvb = 11, | |
43 | crsvc = 12, | |
44 | crsvd = 13, | |
45 | crsve = 14, | |
46 | crsvf = 15 | |
47 | }; | |
48 | ||
7c673cae FG |
49 | // Contents of a WebSocket frame header |
50 | struct frame_header | |
51 | { | |
7c673cae FG |
52 | std::uint64_t len; |
53 | std::uint32_t key; | |
b32b8144 FG |
54 | opcode op; |
55 | bool fin : 1; | |
56 | bool mask : 1; | |
57 | bool rsv1 : 1; | |
58 | bool rsv2 : 1; | |
59 | bool rsv3 : 1; | |
7c673cae FG |
60 | }; |
61 | ||
62 | // holds the largest possible frame header | |
11fdf7f2 | 63 | using fh_buffer = flat_static_buffer<14>; |
7c673cae FG |
64 | |
65 | // holds the largest possible control frame | |
b32b8144 FG |
66 | using frame_buffer = |
67 | flat_static_buffer< 2 + 8 + 4 + 125 >; | |
7c673cae FG |
68 | |
69 | inline | |
70 | bool constexpr | |
71 | is_reserved(opcode op) | |
72 | { | |
73 | return | |
74 | (op >= opcode::rsv3 && op <= opcode::rsv7) || | |
75 | (op >= opcode::crsvb && op <= opcode::crsvf); | |
76 | } | |
77 | ||
78 | inline | |
79 | bool constexpr | |
80 | is_valid(opcode op) | |
81 | { | |
82 | return op <= opcode::crsvf; | |
83 | } | |
84 | ||
85 | inline | |
86 | bool constexpr | |
87 | is_control(opcode op) | |
88 | { | |
89 | return op >= opcode::close; | |
90 | } | |
91 | ||
92 | inline | |
93 | bool | |
94 | is_valid_close_code(std::uint16_t v) | |
95 | { | |
96 | switch(v) | |
97 | { | |
98 | case close_code::normal: // 1000 | |
99 | case close_code::going_away: // 1001 | |
100 | case close_code::protocol_error: // 1002 | |
101 | case close_code::unknown_data: // 1003 | |
102 | case close_code::bad_payload: // 1007 | |
103 | case close_code::policy_error: // 1008 | |
104 | case close_code::too_big: // 1009 | |
105 | case close_code::needs_extension: // 1010 | |
106 | case close_code::internal_error: // 1011 | |
107 | case close_code::service_restart: // 1012 | |
108 | case close_code::try_again_later: // 1013 | |
109 | return true; | |
110 | ||
111 | // explicitly reserved | |
112 | case close_code::reserved1: // 1004 | |
113 | case close_code::no_status: // 1005 | |
114 | case close_code::abnormal: // 1006 | |
115 | case close_code::reserved2: // 1014 | |
116 | case close_code::reserved3: // 1015 | |
117 | return false; | |
118 | } | |
119 | // reserved | |
120 | if(v >= 1016 && v <= 2999) | |
121 | return false; | |
122 | // not used | |
123 | if(v <= 999) | |
124 | return false; | |
125 | return true; | |
126 | } | |
127 | ||
128 | //------------------------------------------------------------------------------ | |
129 | ||
130 | // Write frame header to dynamic buffer | |
131 | // | |
132 | template<class DynamicBuffer> | |
133 | void | |
134 | write(DynamicBuffer& db, frame_header const& fh) | |
135 | { | |
7c673cae FG |
136 | std::size_t n; |
137 | std::uint8_t b[14]; | |
138 | b[0] = (fh.fin ? 0x80 : 0x00) | static_cast<std::uint8_t>(fh.op); | |
139 | if(fh.rsv1) | |
140 | b[0] |= 0x40; | |
141 | if(fh.rsv2) | |
142 | b[0] |= 0x20; | |
143 | if(fh.rsv3) | |
144 | b[0] |= 0x10; | |
145 | b[1] = fh.mask ? 0x80 : 0x00; | |
146 | if(fh.len <= 125) | |
147 | { | |
148 | b[1] |= fh.len; | |
149 | n = 2; | |
150 | } | |
151 | else if(fh.len <= 65535) | |
152 | { | |
153 | b[1] |= 126; | |
92f5a8d4 TL |
154 | auto len_be = endian::native_to_big( |
155 | static_cast<std::uint16_t>(fh.len)); | |
156 | std::memcpy(&b[2], &len_be, sizeof(len_be)); | |
7c673cae FG |
157 | n = 4; |
158 | } | |
159 | else | |
160 | { | |
161 | b[1] |= 127; | |
92f5a8d4 TL |
162 | auto len_be = endian::native_to_big( |
163 | static_cast<std::uint64_t>(fh.len)); | |
164 | std::memcpy(&b[2], &len_be, sizeof(len_be)); | |
7c673cae FG |
165 | n = 10; |
166 | } | |
167 | if(fh.mask) | |
168 | { | |
92f5a8d4 TL |
169 | auto key_le = endian::native_to_little( |
170 | static_cast<std::uint32_t>(fh.key)); | |
171 | std::memcpy(&b[n], &key_le, sizeof(key_le)); | |
7c673cae FG |
172 | n += 4; |
173 | } | |
92f5a8d4 TL |
174 | db.commit(net::buffer_copy( |
175 | db.prepare(n), net::buffer(b))); | |
7c673cae FG |
176 | } |
177 | ||
178 | // Read data from buffers | |
179 | // This is for ping and pong payloads | |
180 | // | |
181 | template<class Buffers> | |
182 | void | |
b32b8144 | 183 | read_ping(ping_data& data, Buffers const& bs) |
7c673cae | 184 | { |
92f5a8d4 TL |
185 | BOOST_ASSERT(buffer_bytes(bs) <= data.max_size()); |
186 | data.resize(buffer_bytes(bs)); | |
187 | net::buffer_copy(net::mutable_buffer{ | |
7c673cae FG |
188 | data.data(), data.size()}, bs); |
189 | } | |
190 | ||
191 | // Read close_reason, return true on success | |
192 | // This is for the close payload | |
193 | // | |
194 | template<class Buffers> | |
195 | void | |
11fdf7f2 TL |
196 | read_close( |
197 | close_reason& cr, | |
198 | Buffers const& bs, | |
199 | error_code& ec) | |
7c673cae | 200 | { |
92f5a8d4 | 201 | auto const n = buffer_bytes(bs); |
7c673cae FG |
202 | BOOST_ASSERT(n <= 125); |
203 | if(n == 0) | |
204 | { | |
205 | cr = close_reason{}; | |
92f5a8d4 | 206 | ec = {}; |
7c673cae FG |
207 | return; |
208 | } | |
209 | if(n == 1) | |
210 | { | |
11fdf7f2 TL |
211 | // invalid payload size == 1 |
212 | ec = error::bad_close_size; | |
7c673cae FG |
213 | return; |
214 | } | |
92f5a8d4 TL |
215 | |
216 | std::uint16_t code_be; | |
217 | cr.reason.resize(n - 2); | |
218 | std::array<net::mutable_buffer, 2> out_bufs{{ | |
219 | net::mutable_buffer(&code_be, sizeof(code_be)), | |
220 | net::mutable_buffer(&cr.reason[0], n - 2)}}; | |
221 | ||
222 | net::buffer_copy(out_bufs, bs); | |
223 | ||
224 | cr.code = endian::big_to_native(code_be); | |
225 | if(! is_valid_close_code(cr.code)) | |
7c673cae | 226 | { |
92f5a8d4 TL |
227 | // invalid close code |
228 | ec = error::bad_close_code; | |
229 | return; | |
7c673cae | 230 | } |
92f5a8d4 TL |
231 | |
232 | if(n > 2 && !check_utf8( | |
233 | cr.reason.data(), cr.reason.size())) | |
7c673cae | 234 | { |
92f5a8d4 TL |
235 | // not valid utf-8 |
236 | ec = error::bad_close_payload; | |
237 | return; | |
7c673cae | 238 | } |
92f5a8d4 | 239 | ec = {}; |
7c673cae FG |
240 | } |
241 | ||
242 | } // detail | |
243 | } // websocket | |
244 | } // beast | |
b32b8144 | 245 | } // boost |
7c673cae FG |
246 | |
247 | #endif |