Add support for ipv6 addresses to addon client and multiplayer client

ipv6 address must be specifid surrounded by [], like [ipv6_address]:port,
since otherwise impossible to disambiguate colons separating ipv6 quads
from the colon delimiting port
This commit is contained in:
loonycyborg 2019-01-04 17:37:45 +03:00
parent 7114174789
commit 34e2ef12c7
6 changed files with 77 additions and 27 deletions

View file

@ -98,6 +98,7 @@
as well.
* Re-added the Font Scaling preference.
* Enabled wesnothd and campaignd to accept IPv6 connections too
* Added support for directly supplying IPv6 address of the server to multiplayer client and addon client. It must be done like this: ```[ipv6_address]``` or ```[ipv6_address]:port```
## Version 1.14.5+dev
### AI

View file

@ -367,6 +367,7 @@ utils/context_free_grammar_generator.cpp
utils/irdya_datetime.cpp
utils/markov_generator.cpp
utils/name_generator_factory.cpp
utils/parse_network_address.cpp
variable.cpp
variable_info.cpp
wesnothd_connection.cpp

View file

@ -29,6 +29,7 @@
#include "serialization/parser.hpp"
#include "serialization/string_utils.hpp"
#include "serialization/utf8_exception.hpp"
#include "utils/parse_network_address.hpp"
#include <stdexcept>
@ -50,17 +51,11 @@ addons_client::addons_client(const std::string& address)
, last_error_()
, last_error_data_()
{
const std::vector<std::string>& address_components =
utils::split(addr_, ':');
if(address_components.empty()) {
try {
std::tie(host_, port_) = parse_network_address(addr_, std::to_string(default_campaignd_port));
} catch(const std::runtime_error&) {
throw invalid_server_address();
}
// FIXME: this parsing will break IPv6 numeric addresses! */
host_ = address_components[0];
port_ = address_components.size() == 2 ?
address_components[1] : std::to_string(default_campaignd_port);
}
void addons_client::connect()

View file

@ -37,6 +37,7 @@
#include "map_settings.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "utils/parse_network_address.hpp"
#include "wesnothd_connection.hpp"
#include "resources.hpp"
#include "replay.hpp"
@ -59,24 +60,20 @@ std::pair<wesnothd_connection_ptr, config> open_connection(std::string host)
return std::make_pair(std::move(sock), config());
}
const int colon_index = host.find_first_of(":");
unsigned int port;
if(colon_index == -1) {
port = 15000;
} else {
port = lexical_cast_default<unsigned int>(host.substr(colon_index + 1), 15000);
host = host.substr(0, colon_index);
}
// shown_hosts is used to prevent the client being locked in a redirect loop.
using hostpair = std::pair<std::string, int>;
using hostpair = std::pair<std::string, std::string>;
std::set<hostpair> shown_hosts;
shown_hosts.emplace(host, port);
hostpair addr;
try {
addr = parse_network_address(host, "15000");
} catch(const std::runtime_error&) {
throw wesnothd_error(_("Invalid address specified for multiplayer server"));
}
shown_hosts.insert(addr);
// Initializes the connection to the server.
sock = std::make_unique<wesnothd_connection>(host, std::to_string(port));
sock = std::make_unique<wesnothd_connection>(addr.first, addr.second);
if(!sock) {
return std::make_pair(std::move(sock), config());
}
@ -127,20 +124,20 @@ std::pair<wesnothd_connection_ptr, config> open_connection(std::string host)
// Check for "redirect" messages
if(const config& redirect = data.child("redirect")) {
host = redirect["host"].str();
port = redirect["port"].to_int(15000);
auto redirect_host = redirect["host"].str();
auto redirect_port = redirect["port"].str("15000");
if(shown_hosts.find(hostpair(host, port)) != shown_hosts.end()) {
if(shown_hosts.find(hostpair(redirect_host, redirect_port)) != shown_hosts.end()) {
throw wesnothd_error(_("Server-side redirect loop"));
}
shown_hosts.emplace(host, port);
shown_hosts.emplace(redirect_host, redirect_port);
gui2::dialogs::loading_screen::progress(loading_stage::redirect);
// Open a new connection with the new host and port.
sock.reset();
sock = std::make_unique<wesnothd_connection>(host, std::to_string(port));
sock = std::make_unique<wesnothd_connection>(redirect_host, redirect_port);
// Wait for new handshake.
while(!sock->handshake_finished()) {

View file

@ -0,0 +1,32 @@
/*
Copyright (C) 2019 by Sergey Popov <loonycyborg@gmail.com>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include <regex>
#include <string>
std::pair<std::string, std::string> parse_network_address(const std::string& address, const std::string& default_port)
{
const char* address_re = "\\[([[:xdigit:]:]*)\\](:(.*))?|([^:]*)(:([[:alnum:]]*))?";
std::smatch m;
std::regex_match(address, m, std::regex(address_re));
if(!m[1].str().empty()) {
return { m[1], m[3].str().empty() ? default_port : m[3] };
}
if(!m[4].str().empty()) {
return { m[4], m[6].str().empty() ? default_port : m[6] };
}
throw std::runtime_error("invalid address");
}

View file

@ -0,0 +1,24 @@
/*
Copyright (C) 2019 by Sergey Popov <loonycyborg@gmail.com>
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#pragma once
#include <string>
/**
* Parse a host:port style network address, supporting [] notation for ipv6 addresses
* @param address
* @param default_port the port to return if address doesn't have it specified
*/
std::pair<std::string, std::string> parse_network_address(const std::string& address, const std::string& default_port);