diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index dcbf82c..8f5da73 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -45,7 +45,6 @@ set(SERVER_SOURCE_FILES src/client/ConnectedClient.cpp src/server/PrecomputedPuzzles.cpp src/client/voice/VoiceClient.cpp - src/client/voice/VoiceClientHandschake.cpp src/client/voice/VoiceClientCommandHandler.cpp src/client/voice/VoiceClientConnectionPacketHandler.cpp src/TS3ServerClientManager.cpp @@ -67,7 +66,6 @@ set(SERVER_SOURCE_FILES src/VirtualServerManager.cpp src/channel/ServerChannel.cpp src/channel/ClientChannelView.cpp - src/Group.cpp src/manager/BanManager.cpp src/client/InternalClient.cpp @@ -81,7 +79,7 @@ set(SERVER_SOURCE_FILES src/ConnectionStatistics.cpp - src/manager/TokeManager.cpp + src/manager/TokenManager.cpp src/terminal/CommandHandler.cpp diff --git a/server/src/DatabaseHelper.cpp b/server/src/DatabaseHelper.cpp index 6eff315..f9c8e41 100644 --- a/server/src/DatabaseHelper.cpp +++ b/server/src/DatabaseHelper.cpp @@ -52,7 +52,7 @@ void DatabaseHelper::tick() { } } -constexpr static std::string_view kSqlBase{"SELECT `client_unique_id`, `client_database_id`, `client_nickname`, `client_created`, `client_last_connected`, `client_total_connections` FROM `clients_server`"}; +constexpr static std::string_view kSqlBase{"SELECT `client_unique_id`, `client_database_id`, `client_nickname`, `client_created`, `client_last_connected`, `client_ip`, `client_total_connections` FROM `clients_server`"}; inline std::deque> query_database_client_info(sql::SqlManager* sql_manager, ServerId server_id, const std::string& query, const std::vector& variables) { std::deque> result{}; @@ -79,6 +79,9 @@ inline std::deque> query_database_client_inf assert(names[index] == "client_last_connected"); entry->client_last_connected = std::chrono::system_clock::time_point{} + std::chrono::seconds{std::stoull(values[index++])}; + assert(names[index] == "client_ip"); + entry->client_ip = values[index++]; + assert(names[index] == "client_total_connections"); entry->client_total_connections = std::stoull(values[index++]); @@ -558,11 +561,11 @@ void DatabaseHelper::saveChannelPermissions(const std::shared_ptr DatabaseHelper::default_properties_client(std::shared_ptr properties, ClientType type){ - if(!properties) - properties = make_shared(); + if(!properties) { + properties = std::make_shared(); + } properties->register_property_type(); - properties->register_property_type(); if(type == ClientType::CLIENT_MUSIC || type == ClientType::CLIENT_QUERY){ (*properties)[property::CLIENT_INPUT_HARDWARE] = true; @@ -707,12 +710,14 @@ std::shared_ptr DatabaseHelper::loadServerProperties(const std: return; } auto weak_server = weak.lock(); - if(!weak_server && serverId != 0) return; + if(!weak_server && serverId != 0) { + return; + } string sql; - if(prop.hasDbReference()) + if(prop.hasDbReference()) { sql = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `key` = :key"; - else { + } else { prop.setDbReference(true); sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)"; } @@ -783,9 +788,9 @@ std::shared_ptr DatabaseHelper::loadPlaylistProperties(const st if(!weak_server && serverId != 0) return; string sql; - if(prop.hasDbReference()) + if(prop.hasDbReference()) { sql = "UPDATE `properties` SET `value` = :value WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `key` = :key"; - else { + } else { prop.setDbReference(true); sql = "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)"; } @@ -903,7 +908,7 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std: } if(entry) { for(const auto& prop : entry->properties) { - if(prop->id == cldbid && (prop->type == property::PROP_TYPE_CLIENT || prop->type == property::PROP_TYPE_CONNECTION)) { + if(prop->id == cldbid && (prop->type == property::PROP_TYPE_CLIENT)) { auto p = (*props)[prop->info]; p = prop->value; p.setModified(true); @@ -915,7 +920,7 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std: } if(!loaded) { - auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND (`type` = :type1 OR `type` = :type2) AND `id` = :id", variable{":serverId", server ? server->getServerId() : 0}, variable{":type1", property::PropertyType::PROP_TYPE_CONNECTION}, variable{":type2", property::PropertyType::PROP_TYPE_CLIENT}, variable{":id", cldbid}); + auto command = sql::command(this->sql, "SELECT `key`, `value`, `type` FROM properties WHERE `serverId` = :serverId AND `type` = :type AND `id` = :id", variable{":serverId", server ? server->getServerId() : 0}, variable{":type", property::PropertyType::PROP_TYPE_CLIENT}, variable{":id", cldbid}); deque> property_list; LOG_SQL_CMD(load_properties(server ? server->getServerId() : 0, property_list, command)); @@ -1005,15 +1010,6 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std: column = "client_total_download"; break; - default: - return; - } - } else if(prop.type().type_property == property::PROP_TYPE_CONNECTION) { - switch (prop.type().property_index) { - case property::CONNECTION_CLIENT_IP: - column = "client_ip"; - break; - default: return; } @@ -1035,6 +1031,14 @@ std::shared_ptr DatabaseHelper::loadClientProperties(const std: return props; } +void DatabaseHelper::updateClientIpAddress(const ServerId &server_id, ClientDbId client_database_id, const std::string &ip_address) { + sql::command(this->sql, "UPDATE `clients_server` SET `client_ip` = :value WHERE `server_id` = :serverId AND `client_database_id` = :cldbid", + variable{":serverId", server_id}, + variable{":cldbid", client_database_id}, + variable{":value", ip_address} + ).executeLater().waitAndGetLater(LOG_SQL_CMD, {1, "future failed"}); +} + void DatabaseHelper::loadStartupCache() { this->loadStartupPermissionCache(); this->loadStartupPropertyCache(); diff --git a/server/src/DatabaseHelper.h b/server/src/DatabaseHelper.h index 3a54c10..7794f47 100644 --- a/server/src/DatabaseHelper.h +++ b/server/src/DatabaseHelper.h @@ -18,6 +18,7 @@ namespace ts::server { ClientDbId client_database_id; std::string client_unique_id; std::string client_nickname; + std::string client_ip; std::chrono::time_point client_created; std::chrono::time_point client_last_connected; @@ -108,6 +109,7 @@ namespace ts::server { std::shared_ptr loadPlaylistProperties(const std::shared_ptr&, PlaylistId); //Read and write std::shared_ptr loadChannelProperties(const std::shared_ptr&, ChannelId); //Read and write std::shared_ptr loadClientProperties(const std::shared_ptr&, ClientDbId, ClientType); + void updateClientIpAddress(const ServerId& /* server id */, ClientDbId /* target database id */, const std::string& /* ip address */); void deleteGroupArtifacts(ServerId, GroupId); bool deleteChannelPermissions(const std::shared_ptr&, ChannelId); diff --git a/server/src/Group.cpp b/server/src/Group.cpp deleted file mode 100644 index d175aeb..0000000 --- a/server/src/Group.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include -#include -#include -#include "Group.h" -#include "VirtualServer.h" -#include "src/client/ConnectedClient.h" -#include "InstanceHandler.h" - -using namespace std; -using namespace std::chrono; -using namespace ts; -using namespace ts::server; -using namespace ts::permission; diff --git a/server/src/InstanceHandler.h b/server/src/InstanceHandler.h index 0974e89..9974fa8 100644 --- a/server/src/InstanceHandler.h +++ b/server/src/InstanceHandler.h @@ -11,10 +11,6 @@ #include namespace ts { - namespace weblist { - class WebListManager; - } - namespace permission { class PermissionNameMapper; } @@ -91,7 +87,6 @@ namespace ts { [[nodiscard]] inline std::shared_ptr getTeamSpeakLicense() { return this->teamspeak_license; } [[nodiscard]] inline PropertyWrapper getDefaultServerProperties() { return PropertyWrapper{this->default_server_properties}; } [[nodiscard]] inline std::shared_ptr getWebIoLoop() { return this->web_event_loop; } - [[nodiscard]] inline std::shared_ptr getWebList() { return this->web_list; } [[nodiscard]] inline std::shared_ptr getPermissionMapper() { return this->permission_mapper; } [[nodiscard]] inline std::shared_ptr getConversationIo() { return this->conversation_io; } @@ -130,7 +125,6 @@ namespace ts { std::shared_ptr conversation_io = nullptr; std::shared_ptr web_event_loop = nullptr; - std::shared_ptr web_list = nullptr; std::shared_ptr default_server_properties = nullptr; std::shared_mutex default_tree_lock; diff --git a/server/src/ServerManagerSnapshotDeploy.cpp b/server/src/ServerManagerSnapshotDeploy.cpp index a9accf3..fe8a501 100644 --- a/server/src/ServerManagerSnapshotDeploy.cpp +++ b/server/src/ServerManagerSnapshotDeploy.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -16,13 +15,6 @@ using namespace ts::server; #define PREFIX string("[SNAPSHOT] ") -inline std::string avArguments(const ts::Command& cmd, int index){ - stringstream res; - for(const auto &key : cmd[index].keys()) - res << key << "=" << cmd[index][key].as() << " "; - return res.str(); -} - //TeamSpeak: permid=i_channel_needed_permission_modify_power permvalue=75 permskip=0 permnegated=0 //TeaSpeak: perm=i_channel_needed_permission_modify_power value=75 granted=75 flag_skip=0 flag_negated=0 struct SnapshotPermissionEntry { @@ -32,104 +24,9 @@ struct SnapshotPermissionEntry { bool negated = false; bool skipped = false; - SnapshotPermissionEntry(const std::shared_ptr& type, permission::PermissionValue value, permission::PermissionValue grant) : type(std::move(type)), value(value), grant(grant) {} SnapshotPermissionEntry(const std::shared_ptr& type, permission::PermissionValue value, permission::PermissionValue grant, bool negated, bool skipped) : type(type), value(value), grant(grant), negated(negated), skipped(skipped) {} SnapshotPermissionEntry() = default; - static deque> parse(const Command& cmd, int& index, permission::teamspeak::GroupType type, int version, const std::vector& ends, bool ignore_first = false) { - deque> result{}; - - while(true) { - bool end_reached = false; - for(const auto& e : ends) - if(cmd[index].has(e)) { end_reached = true; break; } - if(end_reached || cmd.bulkCount() < index) { - if(ignore_first) ignore_first = false; - else break; - } - - if(version == 0) { - auto permission_name = cmd[index]["permid"].string(); - for(const auto& mapped : permission::teamspeak::map_key(permission_name, type)) { - auto permission = permission::resolvePermissionData(mapped); - if(permission->type == permission::unknown) { - logError(0, "Failed to parse permission {}. Original: {}", mapped, permission_name); - index++; - continue; - } - - bool found = false; - for(auto& e : result) - if(e->type->type == permission->type) { - found = true; - if(permission->grantName() == mapped) - e->grant = cmd[index]["permvalue"]; - else { - e->value = cmd[index]["permvalue"]; - e->negated = cmd[index]["permnegated"]; - e->skipped = cmd[index]["permskip"]; - } - } - if(!found) { - auto entry = make_unique(); - entry->type = permission; - if(permission->grantName() == mapped) - entry->grant = cmd[index]["permvalue"]; - else { - entry->value = cmd[index]["permvalue"]; - entry->negated = cmd[index]["permnegated"]; - entry->skipped = cmd[index]["permskip"]; - } - result.push_back(std::move(entry)); - } - } - } else if(version >= 1) { - auto permission_name = cmd[index]["perm"].string(); - - auto permission = permission::resolvePermissionData(permission_name); - if(permission->type == permission::unknown) { - logError(0, "Failed to parse permission {}", permission_name); - index++; - continue; - } - - auto entry = make_unique(); - entry->type = permission; - entry->value = cmd[index]["value"]; - entry->grant = cmd[index]["grant"]; - entry->skipped = cmd[index]["flag_skip"]; - entry->negated = cmd[index]["flag_negated"]; - result.push_back(std::move(entry)); - } else { - logError(0, "Failed to parse snapshot permission entry! Invalid version!"); - index++; - continue; - } - - index++; - } - - - deque> addings{}; - for(const auto& perm : result) { - if(perm->type->type == permission::i_group_auto_update_type) { //Migrate this type - for(const auto& e : permission::update::migrate) - if(e.type == perm->value) { - auto _type = permission::resolvePermissionData(e.permission.name); - if(_type->type == permission::unknown) { - logError(0, "Invalid update permission for update group {}. Permission name: {}", e.type, e.permission.name); - } else { - addings.push_back(make_unique(_type, e.permission.value, e.permission.granted, e.permission.negated, e.permission.skipped)); - } - } - } - } - for(auto& a : addings) - result.push_back(move(a)); - - return result; - } - void write(Command& cmd, int& index, permission::teamspeak::GroupType type, int version) { if(version == 0) { if(this->value != permNotGranted) { @@ -163,544 +60,6 @@ struct SnapshotPermissionEntry { } }; -#if 0 -std::shared_ptr VirtualServerManager::createServerFromSnapshot(shared_ptr old, std::string host, - uint16_t port, const ts::Command &arguments, - std::string &error) { - ServerId serverId = 0; - map> channelGroupRelations; //cid is the new cgid - map channelGroupMapping; - map> serverGroupRelations; //gid is the new gid - map serverGroupMapping; - map db_mapping_client_id; - - deque>> futures; - - bool sid_success = false; - serverId = this->next_available_server_id(sid_success); - if(!sid_success) { - error = "failed to generate new sid"; - return nullptr; - } - ServerId log_server_id = old ? old->getServerId() : serverId; - - futures.emplace_back( - "server id register", - sql::command(this->handle->getSql(), "INSERT INTO `servers` (`serverId`, `host`, `port`) VALUES (:sid, :host, :port)", variable{":sid", serverId}, variable{":host", host}, variable{":port", port}).executeLater() - ); - sql::model sql_insert_channel(this->handle->getSql(), "INSERT INTO `channels` (`serverId`, `channelId`, `type`, `parentId`) VALUES (:sid, :id, :type, :pid)", variable{":sid", serverId}); - sql::model sql_insert_group_assignment(this->handle->getSql(), "INSERT INTO `assignedGroups` (`serverId`, `cldbid`, `groupId`, `channelId`, `until`) VALUES (:sid, :cldbid, :gid, :chid, :until)", variable{":sid", serverId}, variable{":until", 0}); - sql::model sql_insert_permission(this->handle->getSql(), "INSERT INTO `permissions` (`serverId`, `type`, `id`, `channelId`, `permId`, `value`, `grant`, `flag_skip`, `flag_negate`) VALUES (:serverId, :type, :id, :chId, :permId, :value, :grant, :flag_skip, :flag_negate)", - variable{":serverId", serverId}); - sql::model sql_update_permission_grant(this->handle->getSql(), "UPDATE `permissions` SET `grant` = :grant WHERE `serverId` = :sid AND `type` = :type AND `id` = :id AND `channelId` = :chid AND `permId` = :key", variable{":sid", serverId}); - sql::model sql_insert_property(this->handle->getSql(), "INSERT INTO `properties` (`serverId`, `type`, `id`, `key`, `value`) VALUES (:sid, :type, :id, :key, :value)", variable{":sid", serverId}); - sql::model sql_insert_bot(this->handle->getSql(), "INSERT INTO `musicbots` (`serverId`, `botId`, `uniqueId`, `owner`) VALUES (:server_id, :bot_id, :unique_id, :owner)", variable{":server_id", serverId}); - sql::model sql_insert_playlist(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)", variable{":server_id", serverId}); - sql::model sql_insert_playlist_song(this->handle->getSql(), "INSERT INTO `playlist_songs` (`serverId`, `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata`) VALUES (:server_id, :playlist_id, :song_id, :order_id, :invoker_dbid, :url, :url_loader, :loaded, :metadata)", variable{":server_id", serverId}); - - int index = 0; - auto snapshot_version = arguments[index].has("snapshot_version") ? arguments[index++]["snapshot_version"] : 0; - debugMessage(0, "Got server snapshot with version {}", snapshot_version); - while(true){ - for(const auto &key : arguments[index].keys()){ - if(key == "end_virtualserver") continue; - if(key == "begin_virtualserver") continue; - if(snapshot_version == 0) { - if(key == "virtualserver_download_quota" || key == "virtualserver_upload_quota" || key == "virtualserver_max_download_total_bandwidth" || key == "virtualserver_max_upload_total_bandwidth") { - auto value = arguments[index][key].string(); - try { - arguments[index][key] = to_string((int64_t) std::stoull(value)); - } catch(const std::exception& ex) { - continue; - } - } - } - debugMessage(LOG_GENERAL, PREFIX + " Having server key {} = {}", key, arguments[index][key].as()); - futures.emplace_back( - "server property import proprty=" + key, - sql_insert_property.command().values( - variable{":type", property::PropertyType::PROP_TYPE_SERVER}, - variable{":id", 0}, - variable{":key", key}, - variable{":value", arguments[index][key].string()} - ).executeLater() - ); - } - - if(arguments[index].has("end_virtualserver")) break; - index++; - } - - - //`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT, `url_loader` TEXT - /* - * sql::command(this->handle->getSql(), "INSERT INTO `playlists` (`serverId`, `playlist_id`) VALUES (:server_id, :playlist_id)", - variable{":server_id", this->ref_server()->getServerId()}, - variable{":playlist_id", playlist_id} - ) - */ - index++; - for(;index < arguments.bulkCount(); index++) { - if (arguments[index].has("begin_channels")) { - while (true) { - if (arguments[index].has("end_channels")) break; - - auto channel_id = arguments[index]["channel_id"].string(); - futures.emplace_back( - "channel register cid=" + channel_id, - sql_insert_channel.command().values(variable{":id", channel_id}, variable{":type", -1}, variable{":pid", arguments[index]["channel_pid"].string()}).executeLater() - ); //TODO findout type! - for (const auto &key : arguments[index].keys()) { - if(key == "channel_id") continue; - if(key == "channel_pid") continue; - if(key == "channel_security_salt") continue; - if(key == "channel_filepath") continue; //TODO implement channel_filepath? - if(key == "begin_channels") continue; - - debugMessage(log_server_id, PREFIX + "Having channel key " + key + "=" + arguments[index][key].as() + " for channel " + arguments[index]["channel_id"].as()); - - futures.push_back({ - "channel prop register cid=" + channel_id + " property=" + key, - sql_insert_property.command().values(variable{":sid", serverId}, - variable{":type", property::PropertyType::PROP_TYPE_CHANNEL}, - variable{":id", channel_id}, - variable{":key", key}, - variable{":value", arguments[index][key].as()} - ).executeLater() - }); - } - index++; - } - } else if (arguments[index].has("begin_clients")) { - while (true) { - if (arguments[index].has("end_clients")) break; - debugMessage(log_server_id, PREFIX + "Create client {}/{} -> end: {}",arguments[index]["client_id"].as(), arguments[index]["client_nickname"].as(), to_string(arguments[index].has("end_clients"))); - - ClientDbId oldId = 0; - auto res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `clientUid` = :uid AND `serverId` = 0", variable{":uid", arguments[index]["client_unique_id"].as()}).query([](ClientDbId* ptr, int, char** v, char**){ *ptr = stoll(v[0]); return 0;}, &oldId); - LOG_SQL_CMD(res); - if(oldId == 0){ - res = sql::command(this->handle->getSql(), "SELECT `cldbid` FROM `clients` WHERE `serverId` = 0 ORDER BY `cldbid` DESC LIMIT 1").query([](ClientDbId* ptr, int length, char** values, char** names){ - *ptr = static_cast(stoll(values[0])); - return 0; - }, &oldId); - LOG_SQL_CMD(res); - - oldId += 1; - //Create a new client - sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)", - variable{":sid", 0}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as()}, - variable{":firstConnect", arguments[index]["client_created"].as()}, - variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as()}).execute(); //TODO log error - } - db_mapping_client_id[arguments[index]["client_id"].as()] = oldId; - sql::command(this->handle->getSql(), "INSERT INTO `clients` (`serverId`, `cldbid`, `clientUid`, `firstConnect`, `lastConnect`, `connections`, `lastName`) VALUES (:sid, :cldbid, :cluid, :firstConnect, :lastConnect, :connections, :name)", - variable{":sid", serverId}, variable{":cldbid", oldId}, variable{":cluid", arguments[index]["client_unique_id"].as()}, - variable{":firstConnect", arguments[index]["client_created"].as()}, - variable{":lastConnect", "0"}, variable{":connections", "0"}, variable{":name", arguments[index]["client_nickname"].as()}).execute(); //TODO log error - //TODO here - index++; - } - } else if (arguments[index].has("begin_permissions")) { - while (true) { - index++; - if (arguments[index].has("end_permissions")) break; - - else if (arguments[index].has("server_groups")) { - GroupId currentGroupId = 1; - while (true) { - if (arguments[index].has("end_groups")) break; - - if(!arguments[index].has("id")){ //new group - logError(0, "Invalid server group permission entry! Missing group id!"); - index++; - continue; - } - - currentGroupId = static_cast(GroupManager::generateGroupId(this->handle->getSql())); - debugMessage(log_server_id, PREFIX + "Insert group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as()); - serverGroupMapping[arguments[index]["id"].as()] = currentGroupId; - - LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)", - variable{":sid", serverId}, - variable{":gid", currentGroupId}, - variable{":target", GROUPTARGET_SERVER}, - variable{":type", GroupType::GROUP_TYPE_NORMAL}, - variable{":name", arguments[index]["name"].as()}) - .execute()); - - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"}); - for(const auto& entry : permissions) { - futures.push_back({ - "server group permission sgid=" + to_string(currentGroupId) + " permId=" + entry->type->name, - sql_insert_permission.command().values( - variable{":id", currentGroupId}, - variable{":type", permission::SQL_PERM_GROUP}, - variable{":chId", 0}, - - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - index++; - } - - //Relations for sgroups after list - index++; - while (true) { - if (arguments[index].has("end_relations")) break; - serverGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as()]].push_back(serverGroupMapping[arguments[index]["gid"].as()]); - futures.push_back({ - "server group relation", - sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as()]}, variable{":gid", serverGroupMapping[arguments[index]["gid"].as()]}, variable{":chid", "0"}).executeLater() - }); - index++; - } - } else if(arguments[index].has("channel_groups")){ - GroupId currentGroupId = 1; - while (true) { - if (arguments[index].has("end_groups")) break; - - if(!arguments[index].has("id")){ //new group - logError(0, "Invalid server group permission entry! Missing group id!"); - index++; - continue; - } - - currentGroupId = static_cast(GroupManager::generateGroupId(this->handle->getSql())); - debugMessage(log_server_id, PREFIX + "Insert channel group: " + to_string(currentGroupId) + " - " + arguments[index]["name"].as()); - channelGroupMapping[arguments[index]["id"].as()] = currentGroupId; - - LOG_SQL_CMD(sql::command(this->handle->getSql(), "INSERT INTO `groups` (`serverId`, `groupId`, `target`, `type`, `displayName`) VALUES (:sid, :gid, :target, :type, :name)", - variable{":sid", serverId}, - variable{":gid", currentGroupId}, - variable{":target", GROUPTARGET_CHANNEL}, - variable{":type", GroupType::GROUP_TYPE_NORMAL}, - variable{":name", arguments[index]["name"].as()}) - .execute()); - - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::SERVER, snapshot_version, {"end_group"}); - for(const auto& entry : permissions) { - futures.push_back({ - "channel group property insert cgid=" + to_string(currentGroupId) + " property=" + entry->type->name, - sql_insert_permission.command().values( - variable{":id", currentGroupId}, - variable{":type", permission::SQL_PERM_GROUP}, - variable{":chId", 0}, - - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - index++; - } - - //Relations for sgroups after list - ChannelId chId = 0; - index++; - while (true) { - if (arguments[index].has("end_relations")) break; - if(arguments[index].has("iid")) chId = arguments[index]["iid"]; - channelGroupRelations[db_mapping_client_id[arguments[index]["cldbid"].as()]][chId] = channelGroupMapping[arguments[index]["gid"].as()]; - futures.push_back({ - "channel group relation", - sql_insert_group_assignment.command().values(variable{":cldbid", db_mapping_client_id[arguments[index]["cldbid"].as()]}, variable{":gid", channelGroupMapping[arguments[index]["gid"].as()]}, variable{":chid", chId}).executeLater() - }); - index++; - } - } else if (arguments[index].has("client_flat")) { - ClientDbId currentId = 0; - while (true) { - if (arguments[index].has("end_flat")) break; - if(arguments[index].has("id1")) - currentId = arguments[index]["id1"]; - - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "end_flat"}, true); - for(const auto& entry : permissions) { - futures.push_back({ - "client permission insert clientId=" + to_string(currentId) + " permId=" + entry->type->name, - sql_insert_permission.command().values( - variable{":id", currentId}, - variable{":type", permission::SQL_PERM_USER}, - variable{":chId", 0}, - - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - } - } else if (arguments[index].has("channel_flat")) { - ChannelId currentChannelId = 0; - while (true) { - if (arguments[index].has("end_flat")) break; - - if (arguments[index].has("id1")) - currentChannelId = arguments[index]["id1"]; //We also have id2 unknown for what this is. Maybe parent? - - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CHANNEL, snapshot_version, {"id1", "end_flat"}, true); - for(const auto& entry : permissions) { - futures.push_back({ - "channel permission insert channelId=" + to_string(currentChannelId) + " permId=" + entry->type->name, - sql_insert_permission.command().values( - variable{":id", 0}, - variable{":type", permission::SQL_PERM_CHANNEL}, - variable{":chId", currentChannelId}, - - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - } - } else if (arguments[index].has("channel_client_flat")) { - ClientDbId currentClientId = 0; - ChannelId currentChannelId = 0; - while (true) { - if (arguments[index].has("end_flat")) break; - //id1 = channel id - //id2 = client id - if(arguments[index].has("id1")) - currentChannelId = arguments[index]["id1"]; - if(arguments[index].has("id2")) - currentClientId = db_mapping_client_id[arguments[index]["id2"]]; - if(currentChannelId > 0 && currentClientId > 0){ - auto permissions = SnapshotPermissionEntry::parse(arguments, index, permission::teamspeak::CLIENT, snapshot_version, {"id1", "id2", "end_flat"}, true); - for(const auto& entry : permissions) { - futures.push_back({ - "client channel permission insert", - sql_insert_permission.command().values( - variable{":id", currentClientId}, - variable{":type", permission::SQL_PERM_CHANNEL}, - variable{":chId", currentChannelId}, - - variable{":permId", entry->type->name}, - variable{":value", entry->value}, - variable{":grant", entry->grant}, - variable{":flag_skip", entry->skipped}, - variable{":flag_negate", entry->negated} - ).executeLater() - }); - } - } else index++; - } - } else { - error = "Invalid tag in permissions. Available: " + avArguments(arguments, index); - return nullptr; - } - } - } else if(arguments[index].has("begin_music")) { - PlaylistId playlist_index = 0; - PlaylistId playlist_error = 0xFFFFFFFF; - map db_mapping_playlist; - - /* - //gather static info (not needed because its a new server) - { - auto sql_result = sql::command(this->handle->getSql(), "SELECT `playlist_id` FROM `playlists` WHERE `serverId` = :server_id ORDER BY `playlist_id` DESC", variable{":server_id", serverId}).query([&](int length, string* values, string* names){ - if(length != 1) return; - - try { - playlist_index = (PlaylistId) stoll(values[0]); - } catch(const std::exception& ex) { - error = "Failed to parse playlist id from database. ID: " + values[0]; - return; - } - }); - LOG_SQL_CMD(sql_result); - - if(playlist_index == 0) { - error = "failed to gather info"; - return nullptr; - } - } - */ - - while(!arguments[index].has("end_music")) { - if(arguments[index].has("begin_bots")) { - while(!arguments[index].has("end_bots")) { - ClientDbId bot_id = arguments[index]["bot_id"]; - if(db_mapping_client_id.find(bot_id) == db_mapping_client_id.end()) { - logError(log_server_id, PREFIX + "Failed to insert music bot within id {}. (Mapping not found)", bot_id); - index++; - continue; - } - ClientDbId new_bot_id = db_mapping_client_id[bot_id]; - - //begin_bots - auto sql_result = sql_insert_bot.command().values( - variable{":bot_id", new_bot_id}, - variable{":unique_id", arguments[index]["bot_unique_id"].string()}, - variable{":owner", arguments[index]["bot_owner_id"].string()} - ).execute(); - if(!sql_result) { - logError(log_server_id, PREFIX + "Failed to insert music bot with id {} (old: {}). ({})", new_bot_id, bot_id, sql_result.fmtStr()); - index++; - continue; - } - - for(const auto& key : arguments[index].keys()) { - if(key == "begin_music") continue; - if(key == "begin_bots") continue; - if(key == "bot_unique_id") continue; - if(key == "bot_owner_id") continue; - if(key == "bot_id") continue; - - const auto& property = property::find(key); - if(property.is_undefined()) { - debugMessage(log_server_id, PREFIX + "Failed to parse give music bot property {} for bot {} (old: {}). Value: {}", key, new_bot_id, bot_id, arguments[index][key].string()); - continue; - } - - futures.push_back({ - "music bot insert", - sql_insert_property.command().values( - variable{":type", property::PropertyType::PROP_TYPE_CLIENT}, - variable{":id", new_bot_id}, - variable{":key", key}, - variable{":value", arguments[index][key].string()} - ).executeLater() - }); - } - - index++; - } - } else if(arguments[index].has("begin_playlist")) { - while(!arguments[index].has("end_playlist")) { - PlaylistId playlist_id = arguments[index]["playlist_id"]; - db_mapping_playlist[++playlist_index] = playlist_id; - - auto sql_result = sql_insert_playlist.command().values(variable{":playlist_id", playlist_index}).execute(); - if(!sql_result) { - db_mapping_playlist[playlist_index] = playlist_error; - logError(log_server_id, PREFIX + "Failed to insert playlist with id {} (old: {}). ({})", playlist_index, playlist_id, sql_result.fmtStr()); - index++; - continue; - } - - - for(const auto& key : arguments[index].keys()) { - if(key == "begin_music") continue; - if(key == "begin_playlist") continue; - if(key == "playlist_id") continue; - - const auto& property = property::find(key); - if(property.is_undefined()) { - debugMessage(log_server_id, PREFIX + "Failed to parse given playlist property {} for playlist {} (old: {}). Value: {}", key, playlist_index, playlist_id, arguments[index][key].string()); - continue; - } - - futures.push_back({ - "playlist insert", - sql_insert_property.command().values( - variable{":type", property::PropertyType::PROP_TYPE_PLAYLIST}, - variable{":id", playlist_index}, - variable{":key", key}, - variable{":value", arguments[index][key].string()} - ).executeLater() - }); - } - - index++; - } - } else if(arguments[index].has("begin_playlist_songs")) { - PlaylistId current_playlist = 0; - PlaylistId current_playlist_mapped = 0; - while(!arguments[index].has("end_playlist_songs")) { - if(arguments[index].has("song_playlist_id")) { - current_playlist = arguments[index]["song_playlist_id"]; - current_playlist_mapped = (PlaylistId) db_mapping_client_id[current_playlist]; - if(current_playlist_mapped == 0) { - logError(log_server_id, PREFIX + "Failed to insert playlist song entry {}. Playlist id {} hasn't been mapped", arguments[index]["song_id"].value(), current_playlist); - current_playlist_mapped = playlist_error; - } - } - if(current_playlist_mapped == playlist_error) { /* current list is an error */ - index++; - continue; - } - - auto sql_future = sql_insert_playlist_song.command().values( - variable{":playlist_id", current_playlist_mapped}, - variable{":song_id", arguments[index]["song_id"].value()}, - variable{":order_id", arguments[index]["song_order"].value()}, - variable{":invoker_dbid", arguments[index]["song_invoker"].value()}, - variable{":url", arguments[index]["song_url"].value()}, - variable{":url_loader", arguments[index]["song_url_loader"].value()}, - variable{":loaded", arguments[index]["song_loaded"].value()}, - variable{":metadata", arguments[index]["song_metadata"].value()} - ).executeLater(); - futures.push_back({ - "song insert", - sql_future - }); - - index++; - } - } - index++; - } - } else { - error = "Invalid root tag. Available: " + avArguments(arguments, index); - return nullptr; - } - } - - debugMessage(serverId, "Wait for success!"); - - for(const auto& future : futures) { - auto result = future.second.waitAndGet({-1, "timeout"}, chrono::system_clock::now() + chrono::seconds(5)); - if(!result) { - logError(serverId, "Failed to execute snapshotdeploy command {}: {}", future.first, result.fmtStr()); - } - } - - if(old) - this->deleteServer(old); - //Now we load the server - auto server = make_shared(serverId, this->handle->getSql()); - server->self = server; - if(!server->initialize(false)) { - //FIXME Error handling! - } - server->properties()[property::VIRTUALSERVER_HOST] = host; - server->properties()[property::VIRTUALSERVER_PORT] = port; - server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; - - { - threads::MutexLock l(this->instanceLock); - this->instances.push_back(server); - } - this->adjust_executor_threads(); - - server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] = serverGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP].as()]; - server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP].as()]; - server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = channelGroupMapping[server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP].as()]; - server->ensureValidDefaultGroups(); - return server; -} -#endif - -struct CommandTuple { - Command& cmd; - int& index; - int version; -}; - struct PermissionCommandTuple { Command& cmd; int& index; diff --git a/server/src/ShutdownHelper.cpp b/server/src/ShutdownHelper.cpp index adcd9d4..6950595 100644 --- a/server/src/ShutdownHelper.cpp +++ b/server/src/ShutdownHelper.cpp @@ -45,6 +45,10 @@ void ts::server::shutdownInstance(const std::string& message) { mainThreadActive = false; } +bool ts::server::isShuttingDown() { + return shuttingDown; +} + std::shared_ptr currentShutdown = nullptr; std::shared_ptr server::scheduledShutdown() { return currentShutdown; } diff --git a/server/src/ShutdownHelper.h b/server/src/ShutdownHelper.h index 04b609d..64fbb63 100644 --- a/server/src/ShutdownHelper.h +++ b/server/src/ShutdownHelper.h @@ -5,7 +5,7 @@ namespace ts { namespace server { extern void shutdownInstance(const std::string& reason = ts::config::messages::applicationStopped); - + extern bool isShuttingDown(); struct ShutdownData { std::string reason; diff --git a/server/src/SignalHandler.cpp b/server/src/SignalHandler.cpp index 66997f5..59572f2 100644 --- a/server/src/SignalHandler.cpp +++ b/server/src/SignalHandler.cpp @@ -41,6 +41,10 @@ void print_current_exception() { extern bool mainThreadDone; #ifdef BREAKPAD_EXCEPTION_HANDLER static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) { + if(ts::server::isShuttingDown()) { + return true; + } + logCritical(LOG_GENERAL, "The server crashed!"); try { if(!fs::exists(fs::u8path(ts::config::crash_path))) diff --git a/server/src/TS3ServerHeartbeat.cpp b/server/src/TS3ServerHeartbeat.cpp index b57aa22..84fc726 100644 --- a/server/src/TS3ServerHeartbeat.cpp +++ b/server/src/TS3ServerHeartbeat.cpp @@ -17,10 +17,10 @@ using namespace ts::buffer; inline void banClientFlood(VirtualServer* server, const shared_ptr& cl, time_point until){ auto time = until.time_since_epoch().count() == 0 ? 0L : chrono::ceil(until - system_clock::now()).count(); - std::string reason = "You're flooding too much"; + std::string reason{"You're flooding too much"}; serverInstance->banManager()->registerBan(server->getServerId(), cl->getClientDatabaseId(), reason, cl->getUid(), cl->getLoggingPeerIp(), "", "", until); - for(const auto &client : server->findClientsByUid(cl->getUid())) { + for(const auto &client : server->findClientsByUid(cl->getUid())) { server->notify_client_ban(client, server->getServerRoot(), reason, time); client->close_connection(system_clock::now() + seconds(1)); } @@ -32,29 +32,27 @@ timing_end = system_clock::now(); \ variable = duration_cast(timing_end - timing_begin); void VirtualServer::executeServerTick() { - threads::MutexTryLock l(this->stateLock); //Should not attempt to shutdown or start the server while ticking - if(!l) { - if(this->running()) - logError(this->getServerId(), "Failed to lock tick mutex!"); + std::shared_lock state_lock{this->state_mutex}; + if(!this->running()) { return; } - if(!this->running()) return; + auto tick_timestamp = std::chrono::system_clock::now(); try { - if(lastTick.time_since_epoch().count() != 0) { - auto delay = system_clock::now() - lastTick; - auto delay_ms = duration_cast(delay).count(); + if(this->lastTick.time_since_epoch().count() > 0) { + auto delay = tick_timestamp - this->lastTick; + auto delay_ms = std::chrono::duration_cast(delay).count(); if(delay_ms > 510) { if(delay_ms < 750) { logWarning(this->getServerId(), - "Found varianzes within the server tick! (Supposed: 500ms Hold: {}ms)", delay_ms); + "Found variances within the server tick! (Supposed: 500ms Hold: {}ms)", delay_ms); } else { logError(this->getServerId(), - "Found varianzes within the server tick! This long delay could be an issue. (Supposed: 500ms Hold: {}ms)", delay_ms); + "Found variances within the server tick! This long delay could be an issue. (Supposed: 500ms Hold: {}ms)", delay_ms); } } } - lastTick = system_clock::now(); + this->lastTick = tick_timestamp; system_clock::time_point timing_begin, timing_end; milliseconds timing_update_states, timing_client_tick, timing_channel, timing_statistic, timing_groups, timing_ccache, music_manager; @@ -64,8 +62,8 @@ void VirtualServer::executeServerTick() { { BEGIN_TIMINGS(); - size_t clientOnline = 0; - size_t queryOnline = 0; + size_t clientOnline{0}; + size_t queryOnline{0}; for(const auto& conn : client_list){ switch (conn->getType()){ case ClientType::CLIENT_TEAMSPEAK: @@ -73,6 +71,7 @@ void VirtualServer::executeServerTick() { case ClientType::CLIENT_WEB: clientOnline++; break; + case ClientType::CLIENT_QUERY: case ClientType::CLIENT_MUSIC: queryOnline++; @@ -85,13 +84,14 @@ void VirtualServer::executeServerTick() { } } - properties()[property::VIRTUALSERVER_UPTIME] = std::chrono::duration_cast(std::chrono::system_clock::now() - this->startTimestamp).count(); + properties()[property::VIRTUALSERVER_UPTIME] = std::chrono::duration_cast(tick_timestamp - this->startTimestamp).count(); properties()[property::VIRTUALSERVER_CLIENTS_ONLINE] = clientOnline + queryOnline; properties()[property::VIRTUALSERVER_QUERYCLIENTS_ONLINE] = queryOnline; if(clientOnline + queryOnline == 0) { //We don't need to tick, when server is empty! return; } + properties()[property::VIRTUALSERVER_CHANNELS_ONLINE] = this->channelTree->channel_count(); properties()[property::VIRTUALSERVER_TOTAL_PING] = this->generate_network_report().average_ping; END_TIMINGS(timing_update_states); @@ -123,24 +123,30 @@ void VirtualServer::executeServerTick() { } continue; //Fully ha? } + if(cl->floodPoints > flood_block){ if(!cl->ignoresFlood()) { banClientFlood(this, cl, system_clock::now() + minutes(10)); continue; } } - if(cl->floodPoints > flood_decrease) + + if(cl->floodPoints > flood_decrease) { cl->floodPoints -= flood_decrease; - else cl->floodPoints = 0; + } else { + cl->floodPoints = 0; + } + cl->tick_server(tick_client_end); auto voice = dynamic_pointer_cast(cl); - if(flag_update_spoken && voice) + if(flag_update_spoken && voice) { this->spoken_time += voice->takeSpokenTime(); + } tick_client_end = system_clock::now(); auto passed_time = tick_client_end - tick_client_begin; - if(passed_time > microseconds(2500)) { - if(passed_time > milliseconds(10)) { + if(passed_time > microseconds{2500}) { + if(passed_time > milliseconds{10}) { logError(this->serverId, "Ticking of client {:1} ({:2}) needs more that 2500 microseconds! ({:3} microseconds)", cl->getLoggingPeerIp() + ":" + to_string(cl->getPeerPort()), cl->getDisplayName(), @@ -163,8 +169,10 @@ void VirtualServer::executeServerTick() { debugMessage(this->serverId, "Saved client permissions for client {} ({}) in {}ms", cl->getClientDatabaseId(), cl->getDisplayName(), duration_cast(end - begin).count()); } } - if(flag_update_spoken) + + if(flag_update_spoken) { this->spoken_time_timestamp = system_clock::now(); + } END_TIMINGS(timing_client_tick); } @@ -173,30 +181,33 @@ void VirtualServer::executeServerTick() { { BEGIN_TIMINGS(); - unique_lock channel_lock(this->channel_tree_mutex); + std::unique_lock channel_lock{this->channel_tree_mutex}; + auto channels = this->channelTree->channels(); - channel_lock.unlock(); + for(const auto& channel : channels) { + auto server_channel = dynamic_pointer_cast(channel); + assert(server_channel); - for(const auto& channel : channels){ if(channel->channelType() == ChannelType::temporary) { - auto server_channel = dynamic_pointer_cast(channel); - assert(server_channel); - if(server_channel->client_count() > 0 || !this->getClientsByChannelRoot(channel, true).empty()) + if(server_channel->client_count() > 0 || !this->isChannelRootEmpty(channel, false)) { continue; + } - seconds deleteTimeout{channel->properties()[property::CHANNEL_DELETE_DELAY].as_or(0)}; + /* seconds */ + auto channel_delete_timeout = channel->properties()[property::CHANNEL_DELETE_DELAY].as_or(0); + auto empty_seconds = channel->empty_seconds(); - auto last_left = time_point() + milliseconds( - channel->properties()[property::CHANNEL_LAST_LEFT].as_or(0)); - auto channel_created = channel->createdTimestamp(); + if(empty_seconds > channel_delete_timeout) { + this->delete_channel(server_channel, this->serverRoot, "temporary auto delete", channel_lock, true); + if(!channel_lock.owns_lock()) { + channel_lock.lock(); + } - if(system_clock::now() - last_left < deleteTimeout) continue; //One second stay - if(system_clock::now() - channel_created < deleteTimeout + seconds(1)) continue; //One second stay - - this->delete_channel(server_channel, this->serverRoot, "temporary auto delete", channel_lock, true); - if(channel_lock.owns_lock()) - channel_lock.unlock(); + /* no need to tick the channel any more since it has been deleted */ + continue; + } } + { auto permission_manager = channel->permissions(); if(permission_manager->require_db_updates()) { @@ -216,18 +227,13 @@ void VirtualServer::executeServerTick() { this->server_statistics_->tick(); { - lock_guard lock(this->join_attempts_lock); - if(system_clock::now() > this->join_last_decrease + seconds(5)) { - for(auto& elm : this->join_attempts) - if(elm.second > 0) elm.second--; + lock_guard lock{this->join_attempts_lock}; + if(tick_timestamp > this->join_last_decrease + seconds(5)) { + std::erase_if(this->join_attempts, [](auto& entry) { + return --entry.second <= 0; + }); - auto copy = this->join_attempts; - for(const auto& elm : copy) - if(elm.second == 0){ - auto found = find(this->join_attempts.begin(), this->join_attempts.end(), elm); - if(found != this->join_attempts.end()) this->join_attempts.erase(found); - } - this->join_last_decrease = system_clock::now(); + this->join_last_decrease = tick_timestamp; } } END_TIMINGS(timing_statistic); @@ -257,7 +263,7 @@ void VirtualServer::executeServerTick() { if(system_clock::now() - lastTick > milliseconds(100)) { //milliseconds timing_update_states, timing_client_tick, timing_channel, timing_statistic; - logError(this->serverId, "Server tick tooks to long ({}ms => Status updates: {}ms Client tick: {}ms, Channel tick: {}ms, Statistic tick: {}ms, Groups: {}ms, Conversation cache: {}ms)", + logError(this->serverId, "Server tick took to long ({}ms => Status updates: {}ms Client tick: {}ms, Channel tick: {}ms, Statistic tick: {}ms, Groups: {}ms, Conversation cache: {}ms)", duration_cast(system_clock::now() - lastTick).count(), timing_update_states.count(), timing_client_tick.count(), diff --git a/server/src/VirtualServer.cpp b/server/src/VirtualServer.cpp index c3c4eb8..52fc226 100644 --- a/server/src/VirtualServer.cpp +++ b/server/src/VirtualServer.cpp @@ -448,7 +448,7 @@ inline vector split_hosts(const std::string& message, char delimiter) { bool VirtualServer::start(std::string& error) { { - threads::Mutex lock(this->stateLock); + std::unique_lock state_lock{this->state_mutex}; if(this->state != ServerState::OFFLINE){ error = "Server isn't offline"; return false; @@ -591,7 +591,7 @@ bool VirtualServer::start(std::string& error) { this->music_manager_->connectBots(); { - threads::MutexLock lock(this->stateLock); + std::unique_lock state_lock{this->state_mutex}; this->state = ServerState::ONLINE; } return true; @@ -610,8 +610,10 @@ bool VirtualServer::running() { void VirtualServer::preStop(const std::string& reason) { { - threads::MutexLock lock(this->stateLock); - if(!this->running() && this->state != ServerState::SUSPENDING) return; + std::unique_lock state_lock{this->state_mutex}; + if(!this->running() && this->state != ServerState::SUSPENDING) { + return; + } this->state = ServerState::SUSPENDING; } @@ -634,7 +636,7 @@ void VirtualServer::stop(const std::string& reason, bool disconnect_query) { auto self_lock = this->self.lock(); assert(self_lock); { - threads::MutexLock lock(this->stateLock); + std::unique_lock state_lock{this->state_mutex}; if(!this->running() && this->state != ServerState::SUSPENDING) return; this->state = ServerState::SUSPENDING; } @@ -681,7 +683,7 @@ void VirtualServer::stop(const std::string& reason, bool disconnect_query) { properties()[property::VIRTUALSERVER_UPTIME] = 0; { - threads::MutexLock lock(this->stateLock); + std::unique_lock state_lock{this->state_mutex}; this->state = ServerState::OFFLINE; } this->serverRoot->server = nullptr; @@ -961,8 +963,7 @@ vector cache) { + bool calculate_granted) { ClientPermissionCalculator calculator{this->ref(), client_dbid, client_type, channel_id}; return calculator.calculate_permissions(permissions, calculate_granted); } @@ -972,9 +973,8 @@ permission::v2::PermissionFlaggedValue VirtualServer::calculate_permission( ClientDbId cldbid, ClientType type, ChannelId channel, - bool granted, - std::shared_ptr cache) { - auto result = this->calculate_permissions({permission}, cldbid, type, channel, granted, cache); + bool granted) { + auto result = this->calculate_permissions({permission}, cldbid, type, channel, granted); if(result.empty()) return {0, false}; return result.front().second; diff --git a/server/src/VirtualServer.h b/server/src/VirtualServer.h index 8e5537b..314da57 100644 --- a/server/src/VirtualServer.h +++ b/server/src/VirtualServer.h @@ -15,7 +15,7 @@ #include "manager/BanManager.h" #include "Definitions.h" #include "ConnectionStatistics.h" -#include "manager/TokeManager.h" +#include "manager/TokenManager.h" #include "manager/ComplainManager.h" #include "DatabaseHelper.h" #include "manager/LetterManager.h" @@ -226,8 +226,7 @@ namespace ts { ClientDbId, ClientType type, ChannelId channel, - bool granted = false, - std::shared_ptr cache = nullptr + bool granted = false ); std::vector> calculate_permissions( @@ -235,8 +234,7 @@ namespace ts { ClientDbId, ClientType type, ChannelId channel, - bool granted = false, - std::shared_ptr cache = nullptr + bool granted = false ); bool verifyServerPassword(std::string, bool hashed = false); @@ -301,8 +299,8 @@ namespace ts { std::weak_ptr self; //Locks by tick, start and stop - threads::Mutex stateLock; - ServerState::value state = ServerState::OFFLINE; + std::shared_mutex state_mutex{}; + ServerState::value state{ServerState::OFFLINE}; task_id tick_task_id{}; std::chrono::system_clock::time_point lastTick; @@ -348,9 +346,8 @@ namespace ts { std::shared_ptr serverRoot = nullptr; std::shared_ptr serverAdmin = nullptr; - threads::Mutex join_attempts_lock; + std::mutex join_attempts_lock; std::map join_attempts; - threads::Mutex join_lock; std::chrono::system_clock::time_point join_last_decrease; std::chrono::milliseconds spoken_time{0}; diff --git a/server/src/VirtualServerManager.cpp b/server/src/VirtualServerManager.cpp index d26856b..af967c9 100644 --- a/server/src/VirtualServerManager.cpp +++ b/server/src/VirtualServerManager.cpp @@ -412,7 +412,7 @@ bool VirtualServerManager::deleteServer(shared_ptr server) { } } { - threads::MutexLock locK(server->stateLock); + std::unique_lock state_lock{server->state_mutex}; server->state = ServerState::DELETING; } diff --git a/server/src/channel/ClientChannelView.cpp b/server/src/channel/ClientChannelView.cpp index 82b976a..1a188ac 100644 --- a/server/src/channel/ClientChannelView.cpp +++ b/server/src/channel/ClientChannelView.cpp @@ -123,11 +123,10 @@ std::shared_ptr ClientChannelView::find_channel(const std::shared_ptr return head ? static_pointer_cast(head->entry) : nullptr; } -std::deque> ClientChannelView::insert_channels(shared_ptr head, bool test_permissions, bool first_only, std::shared_ptr cache) { +std::deque> ClientChannelView::insert_channels(shared_ptr head, bool test_permissions, bool first_only) { std::deque> result; - if(!cache && test_permissions) cache = make_shared(); - bool has_perm = !test_permissions || permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false, cache)); + bool has_perm = !test_permissions || permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false)); bool first = true; while(head) { if(!first && first_only) break; @@ -136,7 +135,7 @@ std::deque> ClientChannelView::insert_channels(shared auto channel = dynamic_pointer_cast(head->entry); if(this->channel_visible(channel)) { if(head->child_head) { - for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false, cache)) + for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false)) result.push_back(sub); } @@ -183,7 +182,7 @@ std::deque> ClientChannelView::insert_channels(shared result.push_back(entry); if(head->child_head) { - for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false, cache)) + for(const auto& sub : this->insert_channels(head->child_head, test_permissions, false)) result.push_back(sub); } head = head->next; @@ -244,11 +243,9 @@ std::deque> ClientChannelView::show_channel(std::shar } std::deque> ClientChannelView::test_channel(std::shared_ptr l_old, - std::shared_ptr channel_new, shared_ptr cache) { - if(!cache) cache = make_shared(); - + std::shared_ptr channel_new) { std::deque> result; - bool has_perm = permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false, cache)); + bool has_perm = permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false)); if(has_perm) return {}; deque> parents = {l_old}; @@ -284,14 +281,13 @@ std::deque> ClientChannelView::test_channel(std::shar } std::deque>> ClientChannelView::update_channel( - std::shared_ptr l_channel, std::shared_ptr l_own, shared_ptr cache) { - return update_channel_path(std::move(l_channel), std::move(l_own), std::move(cache), 1); + std::shared_ptr l_channel, std::shared_ptr l_own) { + return update_channel_path(std::move(l_channel), std::move(l_own), 1); } -std::deque>> ClientChannelView::update_channel_path(std::shared_ptr l_channel, std::shared_ptr l_own, shared_ptr cache, ssize_t length) { - if(!cache) cache = make_shared(); +std::deque>> ClientChannelView::update_channel_path(std::shared_ptr l_channel, std::shared_ptr l_own, ssize_t length) { std::deque>> result; - bool has_perm = permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false, cache)); + bool has_perm = permission::v2::permission_granted(1, owner->calculate_permission(permission::b_channel_ignore_view_power, 0, false)); while(l_channel && length-- != 0) { auto b_channel = dynamic_pointer_cast(l_channel->entry); @@ -313,20 +309,20 @@ std::deque>> ClientChannelView::updat if(visible) { for(const auto& entry : this->show_channel(l_channel, visible)) result.emplace_back(true, entry); - for(const auto& entry : this->insert_channels(l_channel->child_head, true, false, cache)) + for(const auto& entry : this->insert_channels(l_channel->child_head, true, false)) result.emplace_back(true, entry); } l_channel = l_channel->next; continue; /* all subchannels had been checked */ } else if(visible && !has_perm) { - for(const auto& entry : this->test_channel(l_channel, l_own, cache)) + for(const auto& entry : this->test_channel(l_channel, l_own)) result.emplace_back(false, entry); } //Root node is okey, test children if(l_channel->child_head) { - auto entries = this->update_channel_path(l_channel->child_head, l_own, cache, -1); + auto entries = this->update_channel_path(l_channel->child_head, l_own, -1); result.insert(result.end(), entries.begin(), entries.end()); } diff --git a/server/src/channel/ClientChannelView.h b/server/src/channel/ClientChannelView.h index 258f539..34d5f05 100644 --- a/server/src/channel/ClientChannelView.h +++ b/server/src/channel/ClientChannelView.h @@ -56,8 +56,7 @@ namespace ts { std::deque> insert_channels( std::shared_ptr /* head */, bool test_permissions, - bool first_only, - std::shared_ptr cache = nullptr + bool first_only ); /* shows the specific channel and their parents */ @@ -69,22 +68,19 @@ namespace ts { /* remove invalid channel */ std::deque> test_channel( std::shared_ptr /* old channel */, - std::shared_ptr /* new channel */, - std::shared_ptr cache = nullptr + std::shared_ptr /* new channel */ ); /* [{ add := true | delete := false, channel}] */ std::deque>> update_channel( std::shared_ptr /* channel */, - std::shared_ptr /* own channel */, - std::shared_ptr cache = nullptr + std::shared_ptr /* own channel */ ); /* [{ add := true | delete := false, channel}] */ std::deque>> update_channel_path( std::shared_ptr /* channel */, std::shared_ptr /* own channel */, - std::shared_ptr cache = nullptr, ssize_t length = -1 ); diff --git a/server/src/client/ConnectedClient.cpp b/server/src/client/ConnectedClient.cpp index 058f093..9e1ef6c 100644 --- a/server/src/client/ConnectedClient.cpp +++ b/server/src/client/ConnectedClient.cpp @@ -72,7 +72,7 @@ bool ConnectedClient::loadDataForCurrentServer() { return false; } - this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp(); + serverInstance->databaseHelper()->updateClientIpAddress(this->getServerId(), this->getClientDatabaseId(), this->getLoggingPeerIp()); return true; } @@ -1162,7 +1162,6 @@ permission::PermissionType ConnectedClient::calculate_and_get_join_state(const s } auto channel_id = channel->channelId(); - auto permission_cache = make_shared(); switch(channel->channelType()) { case ChannelType::permanent: if(!permission::v2::permission_granted(1, this->calculate_permission(permission::b_channel_join_permanent, channel_id))) { diff --git a/server/src/client/ConnectedClientNotifyHandler.cpp b/server/src/client/ConnectedClientNotifyHandler.cpp index 70d6575..e4b1cf7 100644 --- a/server/src/client/ConnectedClientNotifyHandler.cpp +++ b/server/src/client/ConnectedClientNotifyHandler.cpp @@ -380,8 +380,9 @@ bool ConnectedClient::notifyConnectionInfo(const shared_ptr &ta } if(info) { - for(const auto& [key, value] : info->properties) + for(const auto& [key, value] : info->properties) { bulk.put(key, value); + } } else { //Fill in what we can, else we trust the client if(target->getType() == ClientType::CLIENT_TEASPEAK || target->getType() == ClientType::CLIENT_TEAMSPEAK || target->getType() == ClientType::CLIENT_WEB) { @@ -403,13 +404,15 @@ bool ConnectedClient::notifyConnectionInfo(const shared_ptr &ta bulk.put(property::CONNECTION_PACKETS_SENT_SPEECH, stats.connection_packets_sent[stats::ConnectionStatistics::category::VOICE]); } } + if(auto vc = dynamic_pointer_cast(target); vc) { bulk.put(property::CONNECTION_PING, floor(vc->current_ping()).count()); bulk.put(property::CONNECTION_PING_DEVIATION, vc->current_ping_deviation()); } #ifdef COMPILE_WEB_CLIENT - else if(dynamic_pointer_cast(target)) + else if(dynamic_pointer_cast(target)) { bulk.put(property::CONNECTION_PING, floor(dynamic_pointer_cast(target)->client_ping()).count()); + } #endif if(auto vc = dynamic_pointer_cast(target); vc){ @@ -823,7 +826,7 @@ bool ConnectedClient::notifyChannelSubscribed(const dequeserver->getClientsByChannel(ch).empty() ? ch->emptySince() : 0; + notify[index]["es"] = this->server->getClientsByChannel(ch).empty() ? ch->empty_seconds() : 0; notify[index++]["cid"] = ch->channelId(); } this->sendCommand(notify); @@ -834,7 +837,7 @@ bool ConnectedClient::notifyChannelUnsubscribed(const dequeserver->getClientsByChannel(ch).empty() ? ch->emptySince() : 0; + notify[index]["es"] = this->server->getClientsByChannel(ch).empty() ? ch->empty_seconds() : 0; notify[index++]["cid"] = ch->channelId(); } this->sendCommand(notify); diff --git a/server/src/client/DataClient.cpp b/server/src/client/DataClient.cpp index c76e142..0b364cc 100644 --- a/server/src/client/DataClient.cpp +++ b/server/src/client/DataClient.cpp @@ -152,15 +152,14 @@ bool DataClient::loadDataForCurrentServer() { std::vector> DataClient::calculate_permissions( const std::deque &permissions, ChannelId channel, - bool granted, - std::shared_ptr) { + bool granted) { ts::server::ClientPermissionCalculator calculator{this, channel}; return calculator.calculate_permissions(permissions, granted); } permission::v2::PermissionFlaggedValue DataClient::calculate_permission( - permission::PermissionType permission, ChannelId channel, bool granted, std::shared_ptr) { + permission::PermissionType permission, ChannelId channel, bool granted) { ts::server::ClientPermissionCalculator calculator{this, channel}; return calculator.calculate_permission(permission, granted); } diff --git a/server/src/client/DataClient.h b/server/src/client/DataClient.h index 938f65e..1d4fcff 100644 --- a/server/src/client/DataClient.h +++ b/server/src/client/DataClient.h @@ -43,14 +43,12 @@ namespace ts { * This method can be called from everywhere without any locking needed. * @param channel * @param granted - * @param cache * @return */ permission::v2::PermissionFlaggedValue calculate_permission( permission::PermissionType, ChannelId channel, - bool granted = false, - std::shared_ptr cache = nullptr + bool granted = false ); /** @@ -58,14 +56,12 @@ namespace ts { * This method can be called from everywhere without any locking needed. * @param channel * @param granted - * @param cache * @return */ std::vector> calculate_permissions( const std::deque&, ChannelId channel, - bool granted = false, - std::shared_ptr cache = nullptr + bool granted = false ); virtual std::vector> assignedServerGroups(); diff --git a/server/src/client/SpeakingClient.cpp b/server/src/client/SpeakingClient.cpp index f7f9eda..1c95cc2 100644 --- a/server/src/client/SpeakingClient.cpp +++ b/server/src/client/SpeakingClient.cpp @@ -100,7 +100,7 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) { TIMING_START(timings); { - lock_guard lock(this->server->join_attempts_lock); + lock_guard lock(this->server->join_attempts_lock); auto client_address = this->getPeerIp(); auto& client_join_attempts = this->server->join_attempts[client_address]; auto& general_join_attempts = this->server->join_attempts["_"]; @@ -252,9 +252,11 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) { } TIMING_STEP(timings, "valid hw ip"); - if(!permission::v2::permission_granted(1, permissions[permission::b_virtualserver_join_ignore_password])) - if(!this->server->verifyServerPassword(cmd["client_server_password"].string(), true)) + if(!permission::v2::permission_granted(1, permissions[permission::b_virtualserver_join_ignore_password])) { + if(!this->server->verifyServerPassword(cmd["client_server_password"].string(), true)) { return command_result{error::server_invalid_password}; + } + } if(!config::server::clients::ignore_max_clone_permissions) { size_t clones_uid = 0; @@ -300,7 +302,7 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) { string fullReason = string() + "You are banned " + (banEntry->serverId == 0 ? "globally" : "from this server") + ". Reason: \"" + banEntry->reason + "\". Ban expires "; string time; - if(banEntry->until.time_since_epoch().count() != 0){ + if(banEntry->until.time_since_epoch().count() != 0) { time += "in "; auto seconds = chrono::ceil(banEntry->until - chrono::system_clock::now()).count(); tm p{}; @@ -310,30 +312,43 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) { p.tm_year++; seconds -= 365 * 24 * 60 * 60; } + while(seconds >= 24 * 60 * 60){ p.tm_yday++; seconds -= 24 * 60 * 60; } + while(seconds >= 60 * 60){ p.tm_hour++; seconds -= 60 * 60; } + while(seconds >= 60){ p.tm_min++; seconds -= 60; } p.tm_sec = (int) seconds; - if(p.tm_year > 0) + if(p.tm_year > 0) { time += to_string(p.tm_year) + " years, "; - if(p.tm_yday > 0) + } + + if(p.tm_yday > 0) { time += to_string(p.tm_yday) + " days, "; - if(p.tm_hour > 0) + } + + if(p.tm_hour > 0) { time += to_string(p.tm_hour) + " hours, "; - if(p.tm_min > 0) + } + + if(p.tm_min > 0) { time += to_string(p.tm_min) + " minutes, "; - if(p.tm_sec > 0) + } + + if(p.tm_sec > 0) { time += to_string(p.tm_sec) + " seconds, "; + } + if(time.empty()) time = "now, "; time = time.substr(0, time.length() - 2); } else time = "never"; @@ -344,11 +359,12 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) { TIMING_STEP(timings, "ban resolve"); size_t count = 0; - { - for(const auto &cl : this->server->getClients()) - if((cl->getType() == CLIENT_TEAMSPEAK || cl->getType() == CLIENT_WEB || cl->getType() == CLIENT_TEASPEAK || cl->getType() == CLIENT_MUSIC)) - if(cl->connectionState() <= ConnectionState::CONNECTED && cl->connectionState() >= ConnectionState::INIT_HIGH) - count++; + for(const auto &cl : this->server->getClients()) { + if((cl->getType() == CLIENT_TEAMSPEAK || cl->getType() == CLIENT_WEB || cl->getType() == CLIENT_TEASPEAK || cl->getType() == CLIENT_MUSIC)) { + if(cl->connectionState() <= ConnectionState::CONNECTED && cl->connectionState() >= ConnectionState::INIT_HIGH) { + count++; + } + } } auto maxClients = this->server->properties()[property::VIRTUALSERVER_MAXCLIENTS].as_or(0); @@ -356,15 +372,17 @@ command_result SpeakingClient::handleCommandClientInit(Command& cmd) { bool allowReserved = permission::v2::permission_granted(1, permissions[permission::b_client_use_reserved_slot]); if(reserved > maxClients){ - if(!allowReserved) + if(!allowReserved) { return command_result{error::server_maxclients_reached}; - } else if(maxClients - (allowReserved ? 0 : reserved) <= count) + } + } else if(maxClients - (allowReserved ? 0 : reserved) <= count) { return command_result{error::server_maxclients_reached}; + } TIMING_STEP(timings, "max clients"); auto old_last_connected = this->properties()[property::CLIENT_LASTCONNECTED].as_or(0); - this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp(); + serverInstance->databaseHelper()->updateClientIpAddress(this->getServerId(), this->getClientDatabaseId(), this->getLoggingPeerIp()); this->properties()[property::CLIENT_LASTCONNECTED] = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); this->properties()[property::CLIENT_TOTALCONNECTIONS].increment_by(1); { diff --git a/server/src/client/command_handler/channel.cpp b/server/src/client/command_handler/channel.cpp index 86d7adf..454b3fa 100644 --- a/server/src/client/command_handler/channel.cpp +++ b/server/src/client/command_handler/channel.cpp @@ -2167,7 +2167,7 @@ command_result ConnectedClient::handleCommandChannelInfo(Command &cmd) { for (const auto &prop : channel->properties()->list_properties(property::FLAG_CHANNEL_VIEW | property::FLAG_CHANNEL_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) res[prop.type().name] = prop.value(); - res["seconds_empty"] = channel->emptySince(); + res["seconds_empty"] = channel->empty_seconds(); res["pid"] = res["cpid"].string(); this->sendCommand(res); diff --git a/server/src/client/command_handler/client.cpp b/server/src/client/command_handler/client.cpp index 97a4e85..de25f49 100644 --- a/server/src/client/command_handler/client.cpp +++ b/server/src/client/command_handler/client.cpp @@ -513,7 +513,10 @@ command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std: logError(this->getServerId(), R"([{}] Tried to change a client property to an invalid value for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); continue; } - if(client->properties()[&info].as_unchecked() == cmd[key].as()) continue; + + if(client->properties()[&info].as_unchecked() == cmd[key].as()) { + continue; + } if (info == property::CLIENT_DESCRIPTION) { if (self) { @@ -836,7 +839,7 @@ command_result ConnectedClient::handleCommandClientList(Command &cmd) { if (cmd.hasParm("country")) result[index]["client_country"] = client->properties()[property::CLIENT_COUNTRY].as_unchecked(); if (cmd.hasParm("ip")) - result[index]["connection_client_ip"] = allow_ip ? client->properties()[property::CONNECTION_CLIENT_IP].as_unchecked() : "hidden"; + result[index]["connection_client_ip"] = allow_ip ? client->getPeerIp() : "hidden"; if (cmd.hasParm("icon")) result[index]["client_icon_id"] = client->properties()[property::CLIENT_ICON_ID].as_unchecked(); @@ -1098,7 +1101,7 @@ command_result ConnectedClient::handleCommandClientDbInfo(Command &cmd) { auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, info->client_database_id, ClientType::CLIENT_TEAMSPEAK); if(allow_ip) { - bulk.put_unchecked("client_lastip", (*props)[property::CONNECTION_CLIENT_IP].as_unchecked()); + bulk.put_unchecked("client_lastip", info->client_ip); } else { bulk.put_unchecked("client_lastip", "hidden"); } @@ -1244,22 +1247,56 @@ command_result ConnectedClient::handleCommandClientInfo(Command &cmd) { continue; } - for (const auto &key : client->properties()->list_properties(property::FLAG_CLIENT_VIEW | property::FLAG_CLIENT_VARIABLE | property::FLAG_CLIENT_INFO, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) + for (const auto &key : client->properties()->list_properties(property::FLAG_CLIENT_VIEW | property::FLAG_CLIENT_VARIABLE | property::FLAG_CLIENT_INFO, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { res[result_index][std::string{key.type().name}] = key.value(); - if(view_remote) - res[result_index]["connection_client_ip"] = client->properties()[property::CONNECTION_CLIENT_IP].value(); - else - res[result_index]["connection_client_ip"] = "hidden"; - res[result_index]["client_idle_time"] = duration_cast(system_clock::now() - client->idleTimestamp).count(); - res[result_index]["connection_connected_time"] = duration_cast(system_clock::now() - client->connectTimestamp).count(); - { - auto channel = client->currentChannel; - if(channel) - res[result_index]["cid"] = channel->channelId(); - else - res[result_index]["cid"] = 0; } + res[result_index]["cid"] = client->getChannelId(); + + if(view_remote) { + res[result_index][property::CONNECTION_CLIENT_IP] = client->getPeerIp(); + } else { + res[result_index][property::CONNECTION_CLIENT_IP] = "hidden"; + } + res[result_index][property::CONNECTION_CONNECTED_TIME] = duration_cast(system_clock::now() - client->connectTimestamp).count(); + + auto total_stats = this->getConnectionStatistics()->total_stats(); + res["connection_packets_sent_total"] = std::accumulate(total_stats.connection_packets_sent.begin(), total_stats.connection_packets_sent.end(), (size_t) 0U); + res["connection_bytes_sent_total"] = std::accumulate(total_stats.connection_bytes_sent.begin(), total_stats.connection_bytes_sent.end(), (size_t) 0U); + res["connection_packets_received_total"] = std::accumulate(total_stats.connection_packets_received.begin(), total_stats.connection_packets_received.end(), (size_t) 0U); + res["connection_bytes_received_total"] = std::accumulate(total_stats.connection_bytes_received.begin(), total_stats.connection_bytes_received.end(), (size_t) 0U); + + auto report_second = this->getConnectionStatistics()->second_stats(); + auto report_minute = this->getConnectionStatistics()->minute_stats(); + res["connection_bandwidth_sent_last_second_total"] = std::accumulate(report_second.connection_bytes_sent.begin(), report_second.connection_bytes_sent.end(), (size_t) 0U); + res["connection_bandwidth_sent_last_minute_total"] = std::accumulate(report_minute.connection_bytes_sent.begin(), report_minute.connection_bytes_sent.end(), (size_t) 0U); + res["connection_bandwidth_received_last_second_total"] = std::accumulate(report_second.connection_bytes_received.begin(), report_second.connection_bytes_received.end(), (size_t) 0U); + res["connection_bandwidth_received_last_minute_total"] = std::accumulate(report_minute.connection_bytes_received.begin(), report_minute.connection_bytes_received.end(), (size_t) 0U); + + res["connection_filetransfer_bandwidth_sent"] = report_second.file_bytes_sent; + res["connection_filetransfer_bandwidth_received"] = report_second.file_bytes_received; + res["connection_filetransfer_bytes_sent_total"] = total_stats.file_bytes_sent; + res["connection_filetransfer_bytes_received_total"] = total_stats.file_bytes_received; + + float server2client_packetloss{0}; + float client2server_packetloss{0}; /* TODO: Parse from the client connect parameters? */ + if(auto vc = dynamic_pointer_cast(this->ref()); vc) { + server2client_packetloss = vc->current_packet_loss(); + } + + if(auto data{this->connection_info.data}; data) { + try { + client2server_packetloss = std::stof(data->properties["connection_server2client_packetloss_total"]); + } catch(std::exception&) {} + } + + res["connection_packetloss_total"] = (server2client_packetloss + client2server_packetloss) / 2; + res["connection_server2client_packetloss_total"] = server2client_packetloss; + res["connection_client2server_packetloss_total"] = client2server_packetloss; + + /* TODO: Is this really right? It might be property::CONNECTION_IDLE_TIME. It might also be that CONNECTION_IDLE_TIME should be client_idle_time */ + res[result_index]["client_idle_time"] = duration_cast(system_clock::now() - client->idleTimestamp).count(); + result_index++; } diff --git a/server/src/client/command_handler/helpers.h b/server/src/client/command_handler/helpers.h index 9139d15..5d36faa 100644 --- a/server/src/client/command_handler/helpers.h +++ b/server/src/client/command_handler/helpers.h @@ -4,7 +4,7 @@ /* use this for any action which will do something with the server */ #define ACTION_REQUIRES_GLOBAL_PERMISSION_CACHED(required_permission_type, required_value, cache) \ do { \ - if(!permission::v2::permission_granted(required_value, this->calculate_permission(required_permission_type, 0, false, cache))) \ + if(!permission::v2::permission_granted(required_value, this->calculate_permission(required_permission_type, 0, false))) \ return command_result{required_permission_type}; \ } while(0) @@ -56,76 +56,10 @@ auto channel_id = command.as(); \ auto l_channel = channel_id ? channel_tree->findLinkedChannel(command.as()) : nullptr; \ if (!l_channel && (channel_id != 0 || force)) return command_result{error::channel_invalid_id, "Cant resolve channel"}; \ - - -//TODO: Map permsid! -#define PARSE_PERMISSION(cmd) \ -permission::PermissionType permType = permission::PermissionType::unknown; \ -bool grant = false; \ -if (cmd[index].has("permid")) { \ - permType = cmd[index]["permid"].as(); \ - if ((permType & PERM_ID_GRANT) != 0) { \ - grant = true; \ - permType &= ~PERM_ID_GRANT; \ - } \ -} else if (cmd[index].has("permsid")) { \ - auto resv = permission::resolvePermissionData(cmd[index]["permsid"].as()); \ - permType = resv->type; \ - if (resv->grantName() == cmd[index]["permsid"].as()) grant = true; \ -} \ -if (permission::resolvePermissionData(permType)->type == permission::PermissionType::unknown) { \ - if (conOnError) continue; \ - return ts::command_result{error::parameter_invalid}; \ -} - - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" inline bool permission_require_granted_value(ts::permission::PermissionType type) { using namespace ts; - /* - switch (type) { - case permission::i_permission_modify_power: - - case permission::i_channel_group_member_add_power: - case permission::i_channel_group_member_remove_power: - case permission::i_channel_group_modify_power: - - case permission::i_channel_group_needed_member_add_power: - case permission::i_channel_group_needed_member_remove_power: - case permission::i_channel_group_needed_modify_power: - - - case permission::i_server_group_member_add_power: - case permission::i_server_group_member_remove_power: - case permission::i_server_group_modify_power: - - case permission::i_server_group_needed_member_add_power: - case permission::i_server_group_needed_member_remove_power: - case permission::i_server_group_needed_modify_power: - - case permission::i_displayed_group_member_add_power: - case permission::i_displayed_group_member_remove_power: - case permission::i_displayed_group_modify_power: - - case permission::i_displayed_group_needed_member_add_power: - case permission::i_displayed_group_needed_member_remove_power: - case permission::i_displayed_group_needed_modify_power: - - case permission::i_channel_permission_modify_power: - case permission::i_channel_needed_permission_modify_power: - case permission::i_client_permission_modify_power: - case permission::i_client_needed_permission_modify_power: - - case permission::i_client_needed_kick_from_server_power: - case permission::i_client_needed_kick_from_channel_power: - case permission::i_client_kick_from_channel_power: - case permission::i_client_kick_from_server_power: - return true; - default: - return false; - } - */ switch (type) { case permission::i_icon_id: diff --git a/server/src/client/command_handler/misc.cpp b/server/src/client/command_handler/misc.cpp index e7fdb2f..d64fd3e 100644 --- a/server/src/client/command_handler/misc.cpp +++ b/server/src/client/command_handler/misc.cpp @@ -3056,7 +3056,7 @@ command_result ConnectedClient::handleCommandDummy_IpChange(ts::Command &cmd) { } } - this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp(); + serverInstance->databaseHelper()->updateClientIpAddress(this->getServerId(), this->getClientDatabaseId(), this->getLoggingPeerIp()); return command_result{error::ok}; } diff --git a/server/src/client/command_handler/server.cpp b/server/src/client/command_handler/server.cpp index 8284bcc..c678a66 100644 --- a/server/src/client/command_handler/server.cpp +++ b/server/src/client/command_handler/server.cpp @@ -71,7 +71,6 @@ command_result ConnectedClient::handleCommandServerEdit(Command &cmd) { ServerId serverId = target_server ? target_server->serverId : 0; auto group_manager = target_server ? target_server->group_manager() : serverInstance->group_manager(); - auto cache = make_shared(); map toApplay; for (auto &key : cmd[0].keys()) { if (key == "sid") continue; diff --git a/server/src/client/query/QueryClient.cpp b/server/src/client/query/QueryClient.cpp index 416c7fa..722c44c 100644 --- a/server/src/client/query/QueryClient.cpp +++ b/server/src/client/query/QueryClient.cpp @@ -66,15 +66,13 @@ void QueryClient::initialize_weak_reference(const std::shared_ptr(dynamic_pointer_cast(self)) ); - this->event_read = event_new(this->handle->event_io_loop, this->client_file_descriptor, EV_READ | EV_PERSIST, [](int a, short b, void* c){ - ((QueryClient *) c)->handle_event_read(a, b, c); }, this); - this->event_write = event_new(this->handle->event_io_loop, this->client_file_descriptor, EV_WRITE, [](int a, short b, void* c){ - ((QueryClient *) c)->handle_event_write(a, b, c); }, this); + this->event_read = event_new(this->handle->event_io_loop, this->client_file_descriptor, EV_READ | EV_PERSIST, QueryClient::handle_event_read, this); + this->event_write = event_new(this->handle->event_io_loop, this->client_file_descriptor, EV_WRITE, QueryClient::handle_event_write, this); } QueryClient::~QueryClient() { if(this->line_buffer) { - free(this->line_buffer); + ::free(this->line_buffer); this->line_buffer = nullptr; } @@ -259,12 +257,14 @@ void QueryClient::enqueue_write_buffer(const std::string_view &message) { buffer->unref(); } -void QueryClient::handle_event_write(int fd, short, void *) { +void QueryClient::handle_event_write(int fd, short, void *ptr_client) { + auto client = (QueryClient*) ptr_client; + NetworkBuffer* write_buffer{nullptr}; { - std::lock_guard buffer_lock{this->network_mutex}; - if(this->write_buffer_head) { - write_buffer = this->write_buffer_head->ref(); + std::lock_guard buffer_lock{client->network_mutex}; + if(client->write_buffer_head) { + write_buffer = client->write_buffer_head->ref(); } } @@ -274,17 +274,17 @@ void QueryClient::handle_event_write(int fd, short, void *) { write_buffer->unref(); if (errno == EINTR || errno == EAGAIN) { - std::lock_guard event_lock{this->network_mutex}; - if(this->event_write) { - event_add(this->event_write, nullptr); + std::lock_guard event_lock{client->network_mutex}; + if(client->event_write) { + event_add(client->event_write, nullptr); } } else { - logError(LOG_QUERY, "{} Failed to send message ({}/{}). Closing connection.", CLIENT_STR_LOG_PREFIX, errno, strerror(errno)); - this->close_connection(std::chrono::system_clock::time_point{}); + logError(LOG_QUERY, "{} Failed to send message ({}/{}). Closing connection.", client->getLoggingPrefix(), errno, strerror(errno)); + client->close_connection(std::chrono::system_clock::time_point{}); { - std::unique_lock event_lock{this->network_mutex}; - auto event_write_ = std::exchange(this->event_write, nullptr); + std::unique_lock event_lock{client->network_mutex}; + auto event_write_ = std::exchange(client->event_write, nullptr); event_lock.unlock(); if(event_write_) { @@ -315,23 +315,23 @@ void QueryClient::handle_event_write(int fd, short, void *) { /* Buffer finished, sending next one */ { - std::lock_guard buffer_lock{this->network_mutex}; - if(this->write_buffer_head == write_buffer) { + std::lock_guard buffer_lock{client->network_mutex}; + if(client->write_buffer_head == write_buffer) { /* Buffer successfully send. Sending the next one. */ - cleanup_buffer = this->write_buffer_head; + cleanup_buffer = client->write_buffer_head; - this->write_buffer_head = this->write_buffer_head->next_buffer; - if(this->write_buffer_head) { + client->write_buffer_head = client->write_buffer_head->next_buffer; + if(client->write_buffer_head) { /* we've a next buffer */ - write_buffer = this->write_buffer_head->ref(); + write_buffer = client->write_buffer_head->ref(); } else { - assert(this->write_buffer_tail == &write_buffer->next_buffer); + assert(client->write_buffer_tail == &write_buffer->next_buffer); write_buffer = nullptr; - this->write_buffer_tail = &this->write_buffer_head; + client->write_buffer_tail = &client->write_buffer_head; } - } else if(this->write_buffer_head) { + } else if(client->write_buffer_head) { /* Our buffer got dropped (who knows why). Just send the next one. */ - write_buffer = this->write_buffer_head->ref(); + write_buffer = client->write_buffer_head->ref(); } else { /* * Nothing more to write. @@ -349,15 +349,17 @@ void QueryClient::handle_event_write(int fd, short, void *) { /* This state should only be reached when no more messages are pending to write */ assert(!write_buffer); - if(this->state == ConnectionState::DISCONNECTING) { - this->handle->enqueue_query_connection_close(dynamic_pointer_cast(this->ref())); + if(client->state == ConnectionState::DISCONNECTING) { + client->handle->enqueue_query_connection_close(dynamic_pointer_cast(client->ref())); } } -void QueryClient::handle_event_read(int fd, short, void *) { +void QueryClient::handle_event_read(int fd, short, void *ptr_client) { static const size_t kReadBufferLength = 1024 * 8; uint8_t buffer[kReadBufferLength]; + auto client = (QueryClient*) ptr_client; + auto length = read(fd, buffer, kReadBufferLength); if(length <= 0){ if(errno == EINTR || errno == EAGAIN) { @@ -366,15 +368,15 @@ void QueryClient::handle_event_read(int fd, short, void *) { } if(length == 0 && errno == 0) { - logMessage(LOG_QUERY, "{} Connection closed. Client disconnected.", CLIENT_STR_LOG_PREFIX); + logMessage(LOG_QUERY, "{} Connection closed. Client disconnected.", client->getLoggingPrefix()); } else { - logMessage(LOG_QUERY, "{} Failed to received message ({}/{}). Closing connection.", CLIENT_STR_LOG_PREFIX, errno, strerror(errno)); + logMessage(LOG_QUERY, "{} Failed to received message ({}/{}). Closing connection.", client->getLoggingPrefix(), errno, strerror(errno)); } - this->close_connection(std::chrono::system_clock::time_point{}); + client->close_connection(std::chrono::system_clock::time_point{}); { - std::unique_lock network_lock{this->network_mutex}; - auto event_read_ = std::exchange(this->event_read, nullptr); + std::unique_lock network_lock{client->network_mutex}; + auto event_read_ = std::exchange(client->event_read, nullptr); network_lock.unlock(); if(event_read_) { @@ -387,7 +389,7 @@ void QueryClient::handle_event_read(int fd, short, void *) { return; } - this->handle_message_read(std::string_view{(const char *) buffer, (size_t) length}); + client->handle_message_read(std::string_view{(const char *) buffer, (size_t) length}); } inline bool is_ssl_handshake_header(const std::string_view& buffer) { diff --git a/server/src/client/query/QueryClient.h b/server/src/client/query/QueryClient.h index aa58cab..bccf450 100644 --- a/server/src/client/query/QueryClient.h +++ b/server/src/client/query/QueryClient.h @@ -74,12 +74,12 @@ namespace ts::server { void tick_query(); /* Methods will be called within the io loop (single thread) */ - void handle_event_read(int, short, void*); + static void handle_event_read(int, short, void*); void handle_message_read(const std::string_view& /* message */); void handle_decoded_message(const std::string_view& /* message */); /* Methods will be called within the io loop (single thread) */ - void handle_event_write(int, short, void*); + static void handle_event_write(int, short, void*); void send_message(const std::string_view&); void enqueue_write_buffer(const std::string_view& /* message */); private: diff --git a/server/src/client/query/QueryClientCommands.cpp b/server/src/client/query/QueryClientCommands.cpp index 404ea31..f1c3f31 100644 --- a/server/src/client/query/QueryClientCommands.cpp +++ b/server/src/client/query/QueryClientCommands.cpp @@ -610,7 +610,7 @@ command_result QueryClient::handleCommandChannelList(Command& cmd) { channel->properties()[property::CHANNEL_TOPIC].as_unchecked()); } if(cmd.hasParm("times") || cmd.hasParm("secondsempty")){ - result.put_unchecked(index, "seconds_empty", channel_clients == 0 ? channel->emptySince() : 0); + result.put_unchecked(index, "seconds_empty", channel_clients == 0 ? channel->empty_seconds() : 0); } index++; } diff --git a/server/src/client/voice/CryptSetupHandler.cpp b/server/src/client/voice/CryptSetupHandler.cpp index 42db285..076ee77 100644 --- a/server/src/client/voice/CryptSetupHandler.cpp +++ b/server/src/client/voice/CryptSetupHandler.cpp @@ -15,8 +15,9 @@ using namespace ts::connection; using namespace ts::server::server::udp; inline void generate_random(uint8_t *destination, size_t length) { - while(length-- > 0) + while(length-- > 0) { *(destination++) = (uint8_t) rand(); + } } CryptSetupHandler::CryptSetupHandler(VoiceClientConnection *connection) : connection{connection} { } @@ -97,8 +98,9 @@ CryptSetupHandler::CommandResult CryptSetupHandler::handleCommandClientInitIv(co { std::unique_lock server_channel_lock(client->server->get_channel_tree_lock()); /* we cant get moved if this is locked! */ - if(client->currentChannel) + if(client->currentChannel) { client->server->client_move(client->ref(), nullptr, nullptr, config::messages::timeout::connection_reinitialized, ViewReasonId::VREASON_TIMEOUT, false, server_channel_lock); + } } client->finalDisconnect(); @@ -132,8 +134,9 @@ CryptSetupHandler::CommandResult CryptSetupHandler::handleCommandClientInitIv(co /* normal TeamSpeak handling */ this->seed_client = base64::decode(cmd.value("alpha")); - if(this->seed_client.length() != 10) + if(this->seed_client.length() != 10) { return ts::command_result{error::parameter_invalid, "alpha"}; + } std::string clientOmega = base64::decode(cmd.value("omega")); //The identity public key std::string ip = cmd.value("ip"); diff --git a/server/src/client/voice/PacketEncoder.cpp b/server/src/client/voice/PacketEncoder.cpp index 8e6f943..f47adb4 100644 --- a/server/src/client/voice/PacketEncoder.cpp +++ b/server/src/client/voice/PacketEncoder.cpp @@ -43,15 +43,11 @@ void PacketEncoder::reset() { } while(whead) { - auto next = whead->next; - whead->unref(); - whead = next; + std::exchange(whead, whead->next)->unref(); } while(rhead) { - auto next = rhead->next; - rhead->unref(); - rhead = next; + std::exchange(rhead, rhead->next)->unref(); } } @@ -96,8 +92,7 @@ void PacketEncoder::send_packet_acknowledge(uint16_t pid, bool low) { #define MAX_COMMAND_PACKET_PAYLOAD_LENGTH (487) void PacketEncoder::send_command(const std::string_view &command, bool low, std::unique_ptr> ack_listener) { - bool own_data_buffer{false}; - void* own_data_buffer_ptr; /* immutable! */ + std::optional temp_data_buffer{}; const char* data_buffer{command.data()}; size_t data_length{command.length()}; @@ -121,10 +116,9 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std: /* we don't need to make the command longer than it is */ if(compressed_size < command.length()) { - own_data_buffer = true; - data_buffer = (char*) compressed_buffer; - own_data_buffer_ptr = compressed_buffer; data_length = compressed_size; + data_buffer = (char*) compressed_buffer; + temp_data_buffer = std::make_optional(compressed_buffer); head_pflags |= protocol::PacketFlag::Compressed; } else { ::free(compressed_buffer); @@ -151,6 +145,7 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std: packet->type_and_flags_ |= protocol::PacketFlag::Fragmented; break; } + data_buffer += bytes; } packets_head->type_and_flags_ |= protocol::PacketFlag::Fragmented; @@ -220,39 +215,29 @@ void PacketEncoder::send_command(const std::string_view &command, bool low, std: } this->callback_request_write(this->callback_data); - if(own_data_buffer) { - ::free(own_data_buffer_ptr); + if(temp_data_buffer.has_value()) { + ::free(*temp_data_buffer); } } void PacketEncoder::encrypt_pending_packets() { - protocol::OutgoingServerPacket* packets_head; + protocol::OutgoingServerPacket *packets_head, **packets_tail; { std::lock_guard wlock{this->write_queue_mutex}; - packets_head = this->encrypt_queue_head; - this->encrypt_queue_head = nullptr; - this->encrypt_queue_tail = &this->encrypt_queue_head; + packets_head = std::exchange(this->encrypt_queue_head, nullptr); + packets_tail = std::exchange(this->encrypt_queue_tail, &this->encrypt_queue_head); } - if(!packets_head) { - return; - } - - auto packet = packets_head; - auto packet_tail = packet; - while(packet) { - this->encrypt_outgoing_packet(packet); - packet = packet->next; - - if(packet) { - packet_tail = packet; - } + auto current_packet{packets_head}; + while(current_packet) { + this->encrypt_outgoing_packet(current_packet); + current_packet = current_packet->next; } { std::lock_guard wlock{this->write_queue_mutex}; *this->send_queue_tail = packets_head; - this->send_queue_tail = &packet_tail->next; + this->send_queue_tail = packets_tail; } } @@ -282,16 +267,19 @@ bool PacketEncoder::encrypt_outgoing_packet(ts::protocol::OutgoingServerPacket * crypt_key, crypt_nonce, error ); + if(!crypt_result) { this->callback_crypt_error(this->callback_data, CryptError::KEY_GENERATION_FAILED, error); return false; } } + return true; } PacketEncoder::BufferPopResult PacketEncoder::pop_write_buffer(protocol::OutgoingServerPacket *&result) { bool need_encrypt{false}, more_packets; + { std::lock_guard wlock{this->write_queue_mutex}; if(this->send_queue_head) { @@ -314,10 +302,12 @@ PacketEncoder::BufferPopResult PacketEncoder::pop_write_buffer(protocol::Outgoin this->encrypt_queue_head = nullptr; this->encrypt_queue_tail = &this->encrypt_queue_head; } + need_encrypt = true; } else { return BufferPopResult::DRAINED; } + result->next = nullptr; more_packets = this->send_queue_head != nullptr || this->encrypt_queue_head != nullptr; } diff --git a/server/src/client/voice/VoiceClientCommandHandler.cpp b/server/src/client/voice/VoiceClientCommandHandler.cpp index a593b4e..a8a28c8 100644 --- a/server/src/client/voice/VoiceClientCommandHandler.cpp +++ b/server/src/client/voice/VoiceClientCommandHandler.cpp @@ -100,7 +100,7 @@ command_result VoiceClient::handleCommandClientInit(Command &cmd) { auto requiredLevel = this->getServer()->properties()[property::VIRTUALSERVER_NEEDED_IDENTITY_SECURITY_LEVEL].as_unchecked(); if(security_level < requiredLevel) { - return command_result{error::client_could_not_validate_identity, to_string(requiredLevel)}; + return command_result{error::client_could_not_validate_identity, std::to_string(requiredLevel)}; } } diff --git a/server/src/client/voice/VoiceClientHandschake.cpp b/server/src/client/voice/VoiceClientHandschake.cpp deleted file mode 100644 index 69e17f3..0000000 --- a/server/src/client/voice/VoiceClientHandschake.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "VoiceClient.h" -#include "../../InstanceHandler.h" - -using namespace std; -using namespace std::chrono; -using namespace ts::server; -using namespace ts::protocol; -using namespace ts::connection; diff --git a/server/src/manager/TokeManager.cpp b/server/src/manager/TokenManager.cpp similarity index 99% rename from server/src/manager/TokeManager.cpp rename to server/src/manager/TokenManager.cpp index 8306572..3c079a1 100644 --- a/server/src/manager/TokeManager.cpp +++ b/server/src/manager/TokenManager.cpp @@ -7,7 +7,7 @@ #include #include #include -#include "TokeManager.h" +#include "TokenManager.h" using namespace std; using namespace std::chrono; diff --git a/server/src/manager/TokeManager.h b/server/src/manager/TokenManager.h similarity index 100% rename from server/src/manager/TokeManager.h rename to server/src/manager/TokenManager.h diff --git a/server/src/music/PlayablePlaylist.cpp b/server/src/music/PlayablePlaylist.cpp index d23e60f..9014f41 100644 --- a/server/src/music/PlayablePlaylist.cpp +++ b/server/src/music/PlayablePlaylist.cpp @@ -1,8 +1,6 @@ #include "PlayablePlaylist.h" #include "src/VirtualServer.h" #include "src/client/music/MusicClient.h" -#include "src/client/ConnectedClient.h" -#include "MusicPlaylist.h" #include using namespace ts; diff --git a/server/src/rtc/lib.cpp b/server/src/rtc/lib.cpp index dd92469..3a238e1 100644 --- a/server/src/rtc/lib.cpp +++ b/server/src/rtc/lib.cpp @@ -18,29 +18,29 @@ struct LibCallbackData { std::weak_ptr weak_ref; }; -#define log(...) \ -switch(level) { \ - case 0: \ - logTrace(__VA_ARGS__); \ - break; \ - case 1: \ - debugMessage(__VA_ARGS__); \ - break; \ - case 2: \ - logMessage(__VA_ARGS__); \ - break; \ - case 3: \ - logWarning(__VA_ARGS__); \ - break; \ - case 4: \ - logError(__VA_ARGS__); \ - break; \ - case 5: \ - logCritical(__VA_ARGS__); \ - break; \ - default: \ - debugMessage(__VA_ARGS__); \ - break; \ +#define log(...) \ +switch(level) { \ + case 0: \ + logTrace(__VA_ARGS__); \ + break; \ + case 1: \ + debugMessage(__VA_ARGS__); \ + break; \ + case 2: \ + logMessage(__VA_ARGS__); \ + break; \ + case 3: \ + logWarning(__VA_ARGS__); \ + break; \ + case 4: \ + logError(__VA_ARGS__); \ + break; \ + case 5: \ + logCritical(__VA_ARGS__); \ + break; \ + default: \ + debugMessage(__VA_ARGS__); \ + break; \ } void librtc_callback_log(uint8_t level, const void* callback_data_ptr, const char* message_ptr, uint32_t length) { diff --git a/server/src/server/PrecomputedPuzzles.cpp b/server/src/server/PrecomputedPuzzles.cpp index 576682c..ed61362 100644 --- a/server/src/server/PrecomputedPuzzles.cpp +++ b/server/src/server/PrecomputedPuzzles.cpp @@ -1,15 +1,23 @@ #include "./PrecomputedPuzzles.h" -#include "src/Configuration.h" +#include "..//Configuration.h" #include using namespace std; using namespace ts::server::udp; +Puzzle::Puzzle() { + mp_init_multi(&this->x, &this->n, &this->result, nullptr); +} + +Puzzle::~Puzzle() { + mp_clear_multi(&this->x, &this->n, &this->result, nullptr); +} + PuzzleManager::PuzzleManager() = default; PuzzleManager::~PuzzleManager() = default; size_t PuzzleManager::precomputed_puzzle_count() { - std::lock_guard lock{this->cache_lock}; + std::lock_guard lock{this->cache_mutex}; return this->cached_puzzles.size(); } @@ -18,14 +26,16 @@ bool PuzzleManager::precompute_puzzles(size_t amount) { std::mt19937 mt{rd()}; amount = 5; - while(this->precomputed_puzzle_count() < amount) + while(this->precomputed_puzzle_count() < amount) { this->generate_puzzle(mt); + } + return this->precomputed_puzzle_count() > 0; } std::shared_ptr PuzzleManager::next_puzzle() { { - std::lock_guard lock{this->cache_lock}; + std::lock_guard lock{this->cache_mutex}; auto it = this->cached_puzzles.begin() + (this->cache_index++ % this->cached_puzzles.size()); if((*it)->fail_count > 2) { this->cached_puzzles.erase(it); @@ -44,8 +54,9 @@ inline void random_number(std::mt19937& generator, mp_int *result, int length){ std::uniform_int_distribution dist{}; uint8_t buffer[length]; - for(auto& byte : buffer) + for(auto& byte : buffer) { byte = dist(generator); + } mp_zero(result); mp_read_unsigned_bin(result, buffer, length); @@ -56,7 +67,6 @@ inline bool solve_puzzle(Puzzle *puzzle) { mp_init(&exp); mp_2expt(&exp, puzzle->level); - if (mp_exptmod(&puzzle->x, &exp, &puzzle->n, &puzzle->result) != CRYPT_OK) { //Sometimes it fails (unknown why :D) mp_clear(&exp); return false; @@ -68,57 +78,51 @@ inline bool solve_puzzle(Puzzle *puzzle) { inline bool write_bin_data(mp_int& data, uint8_t* result, size_t length) { ulong n{length}; - if(auto err = mp_to_unsigned_bin_n(&data, result, &n); err) + if(auto err = mp_to_unsigned_bin_n(&data, result, &n); err) { return false; + } if(n != length) { auto off = length - n; - memcpy(result + off, result, n); + memmove(result + off, result, n); memset(result, 0, off); } return true; } void PuzzleManager::generate_puzzle(std::mt19937& random_generator) { - auto puzzle = new Puzzle{}; - + auto puzzle = std::make_shared(); puzzle->level = ts::config::voice::RsaPuzzleLevel; - mp_init_multi(&puzzle->x, &puzzle->n, &puzzle->result, nullptr); - generate_new: - static int x{0}; - if(x++ == 0 || true) { - mp_set(&puzzle->x, 1); - mp_set(&puzzle->n, 1); - //random_number(random_generator, &puzzle->x, 64); - //random_number(random_generator, &puzzle->n, 64); - } else { - const static std::string_view n_{"\x01\x7a\xc5\x8d\x28\x7a\x61\x58\xf6\xe3\x98\x60\x2f\x81\x9c\x8a\x48\xc9\x20\xd1\x59\xe0\x24\x75\x91\x27\x9f\x52\x1e\x2c\x24\x85\xa9\xdc\x74\xfa\x0b\x36\xf9\x6c\x77\xa3\x7c\xf9\xbb\xf7\x04\xad\xa3\x84\x0d\x97\x25\x54\x19\x72\x4f\x8f\xfc\x66\xbe\x41\xda\x95"}; - const static std::string_view x_{"\xd1\xef\xf0\x16\x34\x48\x56\x53\x15\x97\xa0\x28\xbd\x13\xce\xbf\xc2\xd6\x79\x9d\x21\x81\x83\x37\x8c\xe8\xee\xee\xa1\x22\xa4\xf5\x63\x33\x53\x0c\x38\x2f\x0a\x00\x53\x20\xc7\x93\x52\xa9\xd0\xc2\xfb\xbc\xc5\xc4\xc3\x54\xad\xcb\x49\x52\xc0\xd8\x97\x32\x94\xee"}; - mp_read_unsigned_bin(&puzzle->x, (unsigned char*) x_.data(), x_.length()); - mp_read_unsigned_bin(&puzzle->n, (unsigned char*) n_.data(), n_.length()); + while(true) { + random_number(random_generator, &puzzle->x, 64); + random_number(random_generator, &puzzle->n, 64); + + if(!solve_puzzle(&*puzzle)) { + continue; + } + + auto valid_x = mp_unsigned_bin_size(&puzzle->x) <= 64; + auto valid_n = mp_unsigned_bin_size(&puzzle->n) <= 64; + auto valid_result = mp_unsigned_bin_size(&puzzle->result) <= 64; + if(!valid_n || !valid_x || !valid_result) { + continue; + } + + if(!write_bin_data(puzzle->x, puzzle->data_x, 64)) { + continue; + } + + if(!write_bin_data(puzzle->n, puzzle->data_n, 64)) { + continue; + } + + if(!write_bin_data(puzzle->result, puzzle->data_result, 64)) { + continue; + } + + /* everything seems to be good */ + break; } - - if(!solve_puzzle(puzzle)) - goto generate_new; - - auto valid_x = mp_unsigned_bin_size(&puzzle->x) <= 64; - auto valid_n = mp_unsigned_bin_size(&puzzle->n) <= 64; - auto valid_result = mp_unsigned_bin_size(&puzzle->result) <= 64; - if(!valid_n || !valid_x || !valid_result) - goto generate_new; - - if(!write_bin_data(puzzle->x, puzzle->data_x, 64)) - goto generate_new; - - if(!write_bin_data(puzzle->n, puzzle->data_n, 64)) - goto generate_new; - - if(!write_bin_data(puzzle->result, puzzle->data_result, 64)) - goto generate_new; - - this->cached_puzzles.push_back(shared_ptr(puzzle, [](Puzzle* elm){ - mp_clear_multi(&elm->n, &elm->x, &elm->result, nullptr); - delete elm; - })); + this->cached_puzzles.push_back(std::move(puzzle)); } \ No newline at end of file diff --git a/server/src/server/PrecomputedPuzzles.h b/server/src/server/PrecomputedPuzzles.h index 0272c05..52022a5 100644 --- a/server/src/server/PrecomputedPuzzles.h +++ b/server/src/server/PrecomputedPuzzles.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,9 @@ namespace ts::server::udp { uint8_t data_result[64]{0}; size_t fail_count{0}; + + Puzzle(); + ~Puzzle(); }; class PuzzleManager { @@ -35,7 +39,7 @@ namespace ts::server::udp { void generate_puzzle(std::mt19937&); size_t cache_index{0}; - spin_mutex cache_lock{}; + std::mutex cache_mutex{}; std::vector> cached_puzzles{}; }; } \ No newline at end of file diff --git a/server/src/server/QueryServer.cpp b/server/src/server/QueryServer.cpp index e80f654..b8d3b7b 100644 --- a/server/src/server/QueryServer.cpp +++ b/server/src/server/QueryServer.cpp @@ -669,7 +669,7 @@ void QueryServer::tick_executor() { auto pending_closes = std::move(this->tick_pending_connection_close); if(!this->tick_active) { - if(this->tick_pending_connection_close.empty() && this->tick_pending_connection_close.empty()) { + if(pending_disconnects.empty() && pending_closes.empty()) { /* We're done with our work */ break; } diff --git a/server/src/server/QueryServer.h b/server/src/server/QueryServer.h index 1f2d6f3..040e59c 100644 --- a/server/src/server/QueryServer.h +++ b/server/src/server/QueryServer.h @@ -23,12 +23,6 @@ namespace ts { class VirtualServer; class QueryClient; - struct QueryLoginCredentials { - std::string username; - std::string password; - ClientUid uniqueId; - }; - class QueryAccount { public: std::string username; diff --git a/server/src/server/VoiceIOManager.cpp b/server/src/server/VoiceIOManager.cpp index cf74d86..401daab 100644 --- a/server/src/server/VoiceIOManager.cpp +++ b/server/src/server/VoiceIOManager.cpp @@ -5,7 +5,6 @@ #include "VoiceIOManager.h" #include "VoiceServer.h" #include "src/client/voice/VoiceClient.h" -#include "protocol/buffers.h" using namespace std; using namespace std::chrono; diff --git a/server/src/server/WebServer.cpp b/server/src/server/WebServer.cpp index 50f6e1b..eeddc40 100644 --- a/server/src/server/WebServer.cpp +++ b/server/src/server/WebServer.cpp @@ -104,20 +104,22 @@ void WebControlServer::on_client_receive(int _server_file_descriptor, short ev, int file_descriptor = accept(_server_file_descriptor, (struct sockaddr *) &remote_address, &address_length); if (file_descriptor < 0) { - if(errno == EAGAIN) + if(errno == EAGAIN) { return; + } if(errno == EMFILE || errno == ENFILE) { - if(errno == EMFILE) + if(errno == EMFILE) { logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process file descriptor limit."); - else + } else { logError(this->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process and system-wide file descriptor limit."); + } bool tmp_close_success = false; { lock_guard reserve_fd_lock(server_reserve_fd_lock); if(this->server_reserve_fd > 0) { - debugMessage(this->handle->getServerId(), "[Web] Trying to accept client with the reserved file descriptor to close the incomming connection."); + debugMessage(this->handle->getServerId(), "[Web] Trying to accept client with the reserved file descriptor to close the incoming connection."); auto _ = [&]{ if(close(this->server_reserve_fd) < 0) { debugMessage(this->handle->getServerId(), "[Web] Failed to close reserved file descriptor"); @@ -170,7 +172,7 @@ void WebControlServer::on_client_receive(int _server_file_descriptor, short ev, } - shared_ptr client = std::make_shared(this, file_descriptor); + auto client = std::make_shared(this, file_descriptor); memcpy(&client->remote_address, &remote_address, sizeof(remote_address)); client->initialize_weak_reference(client); client->initialize(); @@ -219,12 +221,12 @@ void WebControlServer::stop() { void WebControlServer::unregisterConnection(const std::shared_ptr& connection) { //TODO may checks? { - threads::MutexLock l(this->clientLock); - for(const auto& cl : this->clients) { - if(cl == connection) { - clients.erase(std::find(this->clients.begin(), this->clients.end(), cl)); - break; - } + std::lock_guard lock{this->clientLock}; + auto index = std::find(this->clients.begin(), this->clients.end(), connection); + if(index == this->clients.end()) { + return; } + + this->clients.erase(index); } } diff --git a/server/src/server/WebServer.h b/server/src/server/WebServer.h index ab54d99..2729b37 100644 --- a/server/src/server/WebServer.h +++ b/server/src/server/WebServer.h @@ -44,8 +44,8 @@ namespace ts { bool _running = false; std::deque> bindings; - std::mutex server_reserve_fd_lock; - int server_reserve_fd = -1; /* -1 = unset | 0 = in use | > 0 ready to use */ + std::mutex server_reserve_fd_lock{}; + int server_reserve_fd{-1}; /* -1 = unset | 0 = in use | > 0 ready to use */ //IO stuff std::chrono::system_clock::time_point accept_event_deleted; diff --git a/server/src/terminal/PipedTerminal.cpp b/server/src/terminal/PipedTerminal.cpp index b2c6384..938787b 100644 --- a/server/src/terminal/PipedTerminal.cpp +++ b/server/src/terminal/PipedTerminal.cpp @@ -179,8 +179,9 @@ bool process_next_command() { append_write_buffer("ok\n"); } - for(const auto& line : handle.response) + for(const auto& line : handle.response) { append_write_buffer(line + "\n"); + } append_write_buffer("\r\n"); append_write_buffer("\r\n"); diff --git a/server/things_to_test b/server/things_to_test deleted file mode 100644 index 6568313..0000000 --- a/server/things_to_test +++ /dev/null @@ -1,23 +0,0 @@ -permission calculation -channel group inheritance -general server & channel groups -- assignments -- removement -- temporary groups - -Notified channel & server member add/remove powers -Group edit if the notify group triggers - -Icon IDs seem to be buggy with groups -group permission copy dosn't work -permission reset - -servergroupclientlist - -server default group edit -channelgroupclientlist -temporary group/channel assignments -Test with `kCacheAllClients` and without `kCacheAllClients` - -TODO: -- Delete the group header \ No newline at end of file diff --git a/shared b/shared index 8fb93d1..8815874 160000 --- a/shared +++ b/shared @@ -1 +1 @@ -Subproject commit 8fb93d1d9d938b78dcf34e7f7953e3d41504d00f +Subproject commit 88158742e873a9ecc076091a76b52af85ca32687