BF2MC-Matchmaker
browsing/client.cpp
1 #include <unistd.h>
2 #include <iostream>
3 #include <iomanip>
4 #include <regex>
5 #include <thread>
6 
7 #include <settings.h>
8 #include <logger.h>
9 #include <server.h>
10 #include <globals.h>
11 #include <util.h>
12 #include <database.h>
13 #include <browsing/sb_crypt.h>
14 #include <browsing/constants.h>
15 
16 #include <browsing/client.h>
17 
18 typedef void (Browsing::Client::*RequestActionFunc)(const std::vector<unsigned char>&);
19 
20 static std::map<uint8_t, RequestActionFunc> mRequestActions =
21 {
22  { REQUEST_SERVER_LIST, &Browsing::Client::requestServerList },
23  { REQUEST_SERVER_INFO, &Browsing::Client::requestServerInfo },
24  //{ REQUEST_SEND_MESSAGE, &Browsing::Client::requestSendMessage },
25  //{ REQUEST_KEEPALIVE, &Browsing::Client::requestKeepAlive },
26  //{ REQUEST_MAPLOOP, &Browsing::Client::requestMapLoop },
27  //{ REQUEST_PLAYERSEARCH, &Browsing::Client::requestPlayerSearch },
28 };
29 
30 Browsing::Client::Client(int socket, struct sockaddr_in address)
31 {
32  this->_socket = socket;
33  this->_address = address;
34  this->UpdateLastRecievedTime();
35 }
36 
38 {
39  this->Disconnect();
40 }
41 
43 {
44  while(true)
45  {
46  std::vector<unsigned char> buffer(4096, 0);
47 
48  int recv_size = read(this->_socket, &(buffer[0]), 4096);
49 
50  // If error or no data is recieved we end the connection
51  if(recv_size <= 0)
52  {
53  break;
54  }
55 
56  // Resize buffer
57  buffer.resize(recv_size);
58 
59  this->UpdateLastRecievedTime();
60 
61  this->_LogTransaction("-->", Util::Buffer::ToString(buffer));
62 
63  // Extract requests
64  std::vector<std::vector<unsigned char>> requests;
65  this->_BufferToRequests(buffer, requests);
66 
67  // Iterate through requests
68  for(const std::vector<unsigned char>& request : requests)
69  {
70  this->onRequest(request);
71  }
72 
73  // Debug
74  //std::stringstream ss;
75  //ss.str("");
76  //for(int i = 0; i < buffer.size(); i++)
77  //{
78  // ss << std::hex << std::setfill('0') << std::setw(2) << (int)(buffer[i]);
79  //}
80  //Logger::debug("buffer = " + ss.str());
81  }
82 
83  this->Disconnect();
84 }
85 
87 {
88  this->Close();
89  g_browsing_server->onClientDisconnect(*this);
90 }
91 
92 // Events
93 
94 void Browsing::Client::onRequest(const std::vector<unsigned char>& request)
95 {
96  uint16_t size = static_cast<uint16_t>(request[1]) | (static_cast<uint16_t>(request[0]) << 8);
97  uint8_t action = static_cast<uint8_t>(request[2]);
98 
99  // Find function
100  auto it = mRequestActions.find(action);
101  if (it != mRequestActions.end())
102  {
103  // Get Function address
104  RequestActionFunc func = it->second;
105 
106  // Execute action function with class object.
107  (this->*(func))(request);
108  }
109  else
110  {
111  Logger::warning("action \"" + std::to_string(action) + "\"not implemented!", Server::Type::Browsing);
112 
113  this->Disconnect();
114  }
115 }
116 
117 std::vector<unsigned char> serverListHeaderItems = {
118  0x1c, 0x00, // Total header items: 28
119  0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, // hostname
120  0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // gametype
121  0x67, 0x61, 0x6d, 0x65, 0x76, 0x65, 0x72, 0x00, 0x00, // gamever
122  0x68, 0x6f, 0x73, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x00, // hostport
123  0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x00, // timelimit
124  0x74, 0x69, 0x6d, 0x65, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x00, 0x00, // timeelapsed
125  0x6d, 0x61, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00, // mapname
126  0x6e, 0x75, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x00, // numplayers
127  0x6d, 0x61, 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x00, // maxplayers
128  0x74, 0x65, 0x61, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x00, // teamplay
129  0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x00, 0x00, // team_t
130  0x70, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x00, // ping_
131  0x63, 0x30, 0x00, 0x00, // c0
132  0x63, 0x31, 0x00, 0x00, // c1
133  0x6e, 0x30, 0x00, 0x00, // n0
134  0x6e, 0x31, 0x00, 0x00, // n1
135  0x63, 0x30, 0x63, 0x00, 0x00, // c0c
136  0x63, 0x31, 0x63, 0x00, 0x00, // c1c
137  0x6e, 0x72, 0x00, 0x00, // nr
138  0x78, 0x72, 0x00, 0x00, // xr
139  0x66, 0x66, 0x00, 0x00, // ff
140  0x6d, 0x63, 0x00, 0x00, // mc
141  0x67, 0x63, 0x00, 0x00, // gc
142  0x72, 0x63, 0x00, 0x00, // rc
143  0x73, 0x72, 0x00, 0x00, // sr
144  0x6e, 0x69, 0x00, 0x00, // ni
145  0x78, 0x69, 0x00, 0x00, // xi
146  0x71, 0x6d, 0x00, 0x00, // qm
147 };
148 
149 /*
150  filter:
151  gamever='V1.31a' and (teamplay=0 and numplayers<=23 and numplayers>=0 and numplayers!=maxplayers and nr<=20 and xr>=20)and (region & 1)=0
152 
153  Min/Max Players:
154  Max: numplayers<=22
155  Min: numplayers>=1
156  Game Mode:
157  Conquest: gc=0 and gametype='conquest'
158  CTF: gc=0 and gametype='capturetheflag'
159  Maps:
160  Backstab: mapname=0 and mc=1
161  DeadlyPass: mapname=1 and mc=1
162  BridgeToFar: mapname=2 and mc=1
163  Dammage: mapname=3 and mc=1
164  HarborEdge: mapname=4 and mc=1
165  Honor: mapname=5 and mc=1
166  LittleBigEye: mapname=6 and mc=1
167  MissleCrisis mapname=7 and mc=1
168  ColdFront mapname=8 and mc=1
169  RussianBorder: mapname=9 and mc=1
170  SpecialOp: mapname=10 and mc=1
171  TheBlackGold mapname=11 and mc=1
172  TheNest mapname=12 and mc=1
173  [All Maps Playlist] mc=2
174  [Assualt Maps] mc=3
175  [Incursion Maps] mc=4
176  [Domination Maps] mc=5
177  Ranked:
178  On: (nr>1 or xr<20)
179  Off: nr=1 and xr=20
180  Stats Tracking:
181  On: sr=1
182  Off: sr=0
183 
184  Clan fight:
185  gamever='V1.31a' and (teamplay!=0 and (c0=-1 or c1=-1 or c0=19 or c1=19) and maxplayers<=24 and numplayers!=maxplayers and nr<=20 and xr>=20)and (region & 1)=0
186 
187  gamever='V1.31a' and (teamplay!=0 and (c0=-1 or c1=-1 or c0=21 or c1=21) and maxplayers<=24 and numplayers!=maxplayers and nr<=1 and xr>=1 and sr=1)and (region & 1)=0
188 */
189 
190 void Browsing::Client::requestServerList(const std::vector<unsigned char>& request)
191 {
192  if(request.size() < 50)
193  {
194  return;
195  }
196 
197  size_t offset = 9;
198 
199  // Read for_gamename and from_gamename
200  std::string for_gamename, from_gamename;
201  if(!Util::Buffer::ReadString(request, offset, for_gamename) ||
202  !Util::Buffer::ReadString(request, offset, from_gamename))
203  {
204  return;
205  }
206 
207  // Copy client_challenge
208  this->_client_challenge.assign(request.begin() + offset, request.begin() + offset + CHALLENGE_CLIENT_LEN);
209  offset += CHALLENGE_CLIENT_LEN;
210 
211  // Read filter and key_list
212  std::string filter, key_list;
213  if(!Util::Buffer::ReadString(request, offset, filter) ||
214  !Util::Buffer::ReadString(request, offset, key_list))
215  {
216  return;
217  }
218 
219  // Debug
220  //Logger::debug("for_gamename = " + for_gamename);
221  //Logger::debug("from_gamename = " + from_gamename);
222  //Logger::debug("filter = " + filter);
223  //Logger::debug("key_list = " + key_list);
224 
225  if(filter == "" or key_list == "")
226  {
227  return;
228  }
229 
230  Logger::info(this->GetAddress() + " --> ServerList: " + filter, Server::Type::Browsing);
231 
232  std::vector<unsigned char> response(CHALLENGE_HEADER_LEN, 0x0);
233 
234  // Copy client information
235  this->_insertClientInfo(response);
236 
237  // Copy over the header items
238  response.insert(response.end(), serverListHeaderItems.begin(), serverListHeaderItems.end());
239 
240  Battlefield::GameServers game_servers;
241  g_database->queryGameServers(game_servers);
242 
243  // Filter Game servers
244  this->_FilterServers(filter, game_servers);
245 
246  for(Battlefield::GameServer game_server : game_servers)
247  {
248  //game_server.useExample();
249  //game_server.Debug();
250 
251  if(!game_server.isVerified())
252  continue;
253 
254  this->_insertGameServerFlagIpPort(response, game_server);
255  }
256 
257  // End data
258  response.push_back(0x00); // Empty flag means end of servers
259 
260  // Add to data to say confirm decryption went correctly
261  response.push_back(0xFF);
262  response.push_back(0xFF);
263  response.push_back(0xFF);
264  response.push_back(0xFF);
265 
266  this->_Encrypt(response);
267 
268  this->Send(response);
269 
270  this->_LogTransaction("<--", Util::Buffer::ToString(response));
271 }
272 
273 std::vector<unsigned char> ServerInfo_test_response = {
274  //0x05, 0xa8, // Total Bytes to read
275  //0x02, // ???
276 
277  // Server information
278  //0xbb, // flags: FLAG_UNSOLICITED_UDP | FLAG_PRIVATE_IP | FLAG_ICMP_IP |
279  // FLAG_NONSTANDARD_PORT | FLAG_NONSTANDARD_PRIVATE_PORT |
280  // FLAG_HAS_FULL_RULES
281  //0x56, 0x57, 0x8B, 0xEB, // wan ip: 78.47.184.23
282  //0x0e, 0x4a, // FLAG_NONSTANDARD_PORT -> wan port: 3658
283  //0x56, 0x57, 0x8B, 0xEB, // FLAG_PRIVATE_IP -> localip0 ip: 78.47.184.23
284  //0x0e, 0x4a, // FLAG_NONSTANDARD_PRIVATE_PORT -> localport: 3658
285  //0x56, 0x57, 0x8B, 0xEB, // FLAG_ICMP_IP -> icmp ip: 78.47.184.23
286 
287  // backend_id = bfield1942ps2:1508534:
288  0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x69, 0x64, 0x00,
289  0x62, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x31, 0x39, 0x34, 0x32, 0x70, 0x73, 0x32, 0x3a, 0x31, 0x35,
290  0x30, 0x38, 0x35, 0x33, 0x34, 0x3a, 0x00,
291 
292  // cl = 100.216335.alpha
293  0x63, 0x6c, 0x00,
294  0x31, 0x30, 0x30, 0x2e, 0x32, 0x31, 0x36, 0x33, 0x33, 0x35, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61,
295  0x00,
296 
297  // country = DE
298  0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x00,
299  0x44, 0x45, 0x00,
300 
301  // ff = 1
302  0x66, 0x66, 0x00,
303  0x31, 0x00,
304 
305  // fraglimit = 0
306  0x66, 0x72, 0x61, 0x67, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00,
307  0x30, 0x00,
308 
309  // gamemode = openplaying
310  0x67, 0x61, 0x6d, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x00,
311  0x6f, 0x70, 0x65, 0x6e, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x00,
312 
313  // gamename = bfield1942ps2
314  0x67, 0x61, 0x6d, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00,
315  0x62, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x31, 0x39, 0x34, 0x32, 0x70, 0x73, 0x32, 0x00,
316 
317  // gametype = conquest
318  0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00,
319  0x63, 0x6f, 0x6e, 0x71, 0x75, 0x65, 0x73, 0x74, 0x00,
320 
321  // gamevariant = Border
322  0x67, 0x61, 0x6d, 0x65, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x00,
323  0x42, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x00,
324 
325  // gamever = V1.31a
326  0x67, 0x61, 0x6d, 0x65, 0x76, 0x65, 0x72, 0x00,
327  0x56, 0x31, 0x2e, 0x33, 0x31, 0x61, 0x00,
328 
329  // gc = 0
330  0x67, 0x63, 0x00,
331  0x30, 0x00,
332 
333  // hostname = [CQ]BF2MC-SERVER1
334  //0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x00,
335  //0x5b, 0x43, 0x51, 0x5d, 0x42, 0x46, 0x32, 0x4d, 0x43, 0x2d, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52,
336  //0x31, 0x00,
337 
338  // hostname = [CQ]BF2MC-IamLupo
339  0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x00,
340  0x5B, 0x43, 0x51, 0x5D, 0x42, 0x46, 0x32, 0x4D, 0x43, 0x2D, 0x49, 0x61, 0x6D, 0x4C, 0x75, 0x70,
341  0x6F, 0x00,
342 
343  // hostport = 3658
344  0x68, 0x6f, 0x73, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x00,
345  0x33, 0x36, 0x35, 0x38, 0x00,
346 
347  // localip0 = 78.47.184.23
348  //0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x70, 0x30, 0x00,
349  //0x37, 0x38, 0x2e, 0x34, 0x37, 0x2e, 0x31, 0x38, 0x34, 0x2e, 0x32, 0x33, 0x00,
350 
351  // localip0 = 86.87.139.235
352  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x70, 0x30, 0x00,
353  0x38, 0x36, 0x2E, 0x38, 0x37, 0x2E, 0x31, 0x33, 0x39, 0x2E, 0x32, 0x33, 0x35, 0x00,
354 
355  // localport = 3658
356  0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x70, 0x6f, 0x72, 0x74, 0x00,
357  0x33, 0x36, 0x35, 0x38, 0x00,
358 
359  // map = backstab
360  0x6d, 0x61, 0x70, 0x00,
361  0x62, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x62, 0x00,
362 
363  // mapname = 0
364  0x6d, 0x61, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x00,
365  0x30, 0x00,
366 
367  // maxplayers = 24
368  0x6d, 0x61, 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00,
369  0x32, 0x34, 0x00,
370 
371  // mc = 1
372  0x6d, 0x63, 0x00,
373  0x31, 0x00,
374 
375  // natneg = 1
376  0x6e, 0x61, 0x74, 0x6e, 0x65, 0x67, 0x00,
377  0x31, 0x00,
378 
379  // ni = -2147483648
380  0x6e, 0x69, 0x00,
381  0x2d, 0x32, 0x31, 0x34, 0x37, 0x34, 0x38, 0x33, 0x36, 0x34, 0x38, 0x00,
382 
383  // nr = 1
384  0x6e, 0x72, 0x00,
385  0x31, 0x00,
386 
387  // numplayers = 9
388  0x6e, 0x75, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00,
389  0x39, 0x00,
390 
391  // numteams = 2
392  0x6e, 0x75, 0x6d, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x00,
393  0x32, 0x00,
394 
395  // password = 0
396  0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00,
397  0x30, 0x00,
398 
399  // qm = 0
400  0x71, 0x6d, 0x00,
401  0x30, 0x00,
402 
403  // rc = 0
404  0x72, 0x63, 0x00,
405  0x30, 0x00,
406 
407  // region = 65536
408  0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x00,
409  0x36, 0x35, 0x35, 0x33, 0x36, 0x00,
410 
411  // rv = retail
412  0x72, 0x76, 0x00,
413  0x72, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x00,
414 
415  // sr = 1
416  0x73, 0x72, 0x00,
417  0x31, 0x00,
418 
419  // statechanged = 1
420  0x73, 0x74, 0x61, 0x74, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00,
421  0x31, 0x00,
422 
423  // teamfraglimit = 0
424  0x74, 0x65, 0x61, 0x6d, 0x66, 0x72, 0x61, 0x67, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00,
425  0x30, 0x00,
426 
427  // teamplay = 0
428  0x74, 0x65, 0x61, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x00,
429  0x30, 0x00,
430 
431  // timeelapsed = 148
432  0x74, 0x69, 0x6d, 0x65, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x00,
433  0x31, 0x34, 0x38, 0x00,
434 
435  // timelimit = 1200
436  0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00,
437  0x31, 0x32, 0x30, 0x30, 0x00,
438 
439  // xi = 2147483647
440  0x78, 0x69, 0x00,
441  0x32, 0x31, 0x34, 0x37, 0x34, 0x38, 0x33, 0x36, 0x34, 0x37, 0x00,
442 
443  // xr = 20
444  0x78, 0x72, 0x00,
445  0x32, 0x30, 0x00,
446 
447  // score_t0 = 447
448  0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x30, 0x00,
449  0x34, 0x34, 0x37, 0x00,
450 
451  // team_t0 = AC
452  0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x30, 0x00,
453  0x41, 0x43, 0x00,
454 
455  // score_t1 = 426
456  0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x31, 0x00,
457  0x34, 0x32, 0x36, 0x00,
458 
459  // team_t1 = US
460  0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x31, 0x00,
461  0x55, 0x53, 0x00,
462 
463  0x00
464 };
465 
466 void Browsing::Client::requestServerInfo(const std::vector<unsigned char>& request)
467 {
468  if(request.size() < 9)
469  {
470  return;
471  }
472 
473  uint8_t ip[4] = { request[3], request[4], request[5], request[6] };
474  std::string str_ip = std::to_string(ip[0]) + "." + std::to_string(ip[1]) + "." +
475  std::to_string(ip[2]) + "." + std::to_string(ip[3]);
476  uint16_t port = static_cast<uint16_t>(request[8]) | (static_cast<uint16_t>(request[7]) << 8);
477 
478  // Debug
479  //Logger::debug("ip = " + str_ip);
480  //Logger::debug("port = " + std::to_string(port));
481 
482  Logger::info(this->GetAddress() + " --> ServerInfo: " + str_ip + ":" + std::to_string(port), Server::Type::Browsing);
483 
484  Battlefield::GameServer game_server;
485  game_server.SetIp(str_ip);
486  game_server.SetPort(port);
487 
488  // Query the database
489  g_database->queryGameServerByIpAndPort(game_server);
490 
491  // No results found
492  if(game_server.GetId() == -1)
493  {
494  return; // Server not found
495  }
496 
497  std::vector<unsigned char> response(CHALLENGE_HEADER_LEN, 0x0);
498 
499  // Copy client information
500  this->_insertClientInfo(response);
501 
502  // Push unknown byte size. Needs to be patched later on
503  response.push_back(0x00);
504  response.push_back(0x00);
505 
506  // Push response code? MAGIC!!!
507  response.push_back(0x02);
508 
509  this->_insertGameServerFlagIpPort(response, game_server);
510 
511  //response.push_back(0x00);
512 
513  // Copy over test response
514  response.insert(response.end(), ServerInfo_test_response.begin(), ServerInfo_test_response.end());
515 
516  // Patch bytes to read
517  uint16_t response_size = ServerInfo_test_response.size() + 20;
518  response[CHALLENGE_HEADER_LEN + 6] = response_size / 256;
519  response[CHALLENGE_HEADER_LEN + 7] = response_size % 256;
520 
521  this->_Encrypt(response);
522 
523  this->Send(response);
524 
525  this->_LogTransaction("<--", Util::Buffer::ToString(response));
526 }
527 
528 // Private functions
529 
530 void Browsing::Client::_BufferToRequests(const std::vector<unsigned char>& buffer,
531  std::vector<std::vector<unsigned char>>& requests)
532 {
533  uint16_t request_size;
534  size_t offset = 0;
535 
536  while(offset < buffer.size())
537  {
538  std::vector<unsigned char> request;
539 
540  request_size = static_cast<uint16_t>(buffer[offset + 1]) | (static_cast<uint16_t>(buffer[offset + 0]) << 8);
541 
542  if(offset + request_size <= buffer.size())
543  {
544  request.insert(request.end(), buffer.begin() + offset, buffer.begin() + offset + request_size);
545 
546  requests.push_back(request);
547  }
548 
549  offset += request_size;
550  }
551 }
552 
553 void Browsing::Client::_insertClientInfo(std::vector<unsigned char>& response)
554 {
555  uint8_t client_ip[4];
556  uint16_t client_port = 6500;
557 
558  this->GetIpArray(client_ip);
559 
560  response.insert(response.end(), client_ip, client_ip + 4);
561  response.push_back(client_port / 256);
562  response.push_back(client_port % 256);
563 }
564 
565 void Browsing::Client::_insertGameServerFlagIpPort(std::vector<unsigned char>& response,
566  const Battlefield::GameServer& game_server)
567 {
568  uint8_t ip[4];
569  uint16_t port;
570  uint8_t flag;
571 
572  // Get values
573  game_server.GetIpArray(ip);
574  port = game_server.GetPort();
575  flag = game_server.GetFlag();
576 
577  // Server flag
578  response.push_back(flag);
579 
580  // wan ip
581  response.insert(response.end(), ip, ip + 4);
582 
583  if(flag & FLAG_NONSTANDARD_PORT) // -> wan port
584  {
585  response.push_back(port / 256);
586  response.push_back(port % 256);
587  }
588 
589  if(flag & FLAG_PRIVATE_IP) // -> localip0 ip
590  {
591  response.insert(response.end(), ip, ip + 4);
592  }
593 
594  if(flag & FLAG_NONSTANDARD_PRIVATE_PORT) // -> localport
595  {
596  response.push_back(port / 256);
597  response.push_back(port % 256);
598  }
599 
600  if(flag & FLAG_ICMP_IP) // -> icmp ip
601  {
602  response.insert(response.end(), ip, ip + 4);
603  }
604 }
605 
606 void Browsing::Client::_Encrypt(std::vector<unsigned char>& response)
607 {
608  uint8_t server_challenge[CHALLENGE_SERVER_LEN];
609  uint8_t secret[CHALLENGE_CLIENT_LEN];
610 
611  GOACryptState m_crypt_state;
612 
613  // Push Crypt Challenge
614  response[0] = 0xe6; // CHALLENGE_CRYPT_LEN ^ 0xec = 0xe6
615  for(int i = 0; i < CHALLENGE_CRYPT_LEN; i++)
616  {
617  response[1 + i] = 0xff; // Emptry crypto challenge
618  }
619 
620  // Push Server challenge
621  response[CHALLENGE_CRYPT_LEN + 1] = 0xf3; // CHALLENGE_SERVER_LEN ^ 0xea = 0xf3
622  for(int i = 0; i < CHALLENGE_SERVER_LEN; i++)
623  {
624  server_challenge[i] = 0x0;
625  response[CHALLENGE_CRYPT_LEN + 2 + i] = server_challenge[i];
626  }
627 
628  // Copy client challenge to secret
629  for(int i = 0; i < CHALLENGE_CLIENT_LEN; i++)
630  {
631  secret[i] = this->_client_challenge[i];
632  }
633 
634  // Generate secret encryption/decryption key
635  for (uint32_t i = 0 ; i < CHALLENGE_SERVER_LEN; i++)
636  {
637  uint8_t index = (i * SECRET_KEY[i % SECRET_KEY_LEN]) % CHALLENGE_CLIENT_LEN;
638  uint8_t value = (secret[i % CHALLENGE_CLIENT_LEN] ^ server_challenge[i]) & 0xFF;
639 
640  secret[index] ^= value;
641  }
642 
643  // Encrypt data
644  GOACryptInit(&(m_crypt_state), (unsigned char *)(&secret), CHALLENGE_CLIENT_LEN);
645  GOAEncrypt(&(m_crypt_state), &response[CHALLENGE_HEADER_LEN], response.size() - CHALLENGE_HEADER_LEN);
646 }
647 
648 void Browsing::Client::_LogTransaction(const std::string& direction, const std::string& response) const
649 {
650  std::shared_lock<std::shared_mutex> guard2(g_settings_mutex); // settings lock (read)
651 
652  if ((g_logger_mode & Logger::Mode::Development) == 0)
653  {
654  return;
655  }
656 
657  bool show_console = (g_settings["browsing"]["show_requests"].asBool() && direction == "-->") ||
658  (g_settings["browsing"]["show_responses"].asBool() && direction == "<--");
659 
660  Logger::info(this->GetAddress() + " " + direction + " " + response,
661  Server::Type::Browsing, show_console);
662 }
663 
664 void Browsing::Client::_FilterServers(const std::string& filter, Battlefield::GameServers& game_servers)
665 {
666  Battlefield::GameServers::const_iterator game_server_it;
667 
668  for(game_server_it = game_servers.begin(); game_server_it != game_servers.end(); ++game_server_it)
669  {
670  // Check if server needs to be removed
671  if(
672  this->_FilterServerGameVersion(filter, *game_server_it) ||
673  this->_FilterServerRegion(filter, *game_server_it) ||
674  this->_FilterServerNumPlayers(filter, *game_server_it) ||
675  this->_FilterServerGameType(filter, *game_server_it) ||
676  this->_FilterServerMapName(filter, *game_server_it) ||
677  this->_FilterServerStatsTracking(filter, *game_server_it) ||
678  this->_FilterServerReconfigurable(filter, *game_server_it) ||
679  this->_FilterServerClan(filter, *game_server_it) ||
680  this->_FilterServerTeamplay(filter, *game_server_it)
681  )
682  {
683  // Remove the game server out of the list
684  game_servers.erase(game_server_it);
685 
686  // Go back one item value
687  game_server_it--;
688  }
689  }
690 }
691 
692 bool Browsing::Client::_FilterServerGameVersion(const std::string& filter, const Battlefield::GameServer& game_server)
693 {
694  std::regex pattern;
695  std::smatch matches;
696 
697  // Find: gamever='V1.31a'
698  pattern = std::regex(R"(gamever='([^']+))");
699  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
700  {
701  std::string gamever = matches[1];
702 
703  if(!(game_server.GetGameVersion() == gamever))
704  return true; // Remove server
705  }
706 
707  return false; // Don't remove server
708 }
709 
710 bool Browsing::Client::_FilterServerRegion(const std::string& filter, const Battlefield::GameServer& game_server)
711 {
712  std::regex pattern;
713  std::smatch matches;
714 
715  // Find: (region & 1)!=0
716  pattern = std::regex(R"(\‍(region \& (\d+)\)!=0)");
717  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
718  {
719  uint64_t v = std::stoull(matches[1]);
720 
721  if(!((game_server.GetRegion() & v) != 0))
722  return true; // Remove server
723  }
724 
725  // Find: (region & 1)=0
726  pattern = std::regex(R"(\‍(region \& (\d+)\)=0)");
727  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
728  {
729  uint64_t v = std::stoull(matches[1]);
730 
731  if(!((game_server.GetRegion() & v) == 0))
732  return true; // Remove server
733  }
734 
735  return false; // Don't remove server
736 }
737 
738 bool Browsing::Client::_FilterServerNumPlayers(const std::string& filter, const Battlefield::GameServer& game_server)
739 {
740  std::regex pattern;
741  std::smatch matches;
742 
743  // Find: numplayers>=0
744  pattern = std::regex(R"(numplayers>=(\d+))");
745  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
746  {
747  uint8_t numplayers = std::stoul(matches[1]);
748 
749  if(!(game_server.GetNumPlayers() >= numplayers))
750  return true; // Remove server
751  }
752 
753  // Find: numplayers<=23
754  pattern = std::regex(R"(numplayers<=(\d+))");
755  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
756  {
757  uint8_t numplayers = std::stoul(matches[1]);
758 
759  if(!(game_server.GetNumPlayers() <= numplayers))
760  return true; // Remove server
761  }
762 
763  return false; // Don't remove server
764 }
765 
766 bool Browsing::Client::_FilterServerGameType(const std::string& filter, const Battlefield::GameServer& game_server)
767 {
768  std::regex pattern;
769  std::smatch matches;
770 
771  // Find: gametype='conquest'
772  pattern = std::regex(R"(gametype='([^']+))");
773  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
774  {
775  std::string gametype = matches[1];
776 
777  if(!(game_server.GetGameType() == gametype))
778  return true; // Remove server
779  }
780 
781  return false; // Don't remove server
782 }
783 
784 bool Browsing::Client::_FilterServerMapName(const std::string& filter, const Battlefield::GameServer& game_server)
785 {
786  std::regex pattern;
787  std::smatch matches;
788 
789  // Find: mapname=2
790  pattern = std::regex(R"(mapname=(\d+))");
791  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
792  {
793  uint8_t mapname = std::stoul(matches[1]);
794 
795  if(!(game_server.GetMapName() == mapname))
796  return true; // Remove server
797  }
798 
799  return false; // Don't remove server
800 }
801 
802 bool Browsing::Client::_FilterServerStatsTracking(const std::string& filter, const Battlefield::GameServer& game_server)
803 {
804  std::regex pattern;
805  std::smatch matches;
806 
807  // Find: mapname=2
808  pattern = std::regex(R"(sr=(\d+))");
809  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
810  {
811  uint8_t sr = std::stoul(matches[1]);
812 
813  if(!(game_server.GetStatsTracking() == sr))
814  return true; // remove server
815  }
816 
817  return false;
818 }
819 
820 bool Browsing::Client::_FilterServerReconfigurable(const std::string& filter, const Battlefield::GameServer& game_server)
821 {
822  std::regex pattern;
823  std::smatch matches;
824 
825  // Find: (rc=1 or teamplay!=0)
826  pattern = std::regex(R"(rc=(\d+) or teamplay!=(\d+))");
827  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
828  {
829 
830  }
831  else
832  {
833  // Find: rc=2
834  pattern = std::regex(R"(rc=(\d+))");
835  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
836  {
837  uint8_t rc = std::stoul(matches[1]);
838 
839  if(!(game_server.GetReconfigurable() == rc))
840  return true; // Remove server
841  }
842  }
843 
844  return false; // Don't remove server
845 }
846 
847 bool Browsing::Client::_FilterServerTeamplay(const std::string& filter, const Battlefield::GameServer& game_server)
848 {
849  std::regex pattern;
850  std::smatch matches;
851 
852  // Find: (rc=1 or teamplay!=0)
853  pattern = std::regex(R"(rc=(\d+) or teamplay!=(\d+))");
854  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
855  {
856  uint8_t rc = std::stoul(matches[1]);
857  uint8_t teamplay = std::stoul(matches[2]);
858 
859  if(!(game_server.GetReconfigurable() == rc || game_server.GetTeamplay() != teamplay))
860  return true; // Remove server
861  }
862  else
863  {
864  // Find: teamplay=0
865  pattern = std::regex(R"(teamplay=(\d+))");
866  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
867  {
868  uint8_t teamplay = std::stoul(matches[1]);
869 
870  if(!(game_server.GetTeamplay() == teamplay))
871  return true; // Remove server
872  }
873 
874  // Find: teamplay!=0
875  pattern = std::regex(R"(teamplay!=(\d+))");
876  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
877  {
878  uint8_t teamplay = std::stoul(matches[1]);
879 
880  if(!(game_server.GetTeamplay() != teamplay))
881  return true; // Remove server
882  }
883  }
884 
885  return false; // Don't remove server
886 }
887 
888 bool Browsing::Client::_FilterServerClan(const std::string& filter, const Battlefield::GameServer& game_server)
889 {
890  std::regex pattern;
891  std::smatch matches;
892 
893  // Find: (c0=33 or c1=33)
894  pattern = std::regex(R"(\‍(c0=(\d+) or c1=(\d+)\))");
895  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
896  {
897  int32_t c0 = std::stoi(matches[1]);
898  int32_t c1 = std::stoi(matches[2]);
899 
900  if(!(game_server.GetClan1Id() == c0 || game_server.GetClan2Id() == c1))
901  return true; // Remove server
902  }
903 
904  //Find: (c0=-1 or c1=-1 or c0=33 or c1=33)
905  pattern = std::regex(R"(\‍(c0=-1 or c1=-1 or c0=(\d+) or c1=(\d+)\))");
906  if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
907  {
908  int32_t c0 = std::stoi(matches[1]);
909  int32_t c1 = std::stoi(matches[2]);
910 
911  if(!(game_server.GetClan1Id() == -1 || game_server.GetClan2Id() == -1 ||
912  game_server.GetClan1Id() == c0 || game_server.GetClan2Id() == c1))
913  return true; // Remove server
914  }
915 
916  return false; // Don't remove server
917 }
918 
919 // Static functions
920 
922 {
923  Logger::info("Heartbeat started", Server::Browsing);
924 
925  while(true)
926  {
927  // Sleep for 60 seconds
928  std::this_thread::sleep_for (std::chrono::seconds(60));
929 
930  // Calculate the target time (1 minute ago)
931  auto target_time = std::chrono::system_clock::now() - std::chrono::minutes(1);
932 
933  // Iterate through all connected clients
934  for(std::shared_ptr<Net::Socket> client : g_browsing_server->GetClients())
935  {
936  // Get the last received time of the client
937  std::chrono::system_clock::time_point last_recieved = client.get()->GetLastRecievedTime();
938 
939  // Check if the last received time is older than the target time
940  if (last_recieved <= target_time)
941  {
942  // Close the client connection
943  client.get()->Close();
944  }
945  }
946  }
947 }
Class representing game server information.
Definition: gameserver.h:63
Client class for handling browsing requests.
bool _FilterServerReconfigurable(const std::string &filter, const Battlefield::GameServer &game_server)
Checks if a game server matches the filter criteria based on reconfigurability.
bool _FilterServerNumPlayers(const std::string &filter, const Battlefield::GameServer &game_server)
Checks if a game server matches the filter criteria based on number of players.
bool _FilterServerGameVersion(const std::string &filter, const Battlefield::GameServer &game_server)
Checks if a game server matches the filter criteria based on game version.
void Listen()
Listens for incoming requests from the client.
void Disconnect()
Disconnects the client.
bool _FilterServerMapName(const std::string &filter, const Battlefield::GameServer &game_server)
Checks if a game server matches the filter criteria based on map name.
void _FilterServers(const std::string &filter, Battlefield::GameServers &game_servers)
Filters game servers based on the provided filter criteria.
~Client()
Destroys the Client object.
void _BufferToRequests(const std::vector< unsigned char > &buffer, std::vector< std::vector< unsigned char >> &requests)
Converts a buffer to a list of requests.
void _insertClientInfo(std::vector< unsigned char > &response)
Inserts client information into the response.
Client(int socket, struct sockaddr_in address)
Constructs a new Client object.
void requestServerList(const std::vector< unsigned char > &request)
Sends a request for the server list.
bool _FilterServerRegion(const std::string &filter, const Battlefield::GameServer &game_server)
Checks if a game server matches the filter criteria based on region.
bool _FilterServerTeamplay(const std::string &filter, const Battlefield::GameServer &game_server)
Checks if a game server matches the filter criteria based on teamplay.
bool _FilterServerStatsTracking(const std::string &filter, const Battlefield::GameServer &game_server)
Checks if a game server matches the filter criteria based on stats tracking.
void _LogTransaction(const std::string &direction, const std::string &response) const
Logs a transaction.
void _Encrypt(std::vector< unsigned char > &response)
Encrypts the response.
void onRequest(const std::vector< unsigned char > &request)
Event handler for incoming requests.
void requestServerInfo(const std::vector< unsigned char > &request)
Sends a request for server information.
bool _FilterServerClan(const std::string &filter, const Battlefield::GameServer &game_server)
Checks if a game server matches the filter criteria based on clan.
void _insertGameServerFlagIpPort(std::vector< unsigned char > &response, const Battlefield::GameServer &game_server)
Inserts game server flag, IP, and port into the response.
static void Heartbeat()
Heartbeat function to manage client connections.
bool _FilterServerGameType(const std::string &filter, const Battlefield::GameServer &game_server)
Checks if a game server matches the filter criteria based on game type.
bool queryGameServers(Battlefield::GameServers &game_servers)
Queries all game servers from the database.
bool queryGameServerByIpAndPort(Battlefield::GameServer &game_server)
Queries a game server by its IP address and port.
void UpdateLastRecievedTime()
Updates the last received time to the current system time.
Definition: socket.cpp:112
struct sockaddr_in _address
Definition: socket.h:18
int _socket
Definition: socket.h:17
std::vector< std::shared_ptr< Net::Socket > > GetClients()
Get the vector of client sockets connected to this server.
Definition: server.cpp:86
@ Browsing
Definition: server.h:21
void onClientDisconnect(const Net::Socket &client)
Called when a client disconnects from the server.
Definition: server.cpp:336