1
0
Fork 0

Codechange: [Network] Move connection string parsing away from C-strings

pull/9199/head
rubidium42 2021-05-04 19:32:37 +02:00 committed by rubidium42
parent 6c4a65eeb8
commit 3d91eee919
4 changed files with 57 additions and 70 deletions

View File

@ -33,6 +33,7 @@
#include "../core/pool_func.hpp" #include "../core/pool_func.hpp"
#include "../gfx_func.h" #include "../gfx_func.h"
#include "../error.h" #include "../error.h"
#include <charconv>
#include "../safeguards.h" #include "../safeguards.h"
@ -453,36 +454,51 @@ static void CheckPauseOnJoin()
* Converts a string to ip/port/company * Converts a string to ip/port/company
* Format: IP:port#company * Format: IP:port#company
* *
* connection_string will be re-terminated to separate out the hostname, port will * Returns the IP part as a string view into the passed string. This view is
* be set to the port strings given by the user, inside the memory area originally * valid as long the passed connection string is valid. If there is no port
* occupied by connection_string. Similar for company, if set. * 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, ':')); std::string_view ip = connection_string;
for (char *p = connection_string; *p != '\0'; p++) { if (company_id != nullptr) {
switch (*p) { size_t offset = ip.find_last_of('#');
case '[': if (offset != std::string::npos) {
ipv6 = true; std::string_view company_string = ip.substr(offset + 1);
break; ip = ip.substr(0, offset);
case ']': uint8 company_value;
ipv6 = false; auto [_, err] = std::from_chars(company_string.data(), company_string.data() + company_string.size(), company_value);
break; if (err == std::errc()) {
if (company_value != COMPANY_NEW_COMPANY && company_value != COMPANY_SPECTATOR) {
case '#': if (company_value > MAX_COMPANIES || company_value == 0) {
if (company == nullptr) continue; *company_id = COMPANY_SPECTATOR;
*company = p + 1; } else {
*p = '\0'; /* "#1" means the first company, which has index 0. */
break; *company_id = (CompanyID)(company_value - 1);
}
case ':': } else {
if (ipv6) break; *company_id = (CompanyID)company_value;
*port = p + 1; }
*p = '\0'; }
break;
} }
} }
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. * @param default_port The default port to set port to if not in connection_string.
* @return A valid NetworkAddress of the parsed information. * @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]; uint16 port = default_port;
strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); std::string_view ip = ParseFullConnectionString(connection_string, port);
return NetworkAddress(ip, port);
const char *port = nullptr;
ParseFullConnectionString(nullptr, &port, internal_connection_string);
int rport = port != nullptr ? atoi(port) : default_port;
return NetworkAddress(internal_connection_string, rport);
} }
/** /**
@ -510,37 +521,16 @@ NetworkAddress ParseConnectionString(const std::string &connection_string, int d
* NetworkAddress, where the string can be postfixed with "#company" to * NetworkAddress, where the string can be postfixed with "#company" to
* indicate the requested company. * indicate the requested company.
* *
* @param company Pointer to the company variable to set iff indicted.
* @param connection_string The string to parse. * @param connection_string The string to parse.
* @param default_port The default port to set port to if not in connection_string. * @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. * @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 uint16 port = default_port;
strecpy(internal_connection_string, connection_string.c_str(), lastof(internal_connection_string)); std::string_view ip = ParseFullConnectionString(connection_string, port, company);
return NetworkAddress(ip, port);
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);
} }
/** /**
@ -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) 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; 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 (!_network_available) return false;
if (!NetworkValidateClientName()) return false; if (!NetworkValidateClientName()) return false;

View File

@ -45,7 +45,7 @@ void NetworkReboot();
void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkDisconnect(bool blocking = false, bool close_admins = true);
void NetworkGameLoop(); void NetworkGameLoop();
void NetworkBackgroundLoop(); 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 NetworkStartDebugLog(const std::string &connection_string);
void NetworkPopulateCompanyStats(NetworkCompanyStats *stats); void NetworkPopulateCompanyStats(NetworkCompanyStats *stats);

View File

@ -119,6 +119,6 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err);
bool NetworkFindName(char *new_name, const char *last); bool NetworkFindName(char *new_name, const char *last);
const char *GenerateCompanyPasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed); 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 */ #endif /* NETWORK_INTERNAL_H */

View File

@ -405,7 +405,7 @@ void OpenBrowser(const char *url)
struct AfterNewGRFScan : NewGRFScanCallback { struct AfterNewGRFScan : NewGRFScanCallback {
Year startyear; ///< The start year. Year startyear; ///< The start year.
uint32 generation_seed; ///< Seed for the new game. 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. uint16 dedicated_port; ///< Port for the dedicated server.
char *network_conn; ///< Information about the server to connect to, or nullptr. char *network_conn; ///< Information about the server to connect to, or nullptr.
const char *join_server_password; ///< The password to join the server with. const char *join_server_password; ///< The password to join the server with.
@ -417,7 +417,7 @@ struct AfterNewGRFScan : NewGRFScanCallback {
*/ */
AfterNewGRFScan() : AfterNewGRFScan() :
startyear(INVALID_YEAR), generation_seed(GENERATE_NEW_SEED), 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), join_server_password(nullptr), join_company_password(nullptr),
save_config(true) save_config(true)
{ {
@ -458,7 +458,7 @@ struct AfterNewGRFScan : NewGRFScanCallback {
if (startyear != INVALID_YEAR) IConsoleSetSetting("game_creation.starting_year", startyear); 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 (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.clear();
_network_bind_list.emplace_back(dedicated_host); _network_bind_list.emplace_back(dedicated_host);
} }
@ -565,10 +565,7 @@ int openttd_main(int argc, char *argv[])
dedicated = true; dedicated = true;
SetDebugString("net=6"); SetDebugString("net=6");
if (mgo.opt != nullptr) { if (mgo.opt != nullptr) {
const char *port = nullptr; scanner->dedicated_host = ParseFullConnectionString(mgo.opt, scanner->dedicated_port);
ParseFullConnectionString(nullptr, &port, mgo.opt);
if (!StrEmpty(mgo.opt)) scanner->dedicated_host = mgo.opt;
if (port != nullptr) scanner->dedicated_port = atoi(port);
} }
break; break;
case 'f': _dedicated_forks = true; break; case 'f': _dedicated_forks = true; break;