blob: c5c3eedd9a3ae9f011d3529b85b3d89d93aeeeae [file] [log] [blame]
Ed Tanousf9273472017-02-28 16:05:13 -08001#include "crow/app.h"
Ed Tanousc4771fb2017-03-13 13:39:49 -07002#include "crow/ci_map.h"
Ed Tanousf9273472017-02-28 16:05:13 -08003#include "crow/common.h"
4#include "crow/dumb_timer_queue.h"
5#include "crow/http_connection.h"
Ed Tanousc4771fb2017-03-13 13:39:49 -07006#include "crow/http_parser_merged.h"
Ed Tanousf9273472017-02-28 16:05:13 -08007#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"
Ed Tanousc4771fb2017-03-13 13:39:49 -070016#include "crow/query_string.h"
Ed Tanousf9273472017-02-28 16:05:13 -080017#include "crow/routing.h"
Ed Tanous0fdddb12017-02-28 11:06:34 -080018#include "crow/settings.h"
19#include "crow/socket_adaptors.h"
Ed Tanous0fdddb12017-02-28 11:06:34 -080020#include "crow/utility.h"
Ed Tanous0fdddb12017-02-28 11:06:34 -080021#include "crow/websocket.h"
Ed Tanous0fdddb12017-02-28 11:06:34 -080022
Ed Tanous5f34a9c2017-02-28 12:35:13 -080023#include "color_cout_g3_sink.hpp"
Ed Tanousf9273472017-02-28 16:05:13 -080024#include "token_authorization_middleware.hpp"
Ed Tanous99923322017-03-03 14:21:24 -080025#include "webassets.hpp"
Ed Tanousf9273472017-02-28 16:05:13 -080026
Ed Tanous0fdddb12017-02-28 11:06:34 -080027#include <iostream>
Ed Tanousc4771fb2017-03-13 13:39:49 -070028#include <memory>
Ed Tanous0fdddb12017-02-28 11:06:34 -080029#include <string>
Ed Tanousf9273472017-02-28 16:05:13 -080030#include "ssl_key_handler.hpp"
Ed Tanous0fdddb12017-02-28 11:06:34 -080031
Ed Tanousc4771fb2017-03-13 13:39:49 -070032#include <boost/endian/arithmetic.hpp>
33
34#include <boost/asio.hpp>
35
36#include <unordered_set>
Ed Tanous904063f2017-03-02 16:48:24 -080037#include <webassets.hpp>
38
Ed Tanousc4771fb2017-03-13 13:39:49 -070039static const std::string rfb_3_3_version_string = "RFB 003.003\n";
40static const std::string rfb_3_7_version_string = "RFB 003.007\n";
41static const std::string rfb_3_8_version_string = "RFB 003.008\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080042
Ed Tanousc4771fb2017-03-13 13:39:49 -070043enum class RfbAuthScheme : uint8_t { connection_failed = 0, no_authentication = 1, vnc_authentication = 2 };
Ed Tanous9b65f1f2017-03-07 15:17:13 -080044
Ed Tanousc4771fb2017-03-13 13:39:49 -070045struct 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
61struct 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
68enum 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
78struct 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
85struct 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
93struct 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
100struct 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
106struct client_cut_text_message {
107 std::vector<uint8_t> data;
108};
109
110enum 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
138struct 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
147struct framebuffer_update_message {
148 boost::endian::big_uint8_t message_type;
149
150 std::vector<framebuffer_rectangle> rectangles;
151};
152
153std::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();
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800158 }
159
Ed Tanousc4771fb2017-03-13 13:39:49 -0700160 std::string serialized(vector_size, 0);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800161
Ed Tanousc4771fb2017-03-13 13:39:49 -0700162 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);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800168
Ed Tanousc4771fb2017-03-13 13:39:49 -0700169 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();
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800177 }
Ed Tanousc4771fb2017-03-13 13:39:49 -0700178
179 return serialized;
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800180}
181
Ed Tanousc4771fb2017-03-13 13:39:49 -0700182enum class VncState { UNSTARTED, AWAITING_CLIENT_VERSION, AWAITING_CLIENT_AUTH_METHOD, AWAITING_CLIENT_INIT_MESSAGE, MAIN_LOOP };
183
184class connection_metadata {
185 public:
186 connection_metadata(void) : vnc_state(VncState::AWAITING_CLIENT_VERSION){};
187
188 VncState vnc_state;
189};
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800190
Ed Tanous99923322017-03-03 14:21:24 -0800191int main(int argc, char** argv) {
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800192 auto worker(g3::LogWorker::createLogWorker());
193
Ed Tanous99923322017-03-03 14:21:24 -0800194 auto handle = worker->addDefaultLogger(argv[0], "/tmp/");
195 g3::initializeLogging(worker.get());
Ed Tanous99923322017-03-03 14:21:24 -0800196 auto sink_handle = worker->addSink(std::make_unique<crow::ColorCoutSink>(), &crow::ColorCoutSink::ReceiveLogMessage);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800197
Ed Tanous99923322017-03-03 14:21:24 -0800198 std::string ssl_pem_file("server.pem");
199 ensuressl::ensure_openssl_key_present_and_valid(ssl_pem_file);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800200
Ed Tanous99923322017-03-03 14:21:24 -0800201 crow::App<crow::TokenAuthorizationMiddleware> app;
Ed Tanous99923322017-03-03 14:21:24 -0800202 crow::webassets::request_routes(app);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800203
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800204 crow::logger::setLogLevel(crow::LogLevel::INFO);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800205
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800206 CROW_ROUTE(app, "/routes")
207 ([&app]() {
208 crow::json::wvalue routes;
209
210 routes["routes"] = app.get_rules();
211 return routes;
212 });
213
Ed Tanousc4771fb2017-03-13 13:39:49 -0700214 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();
Ed Tanous0fdddb12017-02-28 11:06:34 -0800419}