13 #include <browsing/sb_crypt.h>
14 #include <browsing/constants.h>
16 #include <browsing/client.h>
18 typedef void (
Browsing::Client::*RequestActionFunc)(
const std::vector<unsigned char>&);
20 static std::map<uint8_t, RequestActionFunc> mRequestActions =
46 std::vector<unsigned char> buffer(4096, 0);
48 int recv_size = read(this->_socket, &(buffer[0]), 4096);
57 buffer.resize(recv_size);
59 this->UpdateLastRecievedTime();
61 this->_LogTransaction(
"-->", Util::Buffer::ToString(buffer));
64 std::vector<std::vector<unsigned char>> requests;
65 this->_BufferToRequests(buffer, requests);
68 for(
const std::vector<unsigned char>& request : requests)
70 this->onRequest(request);
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]);
100 auto it = mRequestActions.find(action);
101 if (it != mRequestActions.end())
104 RequestActionFunc func = it->second;
107 (this->*(func))(request);
111 Logger::warning(
"action \"" + std::to_string(action) +
"\"not implemented!", Server::Type::Browsing);
117 std::vector<unsigned char> serverListHeaderItems = {
119 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00,
120 0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00,
121 0x67, 0x61, 0x6d, 0x65, 0x76, 0x65, 0x72, 0x00, 0x00,
122 0x68, 0x6f, 0x73, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x00,
123 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x00,
124 0x74, 0x69, 0x6d, 0x65, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x00, 0x00,
125 0x6d, 0x61, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x00,
126 0x6e, 0x75, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x00,
127 0x6d, 0x61, 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x00,
128 0x74, 0x65, 0x61, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x00,
129 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x00, 0x00,
130 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x00,
131 0x63, 0x30, 0x00, 0x00,
132 0x63, 0x31, 0x00, 0x00,
133 0x6e, 0x30, 0x00, 0x00,
134 0x6e, 0x31, 0x00, 0x00,
135 0x63, 0x30, 0x63, 0x00, 0x00,
136 0x63, 0x31, 0x63, 0x00, 0x00,
137 0x6e, 0x72, 0x00, 0x00,
138 0x78, 0x72, 0x00, 0x00,
139 0x66, 0x66, 0x00, 0x00,
140 0x6d, 0x63, 0x00, 0x00,
141 0x67, 0x63, 0x00, 0x00,
142 0x72, 0x63, 0x00, 0x00,
143 0x73, 0x72, 0x00, 0x00,
144 0x6e, 0x69, 0x00, 0x00,
145 0x78, 0x69, 0x00, 0x00,
146 0x71, 0x6d, 0x00, 0x00,
192 if(request.size() < 50)
200 std::string for_gamename, from_gamename;
201 if(!Util::Buffer::ReadString(request, offset, for_gamename) ||
202 !Util::Buffer::ReadString(request, offset, from_gamename))
208 this->_client_challenge.assign(request.begin() + offset, request.begin() + offset + CHALLENGE_CLIENT_LEN);
209 offset += CHALLENGE_CLIENT_LEN;
212 std::string filter, key_list;
213 if(!Util::Buffer::ReadString(request, offset, filter) ||
214 !Util::Buffer::ReadString(request, offset, key_list))
225 if(filter ==
"" or key_list ==
"")
230 Logger::info(this->GetAddress() +
" --> ServerList: " + filter, Server::Type::Browsing);
232 std::vector<unsigned char> response(CHALLENGE_HEADER_LEN, 0x0);
235 this->_insertClientInfo(response);
238 response.insert(response.end(), serverListHeaderItems.begin(), serverListHeaderItems.end());
240 Battlefield::GameServers game_servers;
244 this->_FilterServers(filter, game_servers);
251 if(!game_server.isVerified())
254 this->_insertGameServerFlagIpPort(response, game_server);
258 response.push_back(0x00);
261 response.push_back(0xFF);
262 response.push_back(0xFF);
263 response.push_back(0xFF);
264 response.push_back(0xFF);
266 this->_Encrypt(response);
268 this->Send(response);
270 this->_LogTransaction(
"<--", Util::Buffer::ToString(response));
273 std::vector<unsigned char> ServerInfo_test_response = {
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,
294 0x31, 0x30, 0x30, 0x2e, 0x32, 0x31, 0x36, 0x33, 0x33, 0x35, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61,
298 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x00,
306 0x66, 0x72, 0x61, 0x67, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00,
310 0x67, 0x61, 0x6d, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x00,
311 0x6f, 0x70, 0x65, 0x6e, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x00,
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,
318 0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00,
319 0x63, 0x6f, 0x6e, 0x71, 0x75, 0x65, 0x73, 0x74, 0x00,
322 0x67, 0x61, 0x6d, 0x65, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x00,
323 0x42, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x00,
326 0x67, 0x61, 0x6d, 0x65, 0x76, 0x65, 0x72, 0x00,
327 0x56, 0x31, 0x2e, 0x33, 0x31, 0x61, 0x00,
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,
344 0x68, 0x6f, 0x73, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x00,
345 0x33, 0x36, 0x35, 0x38, 0x00,
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,
356 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x70, 0x6f, 0x72, 0x74, 0x00,
357 0x33, 0x36, 0x35, 0x38, 0x00,
360 0x6d, 0x61, 0x70, 0x00,
361 0x62, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x62, 0x00,
364 0x6d, 0x61, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x00,
368 0x6d, 0x61, 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00,
376 0x6e, 0x61, 0x74, 0x6e, 0x65, 0x67, 0x00,
381 0x2d, 0x32, 0x31, 0x34, 0x37, 0x34, 0x38, 0x33, 0x36, 0x34, 0x38, 0x00,
388 0x6e, 0x75, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00,
392 0x6e, 0x75, 0x6d, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x00,
396 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00,
408 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x00,
409 0x36, 0x35, 0x35, 0x33, 0x36, 0x00,
413 0x72, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x00,
420 0x73, 0x74, 0x61, 0x74, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00,
424 0x74, 0x65, 0x61, 0x6d, 0x66, 0x72, 0x61, 0x67, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00,
428 0x74, 0x65, 0x61, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x00,
432 0x74, 0x69, 0x6d, 0x65, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x00,
433 0x31, 0x34, 0x38, 0x00,
436 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00,
437 0x31, 0x32, 0x30, 0x30, 0x00,
441 0x32, 0x31, 0x34, 0x37, 0x34, 0x38, 0x33, 0x36, 0x34, 0x37, 0x00,
448 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x30, 0x00,
449 0x34, 0x34, 0x37, 0x00,
452 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x30, 0x00,
456 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x31, 0x00,
457 0x34, 0x32, 0x36, 0x00,
460 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x31, 0x00,
468 if(request.size() < 9)
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);
482 Logger::info(this->GetAddress() +
" --> ServerInfo: " + str_ip +
":" + std::to_string(port), Server::Type::Browsing);
485 game_server.SetIp(str_ip);
486 game_server.SetPort(port);
492 if(game_server.GetId() == -1)
497 std::vector<unsigned char> response(CHALLENGE_HEADER_LEN, 0x0);
500 this->_insertClientInfo(response);
503 response.push_back(0x00);
504 response.push_back(0x00);
507 response.push_back(0x02);
509 this->_insertGameServerFlagIpPort(response, game_server);
514 response.insert(response.end(), ServerInfo_test_response.begin(), ServerInfo_test_response.end());
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;
521 this->_Encrypt(response);
523 this->Send(response);
525 this->_LogTransaction(
"<--", Util::Buffer::ToString(response));
531 std::vector<std::vector<unsigned char>>& requests)
533 uint16_t request_size;
536 while(offset < buffer.size())
538 std::vector<unsigned char> request;
540 request_size =
static_cast<uint16_t
>(buffer[offset + 1]) | (
static_cast<uint16_t
>(buffer[offset + 0]) << 8);
542 if(offset + request_size <= buffer.size())
544 request.insert(request.end(), buffer.begin() + offset, buffer.begin() + offset + request_size);
546 requests.push_back(request);
549 offset += request_size;
555 uint8_t client_ip[4];
556 uint16_t client_port = 6500;
558 this->GetIpArray(client_ip);
560 response.insert(response.end(), client_ip, client_ip + 4);
561 response.push_back(client_port / 256);
562 response.push_back(client_port % 256);
573 game_server.GetIpArray(ip);
574 port = game_server.GetPort();
575 flag = game_server.GetFlag();
578 response.push_back(flag);
581 response.insert(response.end(), ip, ip + 4);
583 if(flag & FLAG_NONSTANDARD_PORT)
585 response.push_back(port / 256);
586 response.push_back(port % 256);
589 if(flag & FLAG_PRIVATE_IP)
591 response.insert(response.end(), ip, ip + 4);
594 if(flag & FLAG_NONSTANDARD_PRIVATE_PORT)
596 response.push_back(port / 256);
597 response.push_back(port % 256);
600 if(flag & FLAG_ICMP_IP)
602 response.insert(response.end(), ip, ip + 4);
608 uint8_t server_challenge[CHALLENGE_SERVER_LEN];
609 uint8_t secret[CHALLENGE_CLIENT_LEN];
615 for(
int i = 0; i < CHALLENGE_CRYPT_LEN; i++)
617 response[1 + i] = 0xff;
621 response[CHALLENGE_CRYPT_LEN + 1] = 0xf3;
622 for(
int i = 0; i < CHALLENGE_SERVER_LEN; i++)
624 server_challenge[i] = 0x0;
625 response[CHALLENGE_CRYPT_LEN + 2 + i] = server_challenge[i];
629 for(
int i = 0; i < CHALLENGE_CLIENT_LEN; i++)
631 secret[i] = this->_client_challenge[i];
635 for (uint32_t i = 0 ; i < CHALLENGE_SERVER_LEN; i++)
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;
640 secret[index] ^= value;
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);
650 std::shared_lock<std::shared_mutex> guard2(g_settings_mutex);
652 if ((g_logger_mode & Logger::Mode::Development) == 0)
657 bool show_console = (g_settings[
"browsing"][
"show_requests"].asBool() && direction ==
"-->") ||
658 (g_settings[
"browsing"][
"show_responses"].asBool() && direction ==
"<--");
660 Logger::info(this->GetAddress() +
" " + direction +
" " + response,
661 Server::Type::Browsing, show_console);
666 Battlefield::GameServers::const_iterator game_server_it;
668 for(game_server_it = game_servers.begin(); game_server_it != game_servers.end(); ++game_server_it)
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)
684 game_servers.erase(game_server_it);
698 pattern = std::regex(R
"(gamever='([^']+))");
699 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
701 std::string gamever = matches[1];
703 if(!(game_server.GetGameVersion() == gamever))
716 pattern = std::regex(R
"(\(region \& (\d+)\)!=0)");
717 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
719 uint64_t v = std::stoull(matches[1]);
721 if(!((game_server.GetRegion() & v) != 0))
726 pattern = std::regex(R
"(\(region \& (\d+)\)=0)");
727 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
729 uint64_t v = std::stoull(matches[1]);
731 if(!((game_server.GetRegion() & v) == 0))
744 pattern = std::regex(R
"(numplayers>=(\d+))");
745 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
747 uint8_t numplayers = std::stoul(matches[1]);
749 if(!(game_server.GetNumPlayers() >= numplayers))
754 pattern = std::regex(R
"(numplayers<=(\d+))");
755 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
757 uint8_t numplayers = std::stoul(matches[1]);
759 if(!(game_server.GetNumPlayers() <= numplayers))
772 pattern = std::regex(R
"(gametype='([^']+))");
773 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
775 std::string gametype = matches[1];
777 if(!(game_server.GetGameType() == gametype))
790 pattern = std::regex(R
"(mapname=(\d+))");
791 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
793 uint8_t mapname = std::stoul(matches[1]);
795 if(!(game_server.GetMapName() == mapname))
808 pattern = std::regex(R
"(sr=(\d+))");
809 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
811 uint8_t sr = std::stoul(matches[1]);
813 if(!(game_server.GetStatsTracking() == sr))
826 pattern = std::regex(R
"(rc=(\d+) or teamplay!=(\d+))");
827 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
834 pattern = std::regex(R
"(rc=(\d+))");
835 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
837 uint8_t rc = std::stoul(matches[1]);
839 if(!(game_server.GetReconfigurable() == rc))
853 pattern = std::regex(R
"(rc=(\d+) or teamplay!=(\d+))");
854 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
856 uint8_t rc = std::stoul(matches[1]);
857 uint8_t teamplay = std::stoul(matches[2]);
859 if(!(game_server.GetReconfigurable() == rc || game_server.GetTeamplay() != teamplay))
865 pattern = std::regex(R
"(teamplay=(\d+))");
866 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
868 uint8_t teamplay = std::stoul(matches[1]);
870 if(!(game_server.GetTeamplay() == teamplay))
875 pattern = std::regex(R
"(teamplay!=(\d+))");
876 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
878 uint8_t teamplay = std::stoul(matches[1]);
880 if(!(game_server.GetTeamplay() != teamplay))
894 pattern = std::regex(R
"(\(c0=(\d+) or c1=(\d+)\))");
895 if (std::regex_search(filter, matches, pattern) && matches.size() >= 2)
897 int32_t c0 = std::stoi(matches[1]);
898 int32_t c1 = std::stoi(matches[2]);
900 if(!(game_server.GetClan1Id() == c0 || game_server.GetClan2Id() == c1))
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)
908 int32_t c0 = std::stoi(matches[1]);
909 int32_t c1 = std::stoi(matches[2]);
911 if(!(game_server.GetClan1Id() == -1 || game_server.GetClan2Id() == -1 ||
912 game_server.GetClan1Id() == c0 || game_server.GetClan2Id() == c1))
928 std::this_thread::sleep_for (std::chrono::seconds(60));
931 auto target_time = std::chrono::system_clock::now() - std::chrono::minutes(1);
934 for(std::shared_ptr<Net::Socket> client : g_browsing_server->
GetClients())
937 std::chrono::system_clock::time_point last_recieved = client.get()->GetLastRecievedTime();
940 if (last_recieved <= target_time)
943 client.get()->Close();
Class representing game server information.
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.
struct sockaddr_in _address
std::vector< std::shared_ptr< Net::Socket > > GetClients()
Get the vector of client sockets connected to this server.
void onClientDisconnect(const Net::Socket &client)
Called when a client disconnects from the server.