BF2MC-Matchmaker
websocket/client.cpp
1 #include <unistd.h>
2 #include <iostream>
3 #include <iomanip>
4 #include <fstream>
5 #include <regex>
6 #include <openssl/sha.h>
7 #include <openssl/bio.h>
8 #include <openssl/evp.h>
9 #include <openssl/buffer.h>
10 
11 #include <settings.h>
12 #include <logger.h>
13 #include <server.h>
14 #include <gpcm/client.h>
15 #include <globals.h>
16 #include <database.h>
17 #include <util.h>
18 #include <atomizes.hpp>
19 #include <service/file_system.h>
20 
21 #include <websocket/client.h>
22 
23 using namespace atomizes;
24 
25 typedef void (Websocket::Client::*RequestActionFunc)(const atomizes::HTTPMessage&, const std::string&, const Util::Url::Variables&);
26 
27 static std::map<std::string, RequestActionFunc> mRequestActions =
28 {
29 
30 };
31 
32 Websocket::Client::Client(int socket, struct sockaddr_in address)
33 {
34  this->_socket = socket;
35  this->_address = address;
36  this->UpdateLastRecievedTime();
37 }
38 
39 Websocket::Client::~Client()
40 {
41  this->Disconnect();
42 }
43 
44 void Websocket::Client::Listen()
45 {
46  while(true)
47  {
48  std::vector<char> buffer(4096, 0);
49 
50  // Read socket
51  int recv_size = read(this->_socket, &(buffer[0]), 4096);
52 
53  // If error or no data is recieved we end the connection
54  if(recv_size <= 0)
55  {
56  this->Disconnect();
57 
58  return;
59  }
60 
61  // Resize buffer
62  buffer.resize(recv_size);
63 
64  this->UpdateLastRecievedTime();
65 
66  // Trigger onRequest event
67  this->onRequest(buffer);
68  }
69 
70  this->Disconnect();
71 }
72 
73 void Websocket::Client::Disconnect()
74 {
75  this->Close();
76  g_webserver_server->onClientDisconnect(*this);
77 }
78 
79 void Websocket::Client::Send(const atomizes::HTTPMessage &http_response) const
80 {
81  this->Net::Socket::Send(http_response.ToString());
82 }
83 
84 // Events
85 
86 void Websocket::Client::onRequest(const std::vector<char>& buffer)
87 {
88  if(Util::Buffer::ToString(buffer).substr(0, 3) == "GET")
89  {
90  HTTPMessage http_response;
91  HTTPMessageParser http_parser;
92  HTTPMessage http_request;
93 
94  http_parser.Parse(&http_request, &(buffer[0]));
95 
96  std::string websocket_key = http_request.GetHeader("Sec-WebSocket-Key");
97  std::string websocket_accept_key = this->_GetSocketAcceptKey(websocket_key);
98 
99  Logger::debug("websocket_key = " + websocket_key);
100  Logger::debug("websocket_accept_key = " + websocket_accept_key);
101 
102  http_response.SetStatusCode(101);
103  http_response.SetHeader("Upgrade", "websocket");
104  http_response.SetHeader("Connection", "Upgrade");
105  http_response.SetHeader("Sec-WebSocket-Accept", websocket_accept_key);
106  // No compression support
107  //http_response.SetHeader("Sec-WebSocket-Extensions", "permessage-deflate");
108  http_response.SetHeader("Server", "BF2MC-Matchmaker");
109 
110  this->Send(http_response);
111 
112  this->_LogTransaction("<--", "HTTP/1.1 101 Switching Protocols");
113  }
114  else
115  {
116  std::vector<char> key(buffer.begin() + 4, buffer.begin() + 8);
117  std::vector<char> payload(buffer.begin() + 8, buffer.end());
118 
119  std::vector<char> unmask_data = this->_UnmaskPayload(key, payload);
120 
121  Logger::debug("unmask_data = " + Util::Buffer::ToString(unmask_data));
122 
123  /*
124  auto it = mRequestActions.find(action);
125  if (it != mRequestActions.end())
126  {
127  // Get Function address
128  RequestActionFunc func = it->second;
129 
130  // Execute action function with class object.
131  //(this->*(func))(http_request, url_base, url_variables);
132  }
133  else
134  {
135  Logger::warning("action \"" + url_base + "\" not implemented!", Server::Type::Websocket);
136  }
137  */
138  }
139 }
140 
141 // Private functions
142 
143 void Websocket::Client::_LogTransaction(const std::string& direction, const std::string& response) const
144 {
145  std::shared_lock<std::shared_mutex> guard(g_settings_mutex); // settings lock (read)
146 
147  if ((g_logger_mode & Logger::Mode::Development) == 0)
148  {
149  return;
150  }
151 
152  bool show_console = (g_settings["websocket"]["show_requests"].asBool() && direction == "-->") ||
153  (g_settings["websocket"]["show_responses"].asBool() && direction == "<--");
154 
155  Logger::info(this->GetAddress() + " " + direction + " " + response,
156  Server::Type::Websocket, show_console);
157 }
158 
159 std::string Websocket::Client::_GetSocketAcceptKey(const std::string& websocket_key)
160 {
161  std::string GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
162  std::string concatenated = websocket_key + GUID;
163 
164  unsigned char hash[SHA_DIGEST_LENGTH];
165  SHA1(reinterpret_cast<const unsigned char*>(concatenated.c_str()), concatenated.length(), hash);
166 
167  BIO* b64 = BIO_new(BIO_f_base64());
168  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
169  BIO* bio = BIO_new(BIO_s_mem());
170  BIO_push(b64, bio);
171  BIO_write(b64, hash, SHA_DIGEST_LENGTH);
172  BIO_flush(b64);
173 
174  BUF_MEM* bptr;
175  BIO_get_mem_ptr(b64, &bptr);
176 
177  std::string websocket_accept_key(bptr->data, bptr->length - 1); // Exclude the newline character
178 
179  BIO_free_all(b64);
180 
181  return websocket_accept_key + "=";
182 }
183 
184 std::vector<char> Websocket::Client::_UnmaskPayload(const std::vector<char>& key, const std::vector<char>& payload)
185 {
186  std::vector<char> unmask_data;
187 
188  // Unmask the payload data starting from the 9th byte
189  for (size_t i = 0; i < payload.size(); ++i)
190  {
191  unmask_data.push_back(payload[i] ^ key[i % 4]);
192  }
193 
194  return unmask_data;
195 }
196 
void Send(const std::string &msg) const
Sends a message over the socket.
Definition: socket.cpp:80
void onClientDisconnect(const Net::Socket &client)
Called when a client disconnects from the server.
Definition: server.cpp:336