1 #include "crow/app.h" 2 #include "crow/ci_map.h" 3 #include "crow/common.h" 4 #include "crow/dumb_timer_queue.h" 5 #include "crow/http_connection.h" 6 #include "crow/http_parser_merged.h" 7 #include "crow/http_request.h" 8 #include "crow/http_response.h" 9 #include "crow/http_server.h" 10 #include "crow/json.h" 11 #include "crow/logging.h" 12 #include "crow/middleware.h" 13 #include "crow/middleware_context.h" 14 #include "crow/mustache.h" 15 #include "crow/parser.h" 16 #include "crow/query_string.h" 17 #include "crow/routing.h" 18 #include "crow/settings.h" 19 #include "crow/socket_adaptors.h" 20 #include "crow/utility.h" 21 #include "crow/websocket.h" 22 23 #include "color_cout_g3_sink.hpp" 24 #include "token_authorization_middleware.hpp" 25 #include "webassets.hpp" 26 27 #include <iostream> 28 #include <memory> 29 #include <string> 30 #include "ssl_key_handler.hpp" 31 32 #include <boost/endian/arithmetic.hpp> 33 34 #include <boost/asio.hpp> 35 36 #include <unordered_set> 37 #include <webassets.hpp> 38 39 static const std::string rfb_3_3_version_string = "RFB 003.003\n"; 40 static const std::string rfb_3_7_version_string = "RFB 003.007\n"; 41 static const std::string rfb_3_8_version_string = "RFB 003.008\n"; 42 43 enum class RfbAuthScheme : uint8_t { connection_failed = 0, no_authentication = 1, vnc_authentication = 2 }; 44 45 struct pixel_format_struct { 46 boost::endian::big_uint8_t bits_per_pixel; 47 boost::endian::big_uint8_t depth; 48 boost::endian::big_uint8_t is_big_endian; 49 boost::endian::big_uint8_t is_true_color; 50 boost::endian::big_uint16_t red_max; 51 boost::endian::big_uint16_t green_max; 52 boost::endian::big_uint16_t blue_max; 53 boost::endian::big_uint8_t red_shift; 54 boost::endian::big_uint8_t green_shift; 55 boost::endian::big_uint8_t blue_shift; 56 boost::endian::big_uint8_t pad1; 57 boost::endian::big_uint8_t pad2; 58 boost::endian::big_uint8_t pad3; 59 }; 60 61 struct server_initialization_message { 62 boost::endian::big_uint16_t framebuffer_width; 63 boost::endian::big_uint16_t framebuffer_height; 64 pixel_format_struct pixel_format; 65 boost::endian::big_uint32_t name_length; 66 }; 67 68 enum class client_to_server_message_type : uint8_t { 69 set_pixel_format = 0, 70 fix_color_map_entries = 1, 71 set_encodings = 2, 72 framebuffer_update_request = 3, 73 key_event = 4, 74 pointer_event = 5, 75 client_cut_text = 6 76 }; 77 78 struct set_pixel_format_message { 79 boost::endian::big_uint8_t pad1; 80 boost::endian::big_uint8_t pad2; 81 boost::endian::big_uint8_t pad3; 82 pixel_format_struct pixel_format; 83 }; 84 85 struct frame_buffer_update_request_message { 86 boost::endian::big_uint8_t incremental; 87 boost::endian::big_uint16_t x_position; 88 boost::endian::big_uint16_t y_position; 89 boost::endian::big_uint16_t width; 90 boost::endian::big_uint16_t height; 91 }; 92 93 struct key_event_message { 94 boost::endian::big_uint8_t down_flag; 95 boost::endian::big_uint8_t pad1; 96 boost::endian::big_uint8_t pad2; 97 boost::endian::big_uint32_t key; 98 }; 99 100 struct pointer_event_message { 101 boost::endian::big_uint8_t button_mask; 102 boost::endian::big_uint16_t x_position; 103 boost::endian::big_uint16_t y_position; 104 }; 105 106 struct client_cut_text_message { 107 std::vector<uint8_t> data; 108 }; 109 110 enum class encoding_type : uint32_t { 111 raw = 0x00, 112 copy_rectangle = 0x01, 113 rising_rectangle = 0x02, 114 corre = 0x04, 115 hextile = 0x05, 116 zlib = 0x06, 117 tight = 0x07, 118 zlibhex = 0x08, 119 ultra = 0x09, 120 zrle = 0x10, 121 zywrle = 0x011, 122 cache_enable = 0xFFFF0001, 123 xor_enable = 0xFFFF0006, 124 server_state_ultranvc = 0xFFFF8000, 125 enable_keep_alive = 0xFFFF8001, 126 enableftp_protocol_version = 0xFFFF8002, 127 tight_compress_level_0 = 0xFFFFFF00, 128 tight_compress_level_9 = 0xFFFFFF09, 129 x_cursor = 0xFFFFFF10, 130 rich_cursor = 0xFFFFFF11, 131 pointer_pos = 0xFFFFFF18, 132 last_rect = 0xFFFFFF20, 133 new_framebuffer_size = 0xFFFFFF21, 134 tight_quality_level_0 = 0xFFFFFFE0, 135 tight_quality_level_9 = 0xFFFFFFE9 136 }; 137 138 struct framebuffer_rectangle { 139 boost::endian::big_uint16_t x; 140 boost::endian::big_uint16_t y; 141 boost::endian::big_uint16_t width; 142 boost::endian::big_uint16_t height; 143 boost::endian::big_uint32_t encoding; 144 std::vector<uint8_t> data; 145 }; 146 147 struct framebuffer_update_message { 148 boost::endian::big_uint8_t message_type; 149 150 std::vector<framebuffer_rectangle> rectangles; 151 }; 152 153 std::string serialize(const framebuffer_update_message& msg) { 154 // calculate the size of the needed vector for serialization 155 size_t vector_size = 4; 156 for (const auto& rect : msg.rectangles) { 157 vector_size += 12 + rect.data.size(); 158 } 159 160 std::string serialized(vector_size, 0); 161 162 size_t i = 0; 163 serialized[i++] = 0; // Type 164 serialized[i++] = 0; // Pad byte 165 boost::endian::big_uint16_t number_of_rectangles; 166 std::memcpy(&serialized[i], &number_of_rectangles, sizeof(number_of_rectangles)); 167 i += sizeof(number_of_rectangles); 168 169 for (const auto& rect : msg.rectangles) { 170 // copy the first part of the struct 171 size_t buffer_size = sizeof(framebuffer_rectangle) - sizeof(std::vector<uint8_t>); 172 std::memcpy(&serialized[i], &rect, buffer_size); 173 i += buffer_size; 174 175 std::memcpy(&serialized[i], rect.data.data(), rect.data.size()); 176 i += rect.data.size(); 177 } 178 179 return serialized; 180 } 181 182 enum class VncState { UNSTARTED, AWAITING_CLIENT_VERSION, AWAITING_CLIENT_AUTH_METHOD, AWAITING_CLIENT_INIT_MESSAGE, MAIN_LOOP }; 183 184 class connection_metadata { 185 public: 186 connection_metadata(void) : vnc_state(VncState::AWAITING_CLIENT_VERSION){}; 187 188 VncState vnc_state; 189 }; 190 191 int main(int argc, char** argv) { 192 auto worker(g3::LogWorker::createLogWorker()); 193 194 auto handle = worker->addDefaultLogger(argv[0], "/tmp/"); 195 g3::initializeLogging(worker.get()); 196 auto sink_handle = worker->addSink(std::make_unique<crow::ColorCoutSink>(), &crow::ColorCoutSink::ReceiveLogMessage); 197 198 std::string ssl_pem_file("server.pem"); 199 ensuressl::ensure_openssl_key_present_and_valid(ssl_pem_file); 200 201 crow::App<crow::TokenAuthorizationMiddleware> app; 202 crow::webassets::request_routes(app); 203 204 crow::logger::setLogLevel(crow::LogLevel::INFO); 205 206 CROW_ROUTE(app, "/routes") 207 ([&app]() { 208 crow::json::wvalue routes; 209 210 routes["routes"] = app.get_rules(); 211 return routes; 212 }); 213 214 CROW_ROUTE(app, "/login") 215 .methods("POST"_method)([&](const crow::request& req) { 216 auto auth_token = app.get_context<crow::TokenAuthorizationMiddleware>(req).auth_token; 217 crow::json::wvalue x; 218 x["token"] = auth_token; 219 220 return x; 221 }); 222 223 CROW_ROUTE(app, "/logout") 224 .methods("GET"_method, "POST"_method)([]() { 225 // Do nothing. Credentials have already been cleared by middleware. 226 return 200; 227 }); 228 229 CROW_ROUTE(app, "/systeminfo") 230 ([]() { 231 232 crow::json::wvalue j; 233 j["device_id"] = 0x7B; 234 j["device_provides_sdrs"] = true; 235 j["device_revision"] = true; 236 j["device_available"] = true; 237 j["firmware_revision"] = "0.68"; 238 239 j["ipmi_revision"] = "2.0"; 240 j["supports_chassis_device"] = true; 241 j["supports_bridge"] = true; 242 j["supports_ipmb_event_generator"] = true; 243 j["supports_ipmb_event_receiver"] = true; 244 j["supports_fru_inventory_device"] = true; 245 j["supports_sel_device"] = true; 246 j["supports_sdr_repository_device"] = true; 247 j["supports_sensor_device"] = true; 248 249 j["firmware_aux_revision"] = "0.60.foobar"; 250 251 return j; 252 }); 253 254 typedef std::vector<connection_metadata> meta_list; 255 meta_list connection_states(10); 256 257 connection_metadata meta; 258 259 CROW_ROUTE(app, "/kvmws") 260 .websocket() 261 .onopen([&](crow::websocket::connection& conn) { 262 meta.vnc_state = VncState::AWAITING_CLIENT_VERSION; 263 conn.send_binary(rfb_3_8_version_string); 264 }) 265 .onclose([&](crow::websocket::connection& conn, const std::string& reason) { 266 267 }) 268 .onmessage([&](crow::websocket::connection& conn, const std::string& data, bool is_binary) { 269 switch (meta.vnc_state) { 270 case VncState::AWAITING_CLIENT_VERSION: { 271 std::cout << "Client sent: " << data; 272 if (data == rfb_3_8_version_string || data == rfb_3_7_version_string) { 273 std::string auth_types{1, (uint8_t)RfbAuthScheme::no_authentication}; 274 conn.send_binary(auth_types); 275 meta.vnc_state = VncState::AWAITING_CLIENT_AUTH_METHOD; 276 } else if (data == rfb_3_3_version_string) { 277 // TODO(ed) 278 } else { 279 // TODO(ed) 280 } 281 } break; 282 case VncState::AWAITING_CLIENT_AUTH_METHOD: { 283 std::string security_result{{0, 0, 0, 0}}; 284 if (data[0] == (uint8_t)RfbAuthScheme::no_authentication) { 285 meta.vnc_state = VncState::AWAITING_CLIENT_INIT_MESSAGE; 286 } else { 287 // Mark auth as failed 288 security_result[3] = 1; 289 meta.vnc_state = VncState::UNSTARTED; 290 } 291 conn.send_binary(security_result); 292 } break; 293 case VncState::AWAITING_CLIENT_INIT_MESSAGE: { 294 // Now send the server initialization 295 server_initialization_message server_init_msg; 296 server_init_msg.framebuffer_width = 640; 297 server_init_msg.framebuffer_height = 480; 298 server_init_msg.pixel_format.bits_per_pixel = 32; 299 server_init_msg.pixel_format.is_big_endian = 0; 300 server_init_msg.pixel_format.is_true_color = 1; 301 server_init_msg.pixel_format.red_max = 255; 302 server_init_msg.pixel_format.green_max = 255; 303 server_init_msg.pixel_format.blue_max = 255; 304 server_init_msg.pixel_format.red_shift = 16; 305 server_init_msg.pixel_format.green_shift = 8; 306 server_init_msg.pixel_format.blue_shift = 0; 307 server_init_msg.name_length = 0; 308 std::cout << "size: " << sizeof(server_init_msg); 309 // TODO(ed) this is ugly. Crow should really have a span type interface 310 // to avoid the copy, but alas, today it does not. 311 std::string s(reinterpret_cast<char*>(&server_init_msg), sizeof(server_init_msg)); 312 LOG(DEBUG) << "s.size() " << s.size(); 313 conn.send_binary(s); 314 meta.vnc_state = VncState::MAIN_LOOP; 315 } break; 316 case VncState::MAIN_LOOP: { 317 if (data.size() >= sizeof(client_to_server_message_type)) { 318 auto type = static_cast<client_to_server_message_type>(data[0]); 319 std::cout << "Got type " << (uint32_t)type << "\n"; 320 switch (type) { 321 case client_to_server_message_type::set_pixel_format: { 322 } break; 323 324 case client_to_server_message_type::fix_color_map_entries: { 325 } break; 326 case client_to_server_message_type::set_encodings: { 327 } break; 328 case client_to_server_message_type::framebuffer_update_request: { 329 // Make sure the buffer is long enough to handle what we're about to do 330 if (data.size() >= sizeof(frame_buffer_update_request_message) + sizeof(client_to_server_message_type)) { 331 auto msg = reinterpret_cast<const frame_buffer_update_request_message*>(data.data() + sizeof(client_to_server_message_type)); 332 333 std::cout << "framebuffer_update_request_message\n"; 334 std::cout << " incremental=" << msg->incremental << "\n"; 335 std::cout << " x=" << msg->x_position; 336 std::cout << " y=" << msg->y_position << "\n"; 337 std::cout << " width=" << msg->width; 338 std::cout << " height=" << msg->height << "\n"; 339 340 framebuffer_update_message buffer_update_message; 341 342 // If the viewer is requesting a full update, force write of all 343 // pixels 344 345 framebuffer_rectangle this_rect; 346 this_rect.x = msg->x_position; 347 this_rect.y = msg->y_position; 348 this_rect.width = msg->width; 349 this_rect.height = msg->height; 350 this_rect.encoding = static_cast<uint8_t>(encoding_type::raw); 351 352 this_rect.data.reserve(this_rect.width * this_rect.height * 4); 353 354 for (unsigned int x_index = 0; x_index < this_rect.width; x_index++) { 355 for (unsigned int y_index = 0; y_index < this_rect.height; y_index++) { 356 this_rect.data.push_back(static_cast<uint8_t>(0)); // Blue 357 this_rect.data.push_back(static_cast<uint8_t>(0)); // Green 358 this_rect.data.push_back(static_cast<uint8_t>(x_index * 0xFF / msg->width)); // RED 359 this_rect.data.push_back(static_cast<uint8_t>(0)); // UNUSED 360 } 361 } 362 363 buffer_update_message.rectangles.push_back(std::move(this_rect)); 364 auto serialized = serialize(buffer_update_message); 365 366 conn.send_binary(serialized); 367 } 368 369 } 370 371 break; 372 373 case client_to_server_message_type::key_event: { 374 } break; 375 376 case client_to_server_message_type::pointer_event: { 377 } break; 378 379 case client_to_server_message_type::client_cut_text: { 380 } break; 381 382 default: 383 break; 384 } 385 } 386 387 } break; 388 case VncState::UNSTARTED: 389 // Error? TODO 390 break; 391 } 392 393 }); 394 395 CROW_ROUTE(app, "/ipmiws") 396 .websocket() 397 .onopen([&](crow::websocket::connection& conn) { 398 399 }) 400 .onclose([&](crow::websocket::connection& conn, const std::string& reason) { 401 402 }) 403 .onmessage([&](crow::websocket::connection& conn, const std::string& data, bool is_binary) { 404 boost::asio::io_service io_service; 405 boost::asio::ip::udp::udp::socket socket(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)); 406 boost::asio::ip::udp::resolver resolver(io_service); 407 boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), "10.243.48.31", "623"); 408 boost::asio::ip::udp::resolver::iterator iter = resolver.resolve(query); 409 socket.send_to(boost::asio::buffer(data), *iter); 410 }); 411 412 auto rules = app.get_rules(); 413 for (auto& rule : rules) { 414 LOG(DEBUG) << "Static route: " << rule; 415 } 416 417 // app.port(18080).ssl(std::move(get_ssl_context(ssl_pem_file))).concurrency(4).run(); 418 app.port(18080).concurrency(4).run(); 419 } 420