xref: /openbmc/bmcweb/src/webserver_main.cpp (revision c4771fb4cecd77272a72f3265d19096c83e5e8e9)
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