Various improvements to WAN and LAN connection

This commit is contained in:
Benau 2018-02-24 15:48:30 +08:00
parent d586ab9011
commit b26b784b6a
11 changed files with 286 additions and 242 deletions

View File

@ -280,6 +280,12 @@ void MainLoop::run()
if (STKHost::existHost() && if (STKHost::existHost() &&
STKHost::get()->requestedShutdown()) STKHost::get()->requestedShutdown())
{ {
SFXManager::get()->quickSound("anvil");
core::stringw msg = _("Connection to server is lost.");
if (!STKHost::get()->getErrorMessage().empty())
{
msg = STKHost::get()->getErrorMessage();
}
STKHost::get()->shutdown(); STKHost::get()->shutdown();
if (World::getWorld()) if (World::getWorld())
{ {
@ -293,8 +299,7 @@ void MainLoop::run()
OnlineScreen::getInstance(), NULL OnlineScreen::getInstance(), NULL
}; };
StateManager::get()->resetAndSetStack(new_stack); StateManager::get()->resetAndSetStack(new_stack);
MessageQueue::add(MessageQueue::MT_ERROR, MessageQueue::add(MessageQueue::MT_ERROR, msg);
_("Connection to server is lost."));
} }
NetworkConfig::get()->unsetNetworking(); NetworkConfig::get()->unsetNetworking();
} }

View File

@ -58,6 +58,8 @@ Network::Network(int peer_count, int channel_limit,
return; return;
if (change_port_if_bound) if (change_port_if_bound)
{ {
Log::warn("Network", "%d port is in used, use another port",
address->port);
ENetAddress new_addr; ENetAddress new_addr;
new_addr.host = address->host; new_addr.host = address->host;
// Any port // Any port

View File

@ -32,12 +32,6 @@
#include "utils/time.hpp" #include "utils/time.hpp"
#include "utils/log.hpp" #include "utils/log.hpp"
#ifdef WIN32
# include <iphlpapi.h>
#else
#include <ifaddrs.h>
#endif
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Connects to a server. This is the quick connect constructor, which /** Connects to a server. This is the quick connect constructor, which
* will pick a server randomly. * will pick a server randomly.
@ -84,8 +78,7 @@ void ConnectToServer::setup()
// In case of LAN we already have the server's and our ip address, // In case of LAN we already have the server's and our ip address,
// so we can immediately start requesting a connection. // so we can immediately start requesting a connection.
m_state = NetworkConfig::get()->isLAN() ? GOT_SERVER_ADDRESS : m_state = NetworkConfig::get()->isLAN() ? GOT_SERVER_ADDRESS :
REGISTER_SELF_ADDRESS; SET_PUBLIC_ADDRESS;
} // setup } // setup
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -93,10 +86,17 @@ void ConnectToServer::asynchronousUpdate()
{ {
switch(m_state) switch(m_state)
{ {
case SET_PUBLIC_ADDRESS:
{
STKHost::get()->setPublicAddress();
// Set to DONE will stop STKHost is not connected
m_state = STKHost::get()->getPublicAddress().isUnset() ?
DONE : REGISTER_SELF_ADDRESS;
}
break;
case REGISTER_SELF_ADDRESS: case REGISTER_SELF_ADDRESS:
{ {
registerWithSTKServer(); // Register us with STK server registerWithSTKServer(); // Register us with STK server
if (m_quick_join) if (m_quick_join)
{ {
handleQuickConnect(); handleQuickConnect();
@ -226,6 +226,9 @@ void ConnectToServer::asynchronousUpdate()
if (STKHost::get()->getPeerCount() == 0) if (STKHost::get()->getPeerCount() == 0)
{ {
// Shutdown STKHost (go back to online menu too) // Shutdown STKHost (go back to online menu too)
STKHost::get()->setErrorMessage(
_("Cannot connect to server with address: %s.",
m_server_address.toString().c_str()));
STKHost::get()->requestShutdown(); STKHost::get()->requestShutdown();
} }
break; break;
@ -266,7 +269,10 @@ void ConnectToServer::registerWithSTKServer()
} }
else else
{ {
Log::error("ConnectToServer", "Failed to register address."); irr::core::stringc error(request->getInfo().c_str());
Log::error("ConnectToServer", "Failed to register client address: %s",
error.c_str());
m_state = DONE;
} }
delete request; delete request;
@ -349,58 +355,8 @@ void ConnectToServer::waitingAloha(bool is_wan)
sender.toString().c_str()); sender.toString().c_str());
if (!is_wan) if (!is_wan)
{ {
#ifndef WIN32 if (sender.isPublicAddressLAN())
// just check if the ip is ours : if so, sender.setIP(0x7f000001); // 127.0.0.1
// then just use localhost (127.0.0.1)
struct ifaddrs *ifap, *ifa;
struct sockaddr_in *sa;
getifaddrs(&ifap); // get the info
for (ifa = ifap; ifa; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr->sa_family == AF_INET)
{
sa = (struct sockaddr_in *) ifa->ifa_addr;
// This interface is ours
if (ntohl(sa->sin_addr.s_addr) == sender.getIP())
sender.setIP(0x7f000001); // 127.0.0.1
}
}
freeifaddrs(ifap);
#else
// Query the list of all IP addresses on the local host
// First call to GetIpAddrTable with 0 bytes buffer
// will return insufficient buffer error, and size
// will contain the number of bytes needed for all
// data. Repeat the process of querying the size
// using GetIpAddrTable in a while loop since it
// can happen that an interface comes online between
// the previous call to GetIpAddrTable and the next
// call.
MIB_IPADDRTABLE *table = NULL;
unsigned long size = 0;
int error = GetIpAddrTable(table, &size, 0);
// Also add a count to limit the while loop - in
// case that something strange is going on.
int count = 0;
while (error == ERROR_INSUFFICIENT_BUFFER && count < 10)
{
delete[] table; // deleting NULL is legal
table = (MIB_IPADDRTABLE*)new char[size];
error = GetIpAddrTable(table, &size, 0);
count++;
} // while insufficient buffer
for (unsigned int i = 0; i < table->dwNumEntries; i++)
{
unsigned int ip = ntohl(table->table[i].dwAddr);
if (sender.getIP() == ip) // this interface is ours
{
sender.setIP(0x7f000001); // 127.0.0.1
break;
}
}
delete[] table;
#endif
} }
m_server_address.copy(sender); m_server_address.copy(sender);
m_state = CONNECTING; m_state = CONNECTING;

View File

@ -40,6 +40,7 @@ private:
/** State for finite state machine. */ /** State for finite state machine. */
enum enum
{ {
SET_PUBLIC_ADDRESS,
REGISTER_SELF_ADDRESS, REGISTER_SELF_ADDRESS,
GOT_SERVER_ADDRESS, GOT_SERVER_ADDRESS,
REQUESTING_CONNECTION, REQUESTING_CONNECTION,

View File

@ -83,6 +83,8 @@
ServerLobby::ServerLobby() : LobbyProtocol(NULL) ServerLobby::ServerLobby() : LobbyProtocol(NULL)
{ {
setHandleDisconnections(true); setHandleDisconnections(true);
m_state = SET_PUBLIC_ADDRESS;
// We use maximum 16bit unsigned limit // We use maximum 16bit unsigned limit
auto all_k = kart_properties_manager->getAllAvailableKarts(); auto all_k = kart_properties_manager->getAllAvailableKarts();
auto all_t = track_manager->getAllTrackIdentifiers(); auto all_t = track_manager->getAllTrackIdentifiers();
@ -113,11 +115,6 @@ void ServerLobby::setup()
m_game_setup = STKHost::get()->setupNewGame(); m_game_setup = STKHost::get()->setupNewGame();
m_game_setup->setNumLocalPlayers(0); // no local players on a server m_game_setup->setNumLocalPlayers(0); // no local players on a server
m_next_player_id.setAtomic(0); m_next_player_id.setAtomic(0);
// In case of LAN we don't need our public address or register with the
// STK server, so we can directly go to the accepting clients state.
m_state = NetworkConfig::get()->isLAN() ? ACCEPTING_CLIENTS
: INIT_WAN;
m_selection_enabled = false; m_selection_enabled = false;
Log::info("ServerLobby", "Starting the protocol."); Log::info("ServerLobby", "Starting the protocol.");
@ -185,7 +182,6 @@ bool ServerLobby::notifyEventAsynchronous(Event* event)
case LE_VOTE_LAPS: playerLapsVote(event); break; case LE_VOTE_LAPS: playerLapsVote(event); break;
case LE_RACE_FINISHED_ACK: playerFinishedResult(event); break; case LE_RACE_FINISHED_ACK: playerFinishedResult(event); break;
} // switch } // switch
} // if (event->getType() == EVENT_TYPE_MESSAGE) } // if (event->getType() == EVENT_TYPE_MESSAGE)
else if (event->getType() == EVENT_TYPE_DISCONNECTED) else if (event->getType() == EVENT_TYPE_DISCONNECTED)
{ {
@ -195,21 +191,34 @@ bool ServerLobby::notifyEventAsynchronous(Event* event)
} // notifyEventAsynchronous } // notifyEventAsynchronous
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Simple finite state machine. First get the public ip address. Once this /** Find out the public IP server or poll STK server asynchronously. */
* is known, register the server and its address with the stk server so that void ServerLobby::asynchronousUpdate()
* client can find it.
*/
void ServerLobby::update(float dt)
{ {
switch (m_state.load()) switch (m_state.load())
{ {
case INIT_WAN: case SET_PUBLIC_ADDRESS:
{ {
// Start the protocol to find the public ip address. // In case of LAN we don't need our public address or register with the
m_state = GETTING_PUBLIC_ADDRESS; // STK server, so we can directly go to the accepting clients state.
if (NetworkConfig::get()->isLAN())
{
m_state = ACCEPTING_CLIENTS;
STKHost::get()->startListening();
return;
}
STKHost::get()->setPublicAddress();
if (STKHost::get()->getPublicAddress().isUnset())
{
m_state = ERROR_LEAVE;
}
else
{
STKHost::get()->startListening();
m_state = REGISTER_SELF_ADDRESS;
}
break; break;
} }
case GETTING_PUBLIC_ADDRESS: case REGISTER_SELF_ADDRESS:
{ {
// Register this server with the STK server. This will block // Register this server with the STK server. This will block
// this thread, but there is no need for the protocol manager // this thread, but there is no need for the protocol manager
@ -221,10 +230,39 @@ void ServerLobby::update(float dt)
case ACCEPTING_CLIENTS: case ACCEPTING_CLIENTS:
{ {
// Only poll the STK server if this is a WAN server. // Only poll the STK server if this is a WAN server.
if(NetworkConfig::get()->isWAN()) if (NetworkConfig::get()->isWAN())
checkIncomingConnectionRequests(); checkIncomingConnectionRequests();
break; break;
} }
case ERROR_LEAVE:
{
requestTerminate();
m_state = EXITING;
STKHost::get()->setErrorMessage(_("Failed to setup server."));
STKHost::get()->requestShutdown();
break;
}
default:
break;
}
} // asynchronousUpdate
//-----------------------------------------------------------------------------
/** Simple finite state machine. Once this
* is known, register the server and its address with the stk server so that
* client can find it.
*/
void ServerLobby::update(float dt)
{
switch (m_state.load())
{
case SET_PUBLIC_ADDRESS:
case REGISTER_SELF_ADDRESS:
case ACCEPTING_CLIENTS:
{
// Waiting for asynchronousUpdate
break;
}
case SELECTING: case SELECTING:
// The function playerTrackVote will trigger the next state // The function playerTrackVote will trigger the next state
// once all track votes have been received. // once all track votes have been received.
@ -280,7 +318,8 @@ void ServerLobby::update(float dt)
sendMessageToPeersChangingToken(exit_result_screen, sendMessageToPeersChangingToken(exit_result_screen,
/*reliable*/true); /*reliable*/true);
delete exit_result_screen; delete exit_result_screen;
m_state = ACCEPTING_CLIENTS; m_state = NetworkConfig::get()->isLAN() ?
ACCEPTING_CLIENTS : REGISTER_SELF_ADDRESS;
RaceResultGUI::getInstance()->backToLobby(); RaceResultGUI::getInstance()->backToLobby();
// notify the network world that it is stopped // notify the network world that it is stopped
RaceEventManager::getInstance()->stop(); RaceEventManager::getInstance()->stop();
@ -293,10 +332,7 @@ void ServerLobby::update(float dt)
setup(); setup();
} }
break; break;
case DONE: case ERROR_LEAVE:
m_state = EXITING;
requestTerminate();
break;
case EXITING: case EXITING:
break; break;
} }
@ -337,14 +373,11 @@ void ServerLobby::registerServer()
{ {
irr::core::stringc error(request->getInfo().c_str()); irr::core::stringc error(request->getInfo().c_str());
Log::error("ServerLobby", "%s", error.c_str()); Log::error("ServerLobby", "%s", error.c_str());
STKHost::get()->setErrorMessage(_("Failed to register server: %s", m_state = ERROR_LEAVE;
error.c_str()));
STKHost::get()->requestShutdown();
} }
delete request; delete request;
} // registerServer } // registerServer
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Unregister this server (i.e. its public address) with the STK server, /** Unregister this server (i.e. its public address) with the STK server,
* currently when karts enter kart selection screen it will be done. * currently when karts enter kart selection screen it will be done.
@ -472,6 +505,12 @@ void ServerLobby::checkIncomingConnectionRequests()
if (StkTime::getRealTime() < last_poll_time + POLL_INTERVAL) if (StkTime::getRealTime() < last_poll_time + POLL_INTERVAL)
return; return;
// Keep the port open, it can be sent to anywhere as we will send to the
// correct peer later in ConnectToPeer.
BareNetworkString data;
data.addUInt8(0);
STKHost::get()->sendRawPacket(data, STKHost::get()->getStunAddress());
// Now poll the stk server // Now poll the stk server
last_poll_time = StkTime::getRealTime(); last_poll_time = StkTime::getRealTime();
Online::XMLRequest* request = new Online::XMLRequest(); Online::XMLRequest* request = new Online::XMLRequest();

View File

@ -14,18 +14,17 @@ public:
/* The state for a small finite state machine. */ /* The state for a small finite state machine. */
enum ServerState : unsigned int enum ServerState : unsigned int
{ {
INIT_WAN, // Start state for WAN game SET_PUBLIC_ADDRESS, // Waiting to receive its public ip address
GETTING_PUBLIC_ADDRESS, // Waiting to receive its public ip address REGISTER_SELF_ADDRESS, // Register with STK online server
ACCEPTING_CLIENTS, // In lobby, accepting clients ACCEPTING_CLIENTS, // In lobby, accepting clients
SELECTING, // kart, track, ... selection started SELECTING, // kart, track, ... selection started
LOAD_WORLD, // Server starts loading world LOAD_WORLD, // Server starts loading world
WAIT_FOR_WORLD_LOADED, // Wait for clients and server to load world WAIT_FOR_WORLD_LOADED, // Wait for clients and server to load world
WAIT_FOR_RACE_STARTED, // Wait for all clients to have started the race WAIT_FOR_RACE_STARTED, // Wait for all clients to have started the race
START_RACE, // Inform clients to start race
DELAY_SERVER, // Additional server delay DELAY_SERVER, // Additional server delay
RACING, // racing RACING, // racing
RESULT_DISPLAY, // Show result screen RESULT_DISPLAY, // Show result screen
DONE, // shutting down server ERROR_LEAVE, // shutting down server
EXITING EXITING
}; };
private: private:
@ -92,7 +91,7 @@ public:
virtual bool notifyEvent(Event* event) OVERRIDE; virtual bool notifyEvent(Event* event) OVERRIDE;
virtual void setup() OVERRIDE; virtual void setup() OVERRIDE;
virtual void update(float dt) OVERRIDE; virtual void update(float dt) OVERRIDE;
virtual void asynchronousUpdate() OVERRIDE {}; virtual void asynchronousUpdate() OVERRIDE;
void signalRaceStartToClients(); void signalRaceStartToClients();
void startSelection(const Event *event=NULL); void startSelection(const Event *event=NULL);

View File

@ -261,13 +261,14 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id)
// server is made. // server is made.
m_host_id = 0; m_host_id = 0;
init(); init();
ENetAddress ea; ENetAddress ea;
ea.host = STKHost::HOST_ANY; ea.host = STKHost::HOST_ANY;
ea.port = STKHost::PORT_ANY; ea.port = NetworkConfig::get()->getClientPort();
m_network = new Network(/*peer_count*/1, /*channel_limit*/2, m_network = new Network(/*peer_count*/1, /*channel_limit*/2,
/*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea); /*max_in_bandwidth*/0, /*max_out_bandwidth*/0,
&ea, true/*change_port_if_bound*/);
if (!m_network) if (!m_network)
{ {
Log::fatal ("STKHost", "An error occurred while trying to create " Log::fatal ("STKHost", "An error occurred while trying to create "
@ -275,15 +276,7 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id)
} }
setPrivatePort(); setPrivatePort();
if (NetworkConfig::get()->isWAN()) std::make_shared<ConnectToServer>(server_id, host_id)->requestStart();
{
setPublicAddress();
}
// Don't connect to server if no public address in WAN game
if (!m_public_address.isUnset() || NetworkConfig::get()->isLAN())
{
std::make_shared<ConnectToServer>(server_id, host_id)->requestStart();
}
} // STKHost } // STKHost
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -300,12 +293,13 @@ STKHost::STKHost(const irr::core::stringw &server_name)
ENetAddress addr; ENetAddress addr;
addr.host = STKHost::HOST_ANY; addr.host = STKHost::HOST_ANY;
addr.port = STKHost::PORT_ANY; addr.port = NetworkConfig::get()->getServerPort();
m_network= new Network(NetworkConfig::get()->getMaxPlayers(), m_network= new Network(NetworkConfig::get()->getMaxPlayers(),
/*channel_limit*/2, /*channel_limit*/2,
/*max_in_bandwidth*/0, /*max_in_bandwidth*/0,
/*max_out_bandwidth*/ 0, &addr); /*max_out_bandwidth*/ 0, &addr,
true/*change_port_if_bound*/);
if (!m_network) if (!m_network)
{ {
Log::fatal("STKHost", "An error occurred while trying to create an " Log::fatal("STKHost", "An error occurred while trying to create an "
@ -313,16 +307,8 @@ STKHost::STKHost(const irr::core::stringw &server_name)
} }
setPrivatePort(); setPrivatePort();
// We need the public address for server no matter what to determine ProtocolManager::lock()
// local lan connection ->requestStart(LobbyProtocol::create<ServerLobby>());
setPublicAddress();
// Don't construct server if no public address in WAN game
if (!m_public_address.isUnset() || NetworkConfig::get()->isLAN())
{
startListening();
ProtocolManager::lock()
->requestStart(LobbyProtocol::create<ServerLobby>());
}
} // STKHost(server_name) } // STKHost(server_name)
@ -335,7 +321,6 @@ void STKHost::init()
m_shutdown = false; m_shutdown = false;
m_network = NULL; m_network = NULL;
m_game_setup = NULL; m_game_setup = NULL;
m_error_message = "";
m_exit_flag.clear(); m_exit_flag.clear();
m_exit_flag.test_and_set(); m_exit_flag.test_and_set();
@ -435,17 +420,19 @@ void STKHost::setPublicAddress()
// documentation says it points to "one or more addrinfo structures" // documentation says it points to "one or more addrinfo structures"
assert(res != NULL); assert(res != NULL);
struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr); struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr);
uint32_t stun_server_ip = ntohl(current_interface->sin_addr.s_addr); m_stun_address.setIP(ntohl(current_interface->sin_addr.s_addr));
m_stun_address.setPort(3478);
// Assemble the message for the stun server // Assemble the message for the stun server
BareNetworkString s(20); BareNetworkString s(20);
constexpr uint32_t magic_cookie = 0x2112A442;
// bytes 0-1: the type of the message // bytes 0-1: the type of the message
// bytes 2-3: message length added to header (attributes) // bytes 2-3: message length added to header (attributes)
uint16_t message_type = 0x0001; // binding request uint16_t message_type = 0x0001; // binding request
uint16_t message_length = 0x0000; uint16_t message_length = 0x0000;
s.addUInt16(message_type).addUInt16(message_length) s.addUInt16(message_type).addUInt16(message_length)
.addUInt32(0x2112A442); .addUInt32(magic_cookie);
uint8_t stun_tansaction_id[12]; uint8_t stun_tansaction_id[12];
// bytes 8-19: the transaction id // bytes 8-19: the transaction id
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++)
@ -455,7 +442,7 @@ void STKHost::setPublicAddress()
stun_tansaction_id[i] = random_byte; stun_tansaction_id[i] = random_byte;
} }
m_network->sendRawPacket(s, TransportAddress(stun_server_ip, 3478)); m_network->sendRawPacket(s, m_stun_address);
freeaddrinfo(res); freeaddrinfo(res);
// Recieve now // Recieve now
@ -464,33 +451,37 @@ void STKHost::setPublicAddress()
char buffer[LEN]; char buffer[LEN];
int len = m_network->receiveRawPacket(buffer, LEN, &sender, 2000); int len = m_network->receiveRawPacket(buffer, LEN, &sender, 2000);
if (sender.getIP() != stun_server_ip) if (sender.getIP() != m_stun_address.getIP())
{ {
TransportAddress stun(stun_server_ip, 3478);
Log::warn("STKHost", Log::warn("STKHost",
"Received stun response from %s instead of %s.", "Received stun response from %s instead of %s.",
sender.toString().c_str(), stun.toString().c_str()); sender.toString().c_str(), m_stun_address.toString().c_str());
} }
if (len < 0) if (len <= 0)
{ {
Log::error("STKHost", "STUN response contains no data at all"); Log::error("STKHost", "STUN response contains no data at all");
continue; continue;
} }
// Convert to network string. // Convert to network string.
BareNetworkString datas(buffer, len); BareNetworkString response(buffer, len);
if (response.size() < 20)
// check that the stun response is a response, contains the magic cookie
// and the transaction ID
if (datas.getUInt16() != 0x0101)
{ {
Log::error("STKHost", "STUN response doesn't contain the magic " Log::error("STKHost", "STUN response should be at least 20 bytes.");
"cookie");
continue; continue;
} }
int message_size = datas.getUInt16();
if (datas.getUInt32() != 0x2112A442) if (response.getUInt16() != 0x0101)
{
Log::error("STKHost", "STUN has no binding success response.");
continue;
}
// Skip message size
response.getUInt16();
if (response.getUInt32() != magic_cookie)
{ {
Log::error("STKHost", "STUN response doesn't contain the magic " Log::error("STKHost", "STUN response doesn't contain the magic "
"cookie"); "cookie");
@ -499,7 +490,7 @@ void STKHost::setPublicAddress()
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++)
{ {
if (datas.getUInt8() != stun_tansaction_id[i]) if (response.getUInt8() != stun_tansaction_id[i])
{ {
Log::error("STKHost", "STUN response doesn't contain the " Log::error("STKHost", "STUN response doesn't contain the "
"transaction ID"); "transaction ID");
@ -511,64 +502,77 @@ void STKHost::setPublicAddress()
"The STUN server responded with a valid answer"); "The STUN server responded with a valid answer");
// The stun message is valid, so we parse it now: // The stun message is valid, so we parse it now:
if (message_size == 0)
{
Log::error("STKHost", "STUN response does not contain any "
"information.");
continue;
}
// Cannot even read the size
if (message_size < 4)
{
Log::error("STKHost", "STUN response is too short.");
continue;
}
// Those are the port and the address to be detected // Those are the port and the address to be detected
bool found = false; bool found = false;
while (true) while (true)
{ {
int type = datas.getUInt16(); if (response.size() < 4)
int size = datas.getUInt16();
if (type == 0 || type == 1)
{
assert(size == 8);
datas.getUInt8(); // skip 1 byte
#ifdef DEBUG
uint8_t skip = datas.getUInt8();
// Family IPv4 only
assert(skip == 0x01);
#else
datas.getUInt8();
#endif
m_public_address.setPort(datas.getUInt16());
m_public_address.setIP(datas.getUInt32());
// finished parsing, we know our public transport address
Log::debug("STKHost", "The public address has been found: %s",
m_public_address.toString().c_str());
found = true;
break;
} // type = 0 or 1
datas.skip(4 + size);
message_size -= 4 + size;
if (message_size == 0)
{ {
Log::error("STKHost", "STUN response is invalid."); Log::error("STKHost", "STUN response is invalid.");
break; break;
} }
// Cannot even read the size unsigned type = response.getUInt16();
if (message_size < 4) unsigned size = response.getUInt16();
// Bit determining whether comprehension of an attribute is optional.
// Described in section 15 of RFC 5389.
constexpr uint16_t comprehension_optional = 0x1 << 15;
// Bit determining whether the bit was assigned by IETF Review.
// Described in section 18.1. of RFC 5389.
constexpr uint16_t IETF_review = 0x1 << 14;
// Defined in section 15.1 of RFC 5389
constexpr uint8_t ipv4 = 0x01;
// Defined in section 18.2 of RFC 5389
constexpr uint16_t mapped_address = 0x001;
constexpr uint16_t xor_mapped_address = 0x0020;
// The first two bits are irrelevant to the type
type &= ~(comprehension_optional | IETF_review);
if (type == mapped_address || type == xor_mapped_address)
{ {
Log::error("STKHost", "STUN response is invalid."); if (size != 8 || response.size() < 8)
{
Log::error("STKHost", "Invalid STUN mapped address "
"length");
break;
}
// Ignore the first byte as mentioned in Section 15.1 of RFC
// 5389.
uint8_t ip_type = response.getUInt8();
ip_type = response.getUInt8();
if (ip_type != ipv4)
{
Log::error("STKHost", "Only IPv4 is supported");
break;
}
uint16_t port = response.getUInt16();
uint32_t ip = response.getUInt32();
if (type == xor_mapped_address)
{
// Obfuscation is described in Section 15.2 of RFC 5389.
port ^= magic_cookie >> 16;
ip ^= magic_cookie;
}
m_public_address.setPort(port);
m_public_address.setIP(ip);
found = true;
break; break;
} // type == mapped_address || type == xor_mapped_address
else
{
response.skip(size);
int padding = size % 4;
if (padding != 0)
response.skip(4 - padding);
} }
} // while true } // while true
// Found public address and port // Found public address and port
if (found) if (found)
untried_server.clear(); untried_server.clear();
} }
// We shutdown next frame if no public address
if (m_public_address.isUnset())
requestShutdown();
} // setPublicAddress } // setPublicAddress
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -629,18 +633,14 @@ void STKHost::abort()
*/ */
void STKHost::setErrorMessage(const irr::core::stringw &message) void STKHost::setErrorMessage(const irr::core::stringw &message)
{ {
irr::core::stringc s(message.c_str()); if (!message.empty())
Log::error("STKHost", "%s", s.c_str()); {
irr::core::stringc s(message.c_str());
Log::error("STKHost", "%s", s.c_str());
}
m_error_message = message; m_error_message = message;
} // setErrorMessage } // setErrorMessage
// --------------------------------------------------------------------
/** Returns the last error (or "" if no error has happened). */
const irr::core::stringw& STKHost::getErrorMessage() const
{
return m_error_message;
} // getErrorMessage
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/** \brief Try to establish a connection to a given transport address. /** \brief Try to establish a connection to a given transport address.
* \param peer : The transport address which you want to connect to. * \param peer : The transport address which you want to connect to.
@ -831,7 +831,7 @@ void STKHost::handleDirectSocketRequest(Network* lan_network)
{ {
// In case of a LAN connection, we only allow connections from // In case of a LAN connection, we only allow connections from
// a LAN address (192.168*, ..., and 127.*). // a LAN address (192.168*, ..., and 127.*).
if (!sender.isLAN() && sender.getIP() != m_public_address.getIP()) if (!sender.isLAN() && !sender.isPublicAddressLAN())
{ {
Log::error("STKHost", "Client trying to connect from '%s'", Log::error("STKHost", "Client trying to connect from '%s'",
sender.toString().c_str()); sender.toString().c_str());

View File

@ -101,6 +101,9 @@ private:
/** The public address found by stun (if WAN is used). */ /** The public address found by stun (if WAN is used). */
TransportAddress m_public_address; TransportAddress m_public_address;
/** The public address stun server used. */
TransportAddress m_stun_address;
/** The private port enet socket is bound. */ /** The private port enet socket is bound. */
uint16_t m_private_port; uint16_t m_private_port;
@ -113,8 +116,6 @@ private:
void init(); void init();
void handleDirectSocketRequest(Network* lan_network); void handleDirectSocketRequest(Network* lan_network);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void setPublicAddress();
// ------------------------------------------------------------------------
void mainLoop(); void mainLoop();
public: public:
@ -149,11 +150,16 @@ public:
const TransportAddress& getPublicAddress() const const TransportAddress& getPublicAddress() const
{ return m_public_address; } { return m_public_address; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
const TransportAddress& getStunAddress() const
{ return m_stun_address; }
// ------------------------------------------------------------------------
uint16_t getPrivatePort() const uint16_t getPrivatePort() const
{ return m_private_port; } { return m_private_port; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void setPrivatePort(); void setPrivatePort();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void setPublicAddress();
// ------------------------------------------------------------------------
virtual GameSetup* setupNewGame(); virtual GameSetup* setupNewGame();
void abort(); void abort();
void deleteAllPeers(); void deleteAllPeers();
@ -190,9 +196,11 @@ public:
std::vector<NetworkPlayerProfile*> getMyPlayerProfiles(); std::vector<NetworkPlayerProfile*> getMyPlayerProfiles();
void setErrorMessage(const irr::core::stringw &message); void setErrorMessage(const irr::core::stringw &message);
bool isAuthorisedToControl() const; bool isAuthorisedToControl() const;
const irr::core::stringw&
getErrorMessage() const;
// --------------------------------------------------------------------
/** Returns the last error (or "" if no error has happened). */
const irr::core::stringw& getErrorMessage() const
{ return m_error_message; }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/** Returns true if a shutdown of the network infrastructure was /** Returns true if a shutdown of the network infrastructure was
* requested. */ * requested. */
@ -214,15 +222,6 @@ public:
{ {
m_network->sendRawPacket(buffer, dst); m_network->sendRawPacket(buffer, dst);
} // sendRawPacket } // sendRawPacket
// --------------------------------------------------------------------
/** Returns the IP address of this host. */
uint32_t getAddress() const
{
return m_network->getENetHost()->address.host;
} // getAddress
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/** Returns a const reference to the list of peers. */ /** Returns a const reference to the list of peers. */
const std::vector<STKPeer*> &getPeers() { return m_peers; } const std::vector<STKPeer*> &getPeers() { return m_peers; }

View File

@ -18,8 +18,15 @@
#include "network/transport_address.hpp" #include "network/transport_address.hpp"
#ifdef WIN32
# include <iphlpapi.h>
#else
#include <ifaddrs.h>
#endif
// ----------------------------------------------------------------------------
/** Returns if this IP address belongs to a LAN, i.e. is in 192.168* or /** Returns if this IP address belongs to a LAN, i.e. is in 192.168* or
* 10*, 172.16-31.*, or is on the same host, i.e. 127*. * 10*, 172.16-31.*, or is on the same host, i.e. 127* or same public address.
*/ */
bool TransportAddress::isLAN() const bool TransportAddress::isLAN() const
{ {
@ -33,6 +40,60 @@ bool TransportAddress::isLAN() const
else if (ip >> 24 == 0x7f ) // 127.* localhost else if (ip >> 24 == 0x7f ) // 127.* localhost
return true; return true;
return false; return false;
}
// ----------------------------------------------------------------------------
/** Returns this IP address is localhost (127.0.0.1).
*/
bool TransportAddress::isPublicAddressLAN() const
{
#ifndef WIN32
struct ifaddrs *ifap, *ifa;
struct sockaddr_in *sa;
getifaddrs(&ifap); // get the info
for (ifa = ifap; ifa; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr->sa_family == AF_INET)
{
sa = (struct sockaddr_in *) ifa->ifa_addr;
// This interface is ours
if (ntohl(sa->sin_addr.s_addr) == getIP())
return true;
}
}
freeifaddrs(ifap);
#else
// Query the list of all IP addresses on the local host. First call to
// GetIpAddrTable with 0 bytes buffer will return insufficient buffer
// error, and size will contain the number of bytes needed for all data.
// Repeat the process of querying the size using GetIpAddrTable in a while
// loop since it can happen that an interface comes online between the
// previous call to GetIpAddrTable and the next call.
MIB_IPADDRTABLE *table = NULL;
unsigned long size = 0;
int error = GetIpAddrTable(table, &size, 0);
// Also add a count to limit the while loop - in case that something
// strange is going on.
int count = 0;
while (error == ERROR_INSUFFICIENT_BUFFER && count < 10)
{
delete[] table; // deleting NULL is legal
table = (MIB_IPADDRTABLE*)new char[size];
error = GetIpAddrTable(table, &size, 0);
count++;
} // while insufficient buffer
for (unsigned int i = 0; i < table->dwNumEntries; i++)
{
unsigned int ip = ntohl(table->table[i].dwAddr);
if (getIP() == ip) // this interface is ours
{
delete[] table;
return true;
}
}
delete[] table;
#endif
return false;
} // isLAN } // isLAN
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -42,71 +103,71 @@ bool TransportAddress::isLAN() const
void TransportAddress::unitTesting() void TransportAddress::unitTesting()
{ {
TransportAddress t1("192.168.0.0"); TransportAddress t1("192.168.0.0");
assert(t1.getIP() == (192 << 24) + (168 << 16)); assert(t1.getIP() == (192u << 24) + (168u << 16));
assert(t1.isLAN()); assert(t1.isLAN());
TransportAddress t2("192.168.255.255"); TransportAddress t2("192.168.255.255");
assert(t2.getIP() == (192 << 24) + (168 << 16) + (255 << 8) + 255); assert(t2.getIP() == (192u << 24) + (168u << 16) + (255u << 8) + 255u);
assert(t2.isLAN()); assert(t2.isLAN());
TransportAddress t3("193.168.0.1"); TransportAddress t3("193.168.0.1");
assert(t3.getIP() == (193 << 24) + (168 << 16) + 1); assert(t3.getIP() == (193u << 24) + (168u << 16) + 1);
assert(!t3.isLAN()); assert(!t3.isLAN());
TransportAddress t4("192.167.255.255"); TransportAddress t4("192.167.255.255");
assert(t4.getIP() == (192 << 24) + (167 << 16) + (255 << 8) + 255); assert(t4.getIP() == (192u << 24) + (167u << 16) + (255u << 8) + 255u);
assert(!t4.isLAN()); assert(!t4.isLAN());
TransportAddress t5("192.169.0.0"); TransportAddress t5("192.169.0.0");
assert(t5.getIP() == (192 << 24) + (169 << 16)); assert(t5.getIP() == (192u << 24) + (169u << 16));
assert(!t5.isLAN()); assert(!t5.isLAN());
TransportAddress t6("172.16.0.0"); TransportAddress t6("172.16.0.0");
assert(t6.getIP() == (172 << 24) + (16 << 16)); assert(t6.getIP() == (172u << 24) + (16u << 16));
assert(t6.isLAN()); assert(t6.isLAN());
TransportAddress t7("172.31.255.255"); TransportAddress t7("172.31.255.255");
assert(t7.getIP() == (172 << 24) + (31 << 16) + (255 << 8) + 255); assert(t7.getIP() == (172u << 24) + (31u << 16) + (255u << 8) + 255u);
assert(t7.isLAN()); assert(t7.isLAN());
TransportAddress t8("172.15.255.255"); TransportAddress t8("172.15.255.255");
assert(t8.getIP() == (172 << 24) + (15 << 16) + (255 << 8) + 255); assert(t8.getIP() == (172u << 24) + (15u << 16) + (255u << 8) + 255u);
assert(!t8.isLAN()); assert(!t8.isLAN());
TransportAddress t9("172.32.0.0"); TransportAddress t9("172.32.0.0");
assert(t9.getIP() == (172 << 24) + (32 << 16)); assert(t9.getIP() == (172u << 24) + (32u << 16));
assert(!t9.isLAN()); assert(!t9.isLAN());
TransportAddress t10("10.0.0.0"); TransportAddress t10("10.0.0.0");
assert(t10.getIP() == (10 << 24)); assert(t10.getIP() == (10u << 24));
assert(t10.isLAN()); assert(t10.isLAN());
TransportAddress t11("10.255.255.255"); TransportAddress t11("10.255.255.255");
assert(t11.getIP() == (10 << 24) + (255 << 16) + (255 << 8) + 255); assert(t11.getIP() == (10u << 24) + (255u << 16) + (255u << 8) + 255u);
assert(t11.isLAN()); assert(t11.isLAN());
TransportAddress t12("9.255.255.255"); TransportAddress t12("9.255.255.255");
assert(t12.getIP() == (9 << 24) + (255 << 16) + (255 << 8) + 255); assert(t12.getIP() == (9u << 24) + (255u << 16) + (255u << 8) + 255u);
assert(!t12.isLAN()); assert(!t12.isLAN());
TransportAddress t13("11.0.0.0"); TransportAddress t13("11.0.0.0");
assert(t13.getIP() == (11 << 24) ); assert(t13.getIP() == (11u << 24));
assert(!t13.isLAN()); assert(!t13.isLAN());
TransportAddress t14("127.0.0.0"); TransportAddress t14("127.0.0.0");
assert(t14.getIP() == (127 << 24)); assert(t14.getIP() == (127u << 24));
assert(t14.isLAN()); assert(t14.isLAN());
TransportAddress t15("127.255.255.255"); TransportAddress t15("127.255.255.255");
assert(t15.getIP() == (127 << 24) + (255 << 16) + (255 << 8) + 255); assert(t15.getIP() == (127u << 24) + (255u << 16) + (255u << 8) + 255u);
assert(t15.isLAN()); assert(t15.isLAN());
TransportAddress t16("126.255.255.255"); TransportAddress t16("126.255.255.255");
assert(t16.getIP() == (126 << 24) + (255 << 16) + (255 << 8) + 255); assert(t16.getIP() == (126u << 24) + (255u << 16) + (255u << 8) + 255u);
assert(!t16.isLAN()); assert(!t16.isLAN());
TransportAddress t17("128.0.0.0"); TransportAddress t17("128.0.0.0");
assert(t17.getIP() == (128 << 24)); assert(t17.getIP() == (128u << 24));
assert(!t17.isLAN()); assert(!t17.isLAN());
} // unitTesting } // unitTesting

View File

@ -88,6 +88,9 @@ private:
copy(other); copy(other);
} // TransportAddress(const TransportAddress&) } // TransportAddress(const TransportAddress&)
public: public:
// ------------------------------------------------------------------------
bool isPublicAddressLAN() const;
// ------------------------------------------------------------------------
bool isLAN() const; bool isLAN() const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool isUnset() const { return m_ip == 0 || m_port == 0; } bool isUnset() const { return m_ip == 0 || m_port == 0; }

View File

@ -136,27 +136,6 @@ void CreateServerScreen::onUpdate(float delta)
if(!STKHost::existHost()) if(!STKHost::existHost())
return; return;
// First check if an error happened while registering the server:
// --------------------------------------------------------------
const irr::core::stringw &error = STKHost::get()->getErrorMessage();
if(error!="")
{
SFXManager::get()->quickSound("anvil");
m_info_widget->setErrorColor();
m_info_widget->setText(error, false);
return;
}
// Otherwise wait till we get an answer from the server:
// -----------------------------------------------------
if (!LobbyProtocol::get<LobbyProtocol>())
{
m_info_widget->setDefaultColor();
m_info_widget->setText(StringUtils::loadingDots(_("Creating server")),
false);
return;
}
//FIXME If we really want a gui, we need to decide what else to do here //FIXME If we really want a gui, we need to decide what else to do here
// For now start the (wrong i.e. client) lobby, to prevent to create // For now start the (wrong i.e. client) lobby, to prevent to create
// a server more than once. // a server more than once.