diff options
Diffstat (limited to 'include/web_kvm.hpp')
-rw-r--r-- | include/web_kvm.hpp | 665 |
1 files changed, 367 insertions, 298 deletions
diff --git a/include/web_kvm.hpp b/include/web_kvm.hpp index ad4b352eb4..747a137b87 100644 --- a/include/web_kvm.hpp +++ b/include/web_kvm.hpp @@ -1,181 +1,202 @@ -#include <string> #include <crow/app.h> -#include <boost/endian/arithmetic.hpp> #include <ast_jpeg_decoder.hpp> #include <ast_video_puller.hpp> +#include <boost/endian/arithmetic.hpp> +#include <string> -namespace crow { -namespace kvm { +namespace crow +{ +namespace kvm +{ static const std::string rfb33VersionString = "RFB 003.003\n"; static const std::string rfb37VersionString = "RFB 003.007\n"; static const std::string rfb38VersionString = "RFB 003.008\n"; -enum class RfbAuthScheme : uint8_t { - connection_failed = 0, - no_authentication = 1, - vnc_authentication = 2 +enum class RfbAuthScheme : uint8_t +{ + connection_failed = 0, + no_authentication = 1, + vnc_authentication = 2 }; -struct PixelFormatStruct { - boost::endian::big_uint8_t bitsPerPixel; - boost::endian::big_uint8_t depth; - boost::endian::big_uint8_t isBigEndian; - boost::endian::big_uint8_t isTrueColor; - boost::endian::big_uint16_t redMax; - boost::endian::big_uint16_t greenMax; - boost::endian::big_uint16_t blueMax; - boost::endian::big_uint8_t redShift; - boost::endian::big_uint8_t greenShift; - boost::endian::big_uint8_t blueShift; - boost::endian::big_uint8_t pad1; - boost::endian::big_uint8_t pad2; - boost::endian::big_uint8_t pad3; +struct PixelFormatStruct +{ + boost::endian::big_uint8_t bitsPerPixel; + boost::endian::big_uint8_t depth; + boost::endian::big_uint8_t isBigEndian; + boost::endian::big_uint8_t isTrueColor; + boost::endian::big_uint16_t redMax; + boost::endian::big_uint16_t greenMax; + boost::endian::big_uint16_t blueMax; + boost::endian::big_uint8_t redShift; + boost::endian::big_uint8_t greenShift; + boost::endian::big_uint8_t blueShift; + boost::endian::big_uint8_t pad1; + boost::endian::big_uint8_t pad2; + boost::endian::big_uint8_t pad3; }; -struct ServerInitializationMsg { - boost::endian::big_uint16_t framebufferWidth; - boost::endian::big_uint16_t framebufferHeight; - PixelFormatStruct pixelFormat; - boost::endian::big_uint32_t nameLength; +struct ServerInitializationMsg +{ + boost::endian::big_uint16_t framebufferWidth; + boost::endian::big_uint16_t framebufferHeight; + PixelFormatStruct pixelFormat; + boost::endian::big_uint32_t nameLength; }; -enum class client_to_server_msg_type : uint8_t { - set_pixel_format = 0, - fix_color_map_entries = 1, - set_encodings = 2, - framebuffer_update_request = 3, - key_event = 4, - pointer_event = 5, - client_cut_text = 6 +enum class client_to_server_msg_type : uint8_t +{ + set_pixel_format = 0, + fix_color_map_entries = 1, + set_encodings = 2, + framebuffer_update_request = 3, + key_event = 4, + pointer_event = 5, + client_cut_text = 6 }; -enum class server_to_client_message_type : uint8_t { - framebuffer_update = 0, - set_color_map_entries = 1, - bell_message = 2, - server_cut_text = 3 +enum class server_to_client_message_type : uint8_t +{ + framebuffer_update = 0, + set_color_map_entries = 1, + bell_message = 2, + server_cut_text = 3 }; -struct SetPixelFormatMsg { - boost::endian::big_uint8_t pad1; - boost::endian::big_uint8_t pad2; - boost::endian::big_uint8_t pad3; - PixelFormatStruct pixelFormat; +struct SetPixelFormatMsg +{ + boost::endian::big_uint8_t pad1; + boost::endian::big_uint8_t pad2; + boost::endian::big_uint8_t pad3; + PixelFormatStruct pixelFormat; }; -struct FrameBufferUpdateReq { - boost::endian::big_uint8_t incremental; - boost::endian::big_uint16_t xPosition; - boost::endian::big_uint16_t yPosition; - boost::endian::big_uint16_t width; - boost::endian::big_uint16_t height; +struct FrameBufferUpdateReq +{ + boost::endian::big_uint8_t incremental; + boost::endian::big_uint16_t xPosition; + boost::endian::big_uint16_t yPosition; + boost::endian::big_uint16_t width; + boost::endian::big_uint16_t height; }; -struct KeyEventMsg { - boost::endian::big_uint8_t downFlag; - boost::endian::big_uint8_t pad1; - boost::endian::big_uint8_t pad2; - boost::endian::big_uint32_t key; +struct KeyEventMsg +{ + boost::endian::big_uint8_t downFlag; + boost::endian::big_uint8_t pad1; + boost::endian::big_uint8_t pad2; + boost::endian::big_uint32_t key; }; -struct PointerEventMsg { - boost::endian::big_uint8_t buttonMask; - boost::endian::big_uint16_t xPosition; - boost::endian::big_uint16_t yPosition; +struct PointerEventMsg +{ + boost::endian::big_uint8_t buttonMask; + boost::endian::big_uint16_t xPosition; + boost::endian::big_uint16_t yPosition; }; -struct ClientCutTextMsg { - std::vector<uint8_t> data; +struct ClientCutTextMsg +{ + std::vector<uint8_t> data; }; -enum class encoding_type : uint32_t { - raw = 0x00, - copy_rectangle = 0x01, - rising_rectangle = 0x02, - corre = 0x04, - hextile = 0x05, - zlib = 0x06, - tight = 0x07, - zlibhex = 0x08, - ultra = 0x09, - zrle = 0x10, - zywrle = 0x011, - cache_enable = 0xFFFF0001, - xor_enable = 0xFFFF0006, - server_state_ultranvc = 0xFFFF8000, - enable_keepAlive = 0xFFFF8001, - enableftp_protocol_version = 0xFFFF8002, - tight_compress_level_0 = 0xFFFFFF00, - tight_compress_level_9 = 0xFFFFFF09, - x_cursor = 0xFFFFFF10, - rich_cursor = 0xFFFFFF11, - pointer_pos = 0xFFFFFF18, - last_rect = 0xFFFFFF20, - new_framebuffer_size = 0xFFFFFF21, - tight_quality_level_0 = 0xFFFFFFE0, - tight_quality_level_9 = 0xFFFFFFE9 +enum class encoding_type : uint32_t +{ + raw = 0x00, + copy_rectangle = 0x01, + rising_rectangle = 0x02, + corre = 0x04, + hextile = 0x05, + zlib = 0x06, + tight = 0x07, + zlibhex = 0x08, + ultra = 0x09, + zrle = 0x10, + zywrle = 0x011, + cache_enable = 0xFFFF0001, + xor_enable = 0xFFFF0006, + server_state_ultranvc = 0xFFFF8000, + enable_keepAlive = 0xFFFF8001, + enableftp_protocol_version = 0xFFFF8002, + tight_compress_level_0 = 0xFFFFFF00, + tight_compress_level_9 = 0xFFFFFF09, + x_cursor = 0xFFFFFF10, + rich_cursor = 0xFFFFFF11, + pointer_pos = 0xFFFFFF18, + last_rect = 0xFFFFFF20, + new_framebuffer_size = 0xFFFFFF21, + tight_quality_level_0 = 0xFFFFFFE0, + tight_quality_level_9 = 0xFFFFFFE9 }; -struct FramebufferRectangle { - boost::endian::big_uint16_t x{}; - boost::endian::big_uint16_t y{}; - boost::endian::big_uint16_t width{}; - boost::endian::big_uint16_t height{}; - boost::endian::big_uint32_t encoding{}; - std::vector<uint8_t> data; +struct FramebufferRectangle +{ + boost::endian::big_uint16_t x{}; + boost::endian::big_uint16_t y{}; + boost::endian::big_uint16_t width{}; + boost::endian::big_uint16_t height{}; + boost::endian::big_uint32_t encoding{}; + std::vector<uint8_t> data; }; -struct FramebufferUpdateMsg { - boost::endian::big_uint8_t messageType{}; - std::vector<FramebufferRectangle> rectangles; +struct FramebufferUpdateMsg +{ + boost::endian::big_uint8_t messageType{}; + std::vector<FramebufferRectangle> rectangles; }; -inline std::string serialize(const FramebufferUpdateMsg& msg) { - // calculate the size of the needed vector for serialization - size_t vectorSize = 4; - for (const auto& rect : msg.rectangles) { - vectorSize += 12 + rect.data.size(); - } - - std::string serialized(vectorSize, 0); - - size_t i = 0; - serialized[i++] = static_cast<char>( - server_to_client_message_type::framebuffer_update); // Type - serialized[i++] = 0; // Pad byte - boost::endian::big_uint16_t numberOfRectangles = msg.rectangles.size(); - std::memcpy(&serialized[i], &numberOfRectangles, sizeof(numberOfRectangles)); - i += sizeof(numberOfRectangles); - - for (const auto& rect : msg.rectangles) { - // copy the first part of the struct - size_t bufferSize = - sizeof(FramebufferRectangle) - sizeof(std::vector<uint8_t>); - std::memcpy(&serialized[i], &rect, bufferSize); - i += bufferSize; - - std::memcpy(&serialized[i], rect.data.data(), rect.data.size()); - i += rect.data.size(); - } - - return serialized; +inline std::string serialize(const FramebufferUpdateMsg& msg) +{ + // calculate the size of the needed vector for serialization + size_t vectorSize = 4; + for (const auto& rect : msg.rectangles) + { + vectorSize += 12 + rect.data.size(); + } + + std::string serialized(vectorSize, 0); + + size_t i = 0; + serialized[i++] = static_cast<char>( + server_to_client_message_type::framebuffer_update); // Type + serialized[i++] = 0; // Pad byte + boost::endian::big_uint16_t numberOfRectangles = msg.rectangles.size(); + std::memcpy(&serialized[i], &numberOfRectangles, + sizeof(numberOfRectangles)); + i += sizeof(numberOfRectangles); + + for (const auto& rect : msg.rectangles) + { + // copy the first part of the struct + size_t bufferSize = + sizeof(FramebufferRectangle) - sizeof(std::vector<uint8_t>); + std::memcpy(&serialized[i], &rect, bufferSize); + i += bufferSize; + + std::memcpy(&serialized[i], rect.data.data(), rect.data.size()); + i += rect.data.size(); + } + + return serialized; } -enum class VncState { - UNSTARTED, - AWAITING_CLIENT_VERSION, - AWAITING_CLIENT_AUTH_METHOD, - AWAITING_CLIENT_INIT_msg, - MAIN_LOOP +enum class VncState +{ + UNSTARTED, + AWAITING_CLIENT_VERSION, + AWAITING_CLIENT_AUTH_METHOD, + AWAITING_CLIENT_INIT_msg, + MAIN_LOOP }; -class ConnectionMetadata { - public: - ConnectionMetadata(){}; +class ConnectionMetadata +{ + public: + ConnectionMetadata(){}; - VncState vncState{VncState::UNSTARTED}; + VncState vncState{VncState::UNSTARTED}; }; using meta_list = std::vector<ConnectionMetadata>; @@ -183,171 +204,219 @@ meta_list connectionStates(10); ConnectionMetadata meta; -template <typename... Middlewares> -void requestRoutes(Crow<Middlewares...>& app) { - BMCWEB_ROUTE(app, "/kvmws") - .websocket() - .onopen([&](crow::websocket::Connection& conn) { - if (meta.vncState == VncState::UNSTARTED) { - meta.vncState = VncState::AWAITING_CLIENT_VERSION; - conn.sendBinary(rfb38VersionString); - } else { // SHould never happen - conn.close(); - } - - }) - .onclose( - [&](crow::websocket::Connection& conn, const std::string& reason) { - meta.vncState = VncState::UNSTARTED; - }) - .onmessage([&](crow::websocket::Connection& conn, const std::string& data, - bool is_binary) { - switch (meta.vncState) { - case VncState::AWAITING_CLIENT_VERSION: { - std::cout << "Client sent: " << data; - if (data == rfb38VersionString || data == rfb37VersionString) { - std::string authTypes{1, - (uint8_t)RfbAuthScheme::no_authentication}; - conn.sendBinary(authTypes); - meta.vncState = VncState::AWAITING_CLIENT_AUTH_METHOD; - } else if (data == rfb33VersionString) { - // TODO(ed) Support older protocols - meta.vncState = VncState::UNSTARTED; - conn.close(); - } else { - // TODO(ed) Support older protocols - meta.vncState = VncState::UNSTARTED; - conn.close(); +template <typename... Middlewares> void requestRoutes(Crow<Middlewares...>& app) +{ + BMCWEB_ROUTE(app, "/kvmws") + .websocket() + .onopen([&](crow::websocket::Connection& conn) { + if (meta.vncState == VncState::UNSTARTED) + { + meta.vncState = VncState::AWAITING_CLIENT_VERSION; + conn.sendBinary(rfb38VersionString); } - } break; - case VncState::AWAITING_CLIENT_AUTH_METHOD: { - std::string securityResult{{0, 0, 0, 0}}; - if (data[0] == (uint8_t)RfbAuthScheme::no_authentication) { - meta.vncState = VncState::AWAITING_CLIENT_INIT_msg; - } else { - // Mark auth as failed - securityResult[3] = 1; - meta.vncState = VncState::UNSTARTED; + else + { // SHould never happen + conn.close(); } - conn.sendBinary(securityResult); - } break; - case VncState::AWAITING_CLIENT_INIT_msg: { - // Now send the server initialization - ServerInitializationMsg serverInitMsg{}; - serverInitMsg.framebufferWidth = 800; - serverInitMsg.framebufferHeight = 600; - serverInitMsg.pixelFormat.bitsPerPixel = 32; - serverInitMsg.pixelFormat.isBigEndian = 0; - serverInitMsg.pixelFormat.isTrueColor = 1; - serverInitMsg.pixelFormat.redMax = 255; - serverInitMsg.pixelFormat.greenMax = 255; - serverInitMsg.pixelFormat.blueMax = 255; - serverInitMsg.pixelFormat.redShift = 16; - serverInitMsg.pixelFormat.greenShift = 8; - serverInitMsg.pixelFormat.blueShift = 0; - serverInitMsg.nameLength = 0; - std::cout << "size: " << sizeof(serverInitMsg); - // TODO(ed) this is ugly. Crow should really have a span type - // interface - // to avoid the copy, but alas, today it does not. - std::string s(reinterpret_cast<char*>(&serverInitMsg), - sizeof(serverInitMsg)); - std::cout << "s.size() " << s.size(); - conn.sendBinary(s); - meta.vncState = VncState::MAIN_LOOP; - } break; - case VncState::MAIN_LOOP: { - if (data.size() >= sizeof(client_to_server_msg_type)) { - auto type = static_cast<client_to_server_msg_type>(data[0]); - std::cout << "Received client message type " - << static_cast<std::size_t>(type) << "\n"; - switch (type) { - case client_to_server_msg_type::set_pixel_format: { - } break; - - case client_to_server_msg_type::fix_color_map_entries: { - } break; - case client_to_server_msg_type::set_encodings: { - } break; - case client_to_server_msg_type::framebuffer_update_request: { - // Make sure the buffer is long enough to handle what we're - // about to do - if (data.size() >= sizeof(FrameBufferUpdateReq) + - sizeof(client_to_server_msg_type)) { - auto msg = reinterpret_cast<const FrameBufferUpdateReq*>( - data.data() + // NOLINT - sizeof(client_to_server_msg_type)); - // TODO(ed) find a better way to do this deserialization - - // Todo(ed) lifecycle of the video puller and decoder - // should be - // with the websocket, not recreated every time - ast_video::SimpleVideoPuller p; - p.initialize(); - auto out = p.readVideo(); - ast_video::AstJpegDecoder d; - d.decode(out.buffer, out.width, out.height, out.mode, - out.ySelector, out.uvSelector); - - FramebufferUpdateMsg bufferUpdateMsg; - - // If the viewer is requesting a full update, force write - // of all pixels - - FramebufferRectangle thisRect; - thisRect.x = msg->xPosition; - thisRect.y = msg->yPosition; - thisRect.width = out.width; - thisRect.height = out.height; - thisRect.encoding = - static_cast<uint8_t>(encoding_type::raw); - std::cout << "Encoding is " << thisRect.encoding; - thisRect.data.reserve( - static_cast<std::size_t>(thisRect.width) * - static_cast<std::size_t>(thisRect.height) * 4); - std::cout << "Width " << out.width << " Height " - << out.height; - - for (int i = 0; i < out.width * out.height; i++) { - auto& pixel = d.outBuffer[i]; - thisRect.data.push_back(pixel.b); - thisRect.data.push_back(pixel.g); - thisRect.data.push_back(pixel.r); - thisRect.data.push_back(0); + }) + .onclose( + [&](crow::websocket::Connection& conn, const std::string& reason) { + meta.vncState = VncState::UNSTARTED; + }) + .onmessage([&](crow::websocket::Connection& conn, + const std::string& data, bool is_binary) { + switch (meta.vncState) + { + case VncState::AWAITING_CLIENT_VERSION: + { + std::cout << "Client sent: " << data; + if (data == rfb38VersionString || + data == rfb37VersionString) + { + std::string authTypes{ + 1, (uint8_t)RfbAuthScheme::no_authentication}; + conn.sendBinary(authTypes); + meta.vncState = VncState::AWAITING_CLIENT_AUTH_METHOD; + } + else if (data == rfb33VersionString) + { + // TODO(ed) Support older protocols + meta.vncState = VncState::UNSTARTED; + conn.close(); + } + else + { + // TODO(ed) Support older protocols + meta.vncState = VncState::UNSTARTED; + conn.close(); } - - bufferUpdateMsg.rectangles.push_back(std::move(thisRect)); - auto serialized = serialize(bufferUpdateMsg); - - conn.sendBinary(serialized); - - } // TODO(Ed) handle error - } - break; - - case client_to_server_msg_type::key_event: { - } break; - - case client_to_server_msg_type::pointer_event: { - } break; - - case client_to_server_msg_type::client_cut_text: { - } break; - - default: - break; - } + case VncState::AWAITING_CLIENT_AUTH_METHOD: + { + std::string securityResult{{0, 0, 0, 0}}; + if (data[0] == (uint8_t)RfbAuthScheme::no_authentication) + { + meta.vncState = VncState::AWAITING_CLIENT_INIT_msg; + } + else + { + // Mark auth as failed + securityResult[3] = 1; + meta.vncState = VncState::UNSTARTED; + } + conn.sendBinary(securityResult); + } + break; + case VncState::AWAITING_CLIENT_INIT_msg: + { + // Now send the server initialization + ServerInitializationMsg serverInitMsg{}; + serverInitMsg.framebufferWidth = 800; + serverInitMsg.framebufferHeight = 600; + serverInitMsg.pixelFormat.bitsPerPixel = 32; + serverInitMsg.pixelFormat.isBigEndian = 0; + serverInitMsg.pixelFormat.isTrueColor = 1; + serverInitMsg.pixelFormat.redMax = 255; + serverInitMsg.pixelFormat.greenMax = 255; + serverInitMsg.pixelFormat.blueMax = 255; + serverInitMsg.pixelFormat.redShift = 16; + serverInitMsg.pixelFormat.greenShift = 8; + serverInitMsg.pixelFormat.blueShift = 0; + serverInitMsg.nameLength = 0; + std::cout << "size: " << sizeof(serverInitMsg); + // TODO(ed) this is ugly. Crow should really have a span + // type interface to avoid the copy, but alas, today it does + // not. + std::string s(reinterpret_cast<char*>(&serverInitMsg), + sizeof(serverInitMsg)); + std::cout << "s.size() " << s.size(); + conn.sendBinary(s); + meta.vncState = VncState::MAIN_LOOP; + } + break; + case VncState::MAIN_LOOP: + { + if (data.size() >= sizeof(client_to_server_msg_type)) + { + auto type = + static_cast<client_to_server_msg_type>(data[0]); + std::cout << "Received client message type " + << static_cast<std::size_t>(type) << "\n"; + switch (type) + { + case client_to_server_msg_type::set_pixel_format: + { + } + break; + + case client_to_server_msg_type:: + fix_color_map_entries: + { + } + break; + case client_to_server_msg_type::set_encodings: + { + } + break; + case client_to_server_msg_type:: + framebuffer_update_request: + { + // Make sure the buffer is long enough to handle + // what we're about to do + if (data.size() >= + sizeof(FrameBufferUpdateReq) + + sizeof(client_to_server_msg_type)) + { + auto msg = reinterpret_cast< + const FrameBufferUpdateReq*>( + data.data() + // NOLINT + sizeof(client_to_server_msg_type)); + // TODO(ed) find a better way to do this + // deserialization + + // Todo(ed) lifecycle of the video puller + // and decoder should be with the websocket, + // not recreated every time + ast_video::SimpleVideoPuller p; + p.initialize(); + auto out = p.readVideo(); + ast_video::AstJpegDecoder d; + d.decode(out.buffer, out.width, out.height, + out.mode, out.ySelector, + out.uvSelector); + + FramebufferUpdateMsg bufferUpdateMsg; + + // If the viewer is requesting a full + // update, force write of all pixels + + FramebufferRectangle thisRect; + thisRect.x = msg->xPosition; + thisRect.y = msg->yPosition; + thisRect.width = out.width; + thisRect.height = out.height; + thisRect.encoding = static_cast<uint8_t>( + encoding_type::raw); + std::cout << "Encoding is " + << thisRect.encoding; + thisRect.data.reserve( + static_cast<std::size_t>( + thisRect.width) * + static_cast<std::size_t>( + thisRect.height) * + 4); + std::cout << "Width " << out.width + << " Height " << out.height; + + for (int i = 0; i < out.width * out.height; + i++) + { + auto& pixel = d.outBuffer[i]; + thisRect.data.push_back(pixel.b); + thisRect.data.push_back(pixel.g); + thisRect.data.push_back(pixel.r); + thisRect.data.push_back(0); + } + + bufferUpdateMsg.rectangles.push_back( + std::move(thisRect)); + auto serialized = + serialize(bufferUpdateMsg); + + conn.sendBinary(serialized); + + } // TODO(Ed) handle error + } + + break; + + case client_to_server_msg_type::key_event: + { + } + break; + + case client_to_server_msg_type::pointer_event: + { + } + break; + + case client_to_server_msg_type::client_cut_text: + { + } + break; + + default: + break; + } + } + } + break; + case VncState::UNSTARTED: + // Error? TODO + break; } - - } break; - case VncState::UNSTARTED: - // Error? TODO - break; - } - - }); + }); } -} // namespace kvm -} // namespace crow
\ No newline at end of file +} // namespace kvm +} // namespace crow
\ No newline at end of file |