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