blob: 747a137b8728e2768de9c70bda1136f7c3f70888 [file] [log] [blame]
Ed Tanous7d3dba42017-04-05 13:04:39 -07001#include <crow/app.h>
Ed Tanousb4d29f42017-03-24 16:39:25 -07002
Ed Tanous93f987d2017-04-17 17:52:36 -07003#include <ast_jpeg_decoder.hpp>
4#include <ast_video_puller.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07005#include <boost/endian/arithmetic.hpp>
6#include <string>
Ed Tanousb4d29f42017-03-24 16:39:25 -07007
Ed Tanous1abe55e2018-09-05 08:30:59 -07008namespace crow
9{
10namespace kvm
11{
Ed Tanousb4d29f42017-03-24 16:39:25 -070012
Ed Tanous55c7b7a2018-05-22 15:27:24 -070013static const std::string rfb33VersionString = "RFB 003.003\n";
14static const std::string rfb37VersionString = "RFB 003.007\n";
15static const std::string rfb38VersionString = "RFB 003.008\n";
Ed Tanousc81ca422017-03-21 16:18:49 -070016
Ed Tanous1abe55e2018-09-05 08:30:59 -070017enum class RfbAuthScheme : uint8_t
18{
19 connection_failed = 0,
20 no_authentication = 1,
21 vnc_authentication = 2
Ed Tanousc81ca422017-03-21 16:18:49 -070022};
23
Ed Tanous1abe55e2018-09-05 08:30:59 -070024struct PixelFormatStruct
25{
26 boost::endian::big_uint8_t bitsPerPixel;
27 boost::endian::big_uint8_t depth;
28 boost::endian::big_uint8_t isBigEndian;
29 boost::endian::big_uint8_t isTrueColor;
30 boost::endian::big_uint16_t redMax;
31 boost::endian::big_uint16_t greenMax;
32 boost::endian::big_uint16_t blueMax;
33 boost::endian::big_uint8_t redShift;
34 boost::endian::big_uint8_t greenShift;
35 boost::endian::big_uint8_t blueShift;
36 boost::endian::big_uint8_t pad1;
37 boost::endian::big_uint8_t pad2;
38 boost::endian::big_uint8_t pad3;
Ed Tanousc81ca422017-03-21 16:18:49 -070039};
40
Ed Tanous1abe55e2018-09-05 08:30:59 -070041struct ServerInitializationMsg
42{
43 boost::endian::big_uint16_t framebufferWidth;
44 boost::endian::big_uint16_t framebufferHeight;
45 PixelFormatStruct pixelFormat;
46 boost::endian::big_uint32_t nameLength;
Ed Tanousc81ca422017-03-21 16:18:49 -070047};
48
Ed Tanous1abe55e2018-09-05 08:30:59 -070049enum class client_to_server_msg_type : uint8_t
50{
51 set_pixel_format = 0,
52 fix_color_map_entries = 1,
53 set_encodings = 2,
54 framebuffer_update_request = 3,
55 key_event = 4,
56 pointer_event = 5,
57 client_cut_text = 6
Ed Tanousc81ca422017-03-21 16:18:49 -070058};
59
Ed Tanous1abe55e2018-09-05 08:30:59 -070060enum class server_to_client_message_type : uint8_t
61{
62 framebuffer_update = 0,
63 set_color_map_entries = 1,
64 bell_message = 2,
65 server_cut_text = 3
Ed Tanous9140a672017-04-24 17:01:32 -070066};
67
Ed Tanous1abe55e2018-09-05 08:30:59 -070068struct SetPixelFormatMsg
69{
70 boost::endian::big_uint8_t pad1;
71 boost::endian::big_uint8_t pad2;
72 boost::endian::big_uint8_t pad3;
73 PixelFormatStruct pixelFormat;
Ed Tanousc81ca422017-03-21 16:18:49 -070074};
75
Ed Tanous1abe55e2018-09-05 08:30:59 -070076struct FrameBufferUpdateReq
77{
78 boost::endian::big_uint8_t incremental;
79 boost::endian::big_uint16_t xPosition;
80 boost::endian::big_uint16_t yPosition;
81 boost::endian::big_uint16_t width;
82 boost::endian::big_uint16_t height;
Ed Tanousc81ca422017-03-21 16:18:49 -070083};
84
Ed Tanous1abe55e2018-09-05 08:30:59 -070085struct KeyEventMsg
86{
87 boost::endian::big_uint8_t downFlag;
88 boost::endian::big_uint8_t pad1;
89 boost::endian::big_uint8_t pad2;
90 boost::endian::big_uint32_t key;
Ed Tanousc81ca422017-03-21 16:18:49 -070091};
92
Ed Tanous1abe55e2018-09-05 08:30:59 -070093struct PointerEventMsg
94{
95 boost::endian::big_uint8_t buttonMask;
96 boost::endian::big_uint16_t xPosition;
97 boost::endian::big_uint16_t yPosition;
Ed Tanousc81ca422017-03-21 16:18:49 -070098};
99
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100struct ClientCutTextMsg
101{
102 std::vector<uint8_t> data;
Ed Tanousc81ca422017-03-21 16:18:49 -0700103};
104
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105enum class encoding_type : uint32_t
106{
107 raw = 0x00,
108 copy_rectangle = 0x01,
109 rising_rectangle = 0x02,
110 corre = 0x04,
111 hextile = 0x05,
112 zlib = 0x06,
113 tight = 0x07,
114 zlibhex = 0x08,
115 ultra = 0x09,
116 zrle = 0x10,
117 zywrle = 0x011,
118 cache_enable = 0xFFFF0001,
119 xor_enable = 0xFFFF0006,
120 server_state_ultranvc = 0xFFFF8000,
121 enable_keepAlive = 0xFFFF8001,
122 enableftp_protocol_version = 0xFFFF8002,
123 tight_compress_level_0 = 0xFFFFFF00,
124 tight_compress_level_9 = 0xFFFFFF09,
125 x_cursor = 0xFFFFFF10,
126 rich_cursor = 0xFFFFFF11,
127 pointer_pos = 0xFFFFFF18,
128 last_rect = 0xFFFFFF20,
129 new_framebuffer_size = 0xFFFFFF21,
130 tight_quality_level_0 = 0xFFFFFFE0,
131 tight_quality_level_9 = 0xFFFFFFE9
Ed Tanousc81ca422017-03-21 16:18:49 -0700132};
133
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134struct FramebufferRectangle
135{
136 boost::endian::big_uint16_t x{};
137 boost::endian::big_uint16_t y{};
138 boost::endian::big_uint16_t width{};
139 boost::endian::big_uint16_t height{};
140 boost::endian::big_uint32_t encoding{};
141 std::vector<uint8_t> data;
Ed Tanousc81ca422017-03-21 16:18:49 -0700142};
143
Ed Tanous1abe55e2018-09-05 08:30:59 -0700144struct FramebufferUpdateMsg
145{
146 boost::endian::big_uint8_t messageType{};
147 std::vector<FramebufferRectangle> rectangles;
Ed Tanousc81ca422017-03-21 16:18:49 -0700148};
149
Ed Tanous1abe55e2018-09-05 08:30:59 -0700150inline std::string serialize(const FramebufferUpdateMsg& msg)
151{
152 // calculate the size of the needed vector for serialization
153 size_t vectorSize = 4;
154 for (const auto& rect : msg.rectangles)
155 {
156 vectorSize += 12 + rect.data.size();
157 }
Ed Tanousc81ca422017-03-21 16:18:49 -0700158
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 std::string serialized(vectorSize, 0);
Ed Tanousc81ca422017-03-21 16:18:49 -0700160
Ed Tanous1abe55e2018-09-05 08:30:59 -0700161 size_t i = 0;
162 serialized[i++] = static_cast<char>(
163 server_to_client_message_type::framebuffer_update); // Type
164 serialized[i++] = 0; // Pad byte
165 boost::endian::big_uint16_t numberOfRectangles = msg.rectangles.size();
166 std::memcpy(&serialized[i], &numberOfRectangles,
167 sizeof(numberOfRectangles));
168 i += sizeof(numberOfRectangles);
Ed Tanousc81ca422017-03-21 16:18:49 -0700169
Ed Tanous1abe55e2018-09-05 08:30:59 -0700170 for (const auto& rect : msg.rectangles)
171 {
172 // copy the first part of the struct
173 size_t bufferSize =
174 sizeof(FramebufferRectangle) - sizeof(std::vector<uint8_t>);
175 std::memcpy(&serialized[i], &rect, bufferSize);
176 i += bufferSize;
Ed Tanousc81ca422017-03-21 16:18:49 -0700177
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 std::memcpy(&serialized[i], rect.data.data(), rect.data.size());
179 i += rect.data.size();
180 }
Ed Tanousc81ca422017-03-21 16:18:49 -0700181
Ed Tanous1abe55e2018-09-05 08:30:59 -0700182 return serialized;
Ed Tanousc81ca422017-03-21 16:18:49 -0700183}
184
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185enum class VncState
186{
187 UNSTARTED,
188 AWAITING_CLIENT_VERSION,
189 AWAITING_CLIENT_AUTH_METHOD,
190 AWAITING_CLIENT_INIT_msg,
191 MAIN_LOOP
Ed Tanousc81ca422017-03-21 16:18:49 -0700192};
193
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194class ConnectionMetadata
195{
196 public:
197 ConnectionMetadata(){};
Ed Tanousc81ca422017-03-21 16:18:49 -0700198
Ed Tanous1abe55e2018-09-05 08:30:59 -0700199 VncState vncState{VncState::UNSTARTED};
Ed Tanousc81ca422017-03-21 16:18:49 -0700200};
201
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700202using meta_list = std::vector<ConnectionMetadata>;
203meta_list connectionStates(10);
Ed Tanousc81ca422017-03-21 16:18:49 -0700204
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700205ConnectionMetadata meta;
Ed Tanousc81ca422017-03-21 16:18:49 -0700206
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app)
208{
209 BMCWEB_ROUTE(app, "/kvmws")
210 .websocket()
211 .onopen([&](crow::websocket::Connection& conn) {
212 if (meta.vncState == VncState::UNSTARTED)
213 {
214 meta.vncState = VncState::AWAITING_CLIENT_VERSION;
215 conn.sendBinary(rfb38VersionString);
Ed Tanousc81ca422017-03-21 16:18:49 -0700216 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 else
218 { // SHould never happen
219 conn.close();
Ed Tanousc81ca422017-03-21 16:18:49 -0700220 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221 })
222 .onclose(
223 [&](crow::websocket::Connection& conn, const std::string& reason) {
224 meta.vncState = VncState::UNSTARTED;
225 })
226 .onmessage([&](crow::websocket::Connection& conn,
227 const std::string& data, bool is_binary) {
228 switch (meta.vncState)
229 {
230 case VncState::AWAITING_CLIENT_VERSION:
231 {
232 std::cout << "Client sent: " << data;
233 if (data == rfb38VersionString ||
234 data == rfb37VersionString)
235 {
236 std::string authTypes{
237 1, (uint8_t)RfbAuthScheme::no_authentication};
238 conn.sendBinary(authTypes);
239 meta.vncState = VncState::AWAITING_CLIENT_AUTH_METHOD;
Ed Tanous9140a672017-04-24 17:01:32 -0700240 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 else if (data == rfb33VersionString)
242 {
243 // TODO(ed) Support older protocols
244 meta.vncState = VncState::UNSTARTED;
245 conn.close();
246 }
247 else
248 {
249 // TODO(ed) Support older protocols
250 meta.vncState = VncState::UNSTARTED;
251 conn.close();
252 }
Ed Tanousc81ca422017-03-21 16:18:49 -0700253 }
Ed Tanousc81ca422017-03-21 16:18:49 -0700254 break;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255 case VncState::AWAITING_CLIENT_AUTH_METHOD:
256 {
257 std::string securityResult{{0, 0, 0, 0}};
258 if (data[0] == (uint8_t)RfbAuthScheme::no_authentication)
259 {
260 meta.vncState = VncState::AWAITING_CLIENT_INIT_msg;
261 }
262 else
263 {
264 // Mark auth as failed
265 securityResult[3] = 1;
266 meta.vncState = VncState::UNSTARTED;
267 }
268 conn.sendBinary(securityResult);
269 }
270 break;
271 case VncState::AWAITING_CLIENT_INIT_msg:
272 {
273 // Now send the server initialization
274 ServerInitializationMsg serverInitMsg{};
275 serverInitMsg.framebufferWidth = 800;
276 serverInitMsg.framebufferHeight = 600;
277 serverInitMsg.pixelFormat.bitsPerPixel = 32;
278 serverInitMsg.pixelFormat.isBigEndian = 0;
279 serverInitMsg.pixelFormat.isTrueColor = 1;
280 serverInitMsg.pixelFormat.redMax = 255;
281 serverInitMsg.pixelFormat.greenMax = 255;
282 serverInitMsg.pixelFormat.blueMax = 255;
283 serverInitMsg.pixelFormat.redShift = 16;
284 serverInitMsg.pixelFormat.greenShift = 8;
285 serverInitMsg.pixelFormat.blueShift = 0;
286 serverInitMsg.nameLength = 0;
287 std::cout << "size: " << sizeof(serverInitMsg);
288 // TODO(ed) this is ugly. Crow should really have a span
289 // type interface to avoid the copy, but alas, today it does
290 // not.
291 std::string s(reinterpret_cast<char*>(&serverInitMsg),
292 sizeof(serverInitMsg));
293 std::cout << "s.size() " << s.size();
294 conn.sendBinary(s);
295 meta.vncState = VncState::MAIN_LOOP;
296 }
297 break;
298 case VncState::MAIN_LOOP:
299 {
300 if (data.size() >= sizeof(client_to_server_msg_type))
301 {
302 auto type =
303 static_cast<client_to_server_msg_type>(data[0]);
304 std::cout << "Received client message type "
305 << static_cast<std::size_t>(type) << "\n";
306 switch (type)
307 {
308 case client_to_server_msg_type::set_pixel_format:
309 {
310 }
311 break;
Ed Tanousc81ca422017-03-21 16:18:49 -0700312
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 case client_to_server_msg_type::
314 fix_color_map_entries:
315 {
316 }
317 break;
318 case client_to_server_msg_type::set_encodings:
319 {
320 }
321 break;
322 case client_to_server_msg_type::
323 framebuffer_update_request:
324 {
325 // Make sure the buffer is long enough to handle
326 // what we're about to do
327 if (data.size() >=
328 sizeof(FrameBufferUpdateReq) +
329 sizeof(client_to_server_msg_type))
330 {
331 auto msg = reinterpret_cast<
332 const FrameBufferUpdateReq*>(
333 data.data() + // NOLINT
334 sizeof(client_to_server_msg_type));
335 // TODO(ed) find a better way to do this
336 // deserialization
Ed Tanousc81ca422017-03-21 16:18:49 -0700337
Ed Tanous1abe55e2018-09-05 08:30:59 -0700338 // Todo(ed) lifecycle of the video puller
339 // and decoder should be with the websocket,
340 // not recreated every time
341 ast_video::SimpleVideoPuller p;
342 p.initialize();
343 auto out = p.readVideo();
344 ast_video::AstJpegDecoder d;
345 d.decode(out.buffer, out.width, out.height,
346 out.mode, out.ySelector,
347 out.uvSelector);
Ed Tanousc81ca422017-03-21 16:18:49 -0700348
Ed Tanous1abe55e2018-09-05 08:30:59 -0700349 FramebufferUpdateMsg bufferUpdateMsg;
Ed Tanousc81ca422017-03-21 16:18:49 -0700350
Ed Tanous1abe55e2018-09-05 08:30:59 -0700351 // If the viewer is requesting a full
352 // update, force write of all pixels
353
354 FramebufferRectangle thisRect;
355 thisRect.x = msg->xPosition;
356 thisRect.y = msg->yPosition;
357 thisRect.width = out.width;
358 thisRect.height = out.height;
359 thisRect.encoding = static_cast<uint8_t>(
360 encoding_type::raw);
361 std::cout << "Encoding is "
362 << thisRect.encoding;
363 thisRect.data.reserve(
364 static_cast<std::size_t>(
365 thisRect.width) *
366 static_cast<std::size_t>(
367 thisRect.height) *
368 4);
369 std::cout << "Width " << out.width
370 << " Height " << out.height;
371
372 for (int i = 0; i < out.width * out.height;
373 i++)
374 {
375 auto& pixel = d.outBuffer[i];
376 thisRect.data.push_back(pixel.b);
377 thisRect.data.push_back(pixel.g);
378 thisRect.data.push_back(pixel.r);
379 thisRect.data.push_back(0);
380 }
381
382 bufferUpdateMsg.rectangles.push_back(
383 std::move(thisRect));
384 auto serialized =
385 serialize(bufferUpdateMsg);
386
387 conn.sendBinary(serialized);
388
389 } // TODO(Ed) handle error
390 }
391
392 break;
393
394 case client_to_server_msg_type::key_event:
395 {
396 }
397 break;
398
399 case client_to_server_msg_type::pointer_event:
400 {
401 }
402 break;
403
404 case client_to_server_msg_type::client_cut_text:
405 {
406 }
407 break;
408
409 default:
410 break;
411 }
412 }
413 }
414 break;
415 case VncState::UNSTARTED:
416 // Error? TODO
417 break;
Ed Tanousc81ca422017-03-21 16:18:49 -0700418 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700419 });
Ed Tanousc81ca422017-03-21 16:18:49 -0700420}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700421} // namespace kvm
422} // namespace crow