From 3d91eee919de2ecec11102b4ba1f4caafa4d7a83 Mon Sep 17 00:00:00 2001 From: rubidium42 Date: Tue, 4 May 2021 19:32:37 +0200 Subject: Codechange: [Network] Move connection string parsing away from C-strings --- src/network/network.cpp | 114 +++++++++++++++++++---------------------- src/network/network_func.h | 2 +- src/network/network_internal.h | 2 +- src/openttd.cpp | 11 ++-- 4 files changed, 58 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/network/network.cpp b/src/network/network.cpp index 98b49e81f..a90f8197e 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -33,6 +33,7 @@ #include "../core/pool_func.hpp" #include "../gfx_func.h" #include "../error.h" +#include #include "../safeguards.h" @@ -453,36 +454,51 @@ static void CheckPauseOnJoin() * Converts a string to ip/port/company * Format: IP:port#company * - * connection_string will be re-terminated to separate out the hostname, port will - * be set to the port strings given by the user, inside the memory area originally - * occupied by connection_string. Similar for company, if set. + * Returns the IP part as a string view into the passed string. This view is + * valid as long the passed connection string is valid. If there is no port + * present in the connection string, the port reference will not be touched. + * When there is no company ID present in the connection string or company_id + * is nullptr, then company ID will not be touched. + * + * @param connection_string The string with the connection data. + * @param port The port reference to set. + * @param company_id The company ID to set, if available. + * @return A std::string_view into the connection string with the (IP) address part. */ -void ParseFullConnectionString(const char **company, const char **port, char *connection_string) +std::string_view ParseFullConnectionString(const std::string &connection_string, uint16 &port, CompanyID *company_id) { - bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':')); - for (char *p = connection_string; *p != '\0'; p++) { - switch (*p) { - case '[': - ipv6 = true; - break; - - case ']': - ipv6 = false; - break; - - case '#': - if (company == nullptr) continue; - *company = p + 1; - *p = '\0'; - break; - - case ':': - if (ipv6) break; - *port = p + 1; - *p = '\0'; - break; + std::string_view ip = connection_string; + if (company_id != nullptr) { + size_t offset = ip.find_last_of('#'); + if (offset != std::string::npos) { + std::string_view company_string = ip.substr(offset + 1); + ip = ip.substr(0, offset); + + uint8 company_value; + auto [_, err] = std::from_chars(company_string.data(), company_string.data() + company_string.size(), company_value); + if (err == std::errc()) { + if (company_value != COMPANY_NEW_COMPANY && company_value != COMPANY_SPECTATOR) { + if (company_value > MAX_COMPANIES || company_value == 0) { + *company_id = COMPANY_SPECTATOR; + } else { + /* "#1" means the first company, which has index 0. */ + *company_id = (CompanyID)(company_value - 1); + } + } else { + *company_id = (CompanyID)company_value; + } + } } } + + size_t port_offset = ip.find_last_of(':'); + size_t ipv6_close = ip.find_last_of(']'); + if (port_offset != std::string::npos && (ipv6_close == std::string::npos || ipv6_close < port_offset)) { + std::string_view port_string = ip.substr(port_offset + 1); + ip = ip.substr(0, port_offset); + std::from_chars(port_string.data(), port_string.data() + port_string.size(), port); + } + return ip; } /** @@ -493,16 +509,11 @@ void ParseFullConnectionString(const char **company, const char **port, char *co * @param default_port The default port to set port to if not in connection_string. * @return A valid NetworkAddress of the parsed information. */ -NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port) +NetworkAddress ParseConnectionString(const std::string &connection_string, uint16 default_port) { - char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH]; - strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); - - const char *port = nullptr; - ParseFullConnectionString(nullptr, &port, internal_connection_string); - - int rport = port != nullptr ? atoi(port) : default_port; - return NetworkAddress(internal_connection_string, rport); + uint16 port = default_port; + std::string_view ip = ParseFullConnectionString(connection_string, port); + return NetworkAddress(ip, port); } /** @@ -510,37 +521,16 @@ NetworkAddress ParseConnectionString(const std::string &connection_string, int d * NetworkAddress, where the string can be postfixed with "#company" to * indicate the requested company. * - * @param company Pointer to the company variable to set iff indicted. * @param connection_string The string to parse. * @param default_port The default port to set port to if not in connection_string. + * @param company Pointer to the company variable to set iff indicted. * @return A valid NetworkAddress of the parsed information. */ -static NetworkAddress ParseGameConnectionString(CompanyID *company, const std::string &connection_string, int default_port) +static NetworkAddress ParseGameConnectionString(const std::string &connection_string, uint16 default_port, CompanyID *company) { - char internal_connection_string[NETWORK_HOSTNAME_PORT_LENGTH + 4]; // 4 extra for the "#" and company - strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); - - const char *port_s = nullptr; - const char *company_s = nullptr; - ParseFullConnectionString(&company_s, &port_s, internal_connection_string); - - if (company_s != nullptr) { - uint company_value = atoi(company_s); - - if (company_value != COMPANY_NEW_COMPANY && company_value != COMPANY_SPECTATOR) { - if (company_value > MAX_COMPANIES || company_value == 0) { - *company = COMPANY_SPECTATOR; - } else { - /* "#1" means the first company, which has index 0. */ - *company = (CompanyID)(company_value - 1); - } - } else { - *company = (CompanyID)company_value; - } - } - - int port = port_s != nullptr ? atoi(port_s) : default_port; - return NetworkAddress(internal_connection_string, port); + uint16 port = default_port; + std::string_view ip = ParseFullConnectionString(connection_string, port, company); + return NetworkAddress(ip, port); } /** @@ -751,7 +741,7 @@ public: bool NetworkClientConnectGame(const std::string &connection_string, CompanyID default_company, const char *join_server_password, const char *join_company_password) { CompanyID join_as = default_company; - std::string resolved_connection_string = ParseGameConnectionString(&join_as, connection_string, NETWORK_DEFAULT_PORT).GetAddressAsString(false); + std::string resolved_connection_string = ParseGameConnectionString(connection_string, NETWORK_DEFAULT_PORT, &join_as).GetAddressAsString(false); if (!_network_available) return false; if (!NetworkValidateClientName()) return false; diff --git a/src/network/network_func.h b/src/network/network_func.h index ba26e06c0..ef81b0fb1 100644 --- a/src/network/network_func.h +++ b/src/network/network_func.h @@ -45,7 +45,7 @@ void NetworkReboot(); void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkGameLoop(); void NetworkBackgroundLoop(); -void ParseFullConnectionString(const char **company, const char **port, char *connection_string); +std::string_view ParseFullConnectionString(const std::string &connection_string, uint16 &port, CompanyID *company_id = nullptr); void NetworkStartDebugLog(const std::string &connection_string); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); diff --git a/src/network/network_internal.h b/src/network/network_internal.h index ef414c46a..2a2024bac 100644 --- a/src/network/network_internal.h +++ b/src/network/network_internal.h @@ -119,6 +119,6 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err); bool NetworkFindName(char *new_name, const char *last); const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed); -NetworkAddress ParseConnectionString(const std::string &connection_string, int default_port); +NetworkAddress ParseConnectionString(const std::string &connection_string, uint16 default_port); #endif /* NETWORK_INTERNAL_H */ diff --git a/src/openttd.cpp b/src/openttd.cpp index d4c7da6e4..d9e0b835e 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -405,7 +405,7 @@ void OpenBrowser(const char *url) struct AfterNewGRFScan : NewGRFScanCallback { Year startyear; ///< The start year. uint32 generation_seed; ///< Seed for the new game. - char *dedicated_host; ///< Hostname for the dedicated server. + std::string dedicated_host; ///< Hostname for the dedicated server. uint16 dedicated_port; ///< Port for the dedicated server. char *network_conn; ///< Information about the server to connect to, or nullptr. const char *join_server_password; ///< The password to join the server with. @@ -417,7 +417,7 @@ struct AfterNewGRFScan : NewGRFScanCallback { */ AfterNewGRFScan() : startyear(INVALID_YEAR), generation_seed(GENERATE_NEW_SEED), - dedicated_host(nullptr), dedicated_port(0), network_conn(nullptr), + dedicated_port(0), network_conn(nullptr), join_server_password(nullptr), join_company_password(nullptr), save_config(true) { @@ -458,7 +458,7 @@ struct AfterNewGRFScan : NewGRFScanCallback { if (startyear != INVALID_YEAR) IConsoleSetSetting("game_creation.starting_year", startyear); if (generation_seed != GENERATE_NEW_SEED) _settings_newgame.game_creation.generation_seed = generation_seed; - if (dedicated_host != nullptr) { + if (!dedicated_host.empty()) { _network_bind_list.clear(); _network_bind_list.emplace_back(dedicated_host); } @@ -565,10 +565,7 @@ int openttd_main(int argc, char *argv[]) dedicated = true; SetDebugString("net=6"); if (mgo.opt != nullptr) { - const char *port = nullptr; - ParseFullConnectionString(nullptr, &port, mgo.opt); - if (!StrEmpty(mgo.opt)) scanner->dedicated_host = mgo.opt; - if (port != nullptr) scanner->dedicated_port = atoi(port); + scanner->dedicated_host = ParseFullConnectionString(mgo.opt, scanner->dedicated_port); } break; case 'f': _dedicated_forks = true; break; -- cgit v1.2.3-54-g00ecf