Fix wan connection, move get public address from stun to stk host

This commit is contained in:
Benau 2018-02-22 15:10:30 +08:00
parent 8daebe06e1
commit 1458f3ef8e
18 changed files with 574 additions and 449 deletions

View File

@ -1027,6 +1027,7 @@ int handleCmdLine()
while (true)
{
Online::RequestManager::get()->update(0.0f);
StkTime::sleep(1);
if (PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_IN)
{
break;
@ -1074,9 +1075,6 @@ int handleCmdLine()
if(CommandLine::has("--password", &s))
password = s.c_str();
if (CommandLine::has("--my-address", &s))
GetPublicAddress::setMyIPAddress(s);
/** Disable detection of LAN connection when connecting via WAN. This is
* mostly a debugging feature to force using WAN connection. */
if (CommandLine::has("--disable-lan"))

View File

@ -302,31 +302,32 @@ void MainLoop::run()
->addNextTimeStep(World::getWorld()->getTime(), dt);
}
if (!m_abort && !ProfileWorld::isNoGraphics())
if (!m_abort)
{
float frame_duration = num_steps * dt;
if (!ProfileWorld::isNoGraphics())
{
// Render the previous frame, and also handle all user input.
PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F);
irr_driver->update(frame_duration);
PROFILER_POP_CPU_MARKER();
// Render the previous frame, and also handle all user input.
PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F);
irr_driver->update(frame_duration);
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("Input/GUI", 0x7F, 0x00, 0x00);
#ifdef ENABLE_WIIUSE
wiimote_manager->update();
#endif
input_manager->update(frame_duration);
GUIEngine::update(frame_duration);
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("Music", 0x7F, 0x00, 0x00);
SFXManager::get()->update();
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("Input/GUI", 0x7F, 0x00, 0x00);
#ifdef ENABLE_WIIUSE
wiimote_manager->update();
#endif
input_manager->update(frame_duration);
GUIEngine::update(frame_duration);
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("Music", 0x7F, 0x00, 0x00);
SFXManager::get()->update();
PROFILER_POP_CPU_MARKER();
}
// Some protocols in network will use RequestManager
PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F);
Online::RequestManager::get()->update(frame_duration);
PROFILER_POP_CPU_MARKER();
}
for(int i=0; i<num_steps; i++)

View File

@ -42,6 +42,7 @@ ConnectToPeer::ConnectToPeer(uint32_t peer_id) : Protocol(PROTOCOL_CONNECTION)
m_state = NONE;
m_is_lan = false;
setHandleConnections(true);
resetTimer();
} // ConnectToPeer(peer_id)
// ----------------------------------------------------------------------------
@ -54,7 +55,8 @@ ConnectToPeer::ConnectToPeer(const TransportAddress &address)
m_peer_address.copy(address);
// We don't need to find the peer address, so we can start
// with the state when we found the peer address.
m_state = RECEIVED_PEER_ADDRESS;
m_state = WAIT_FOR_CONNECTION;
resetTimer();
m_is_lan = true;
setHandleConnections(true);
} // ConnectToPeers(TransportAddress)
@ -66,14 +68,6 @@ ConnectToPeer::~ConnectToPeer()
} // ~ConnectToPeer
// ----------------------------------------------------------------------------
void ConnectToPeer::setup()
{
m_broadcast_count = 0;
m_time_last_broadcast = 0;
} // setup
// ----------------------------------------------------------------------------
bool ConnectToPeer::notifyEventAsynchronous(Event* event)
{
if (event->getType() == EVENT_TYPE_CONNECTED)
@ -97,93 +91,81 @@ void ConnectToPeer::asynchronousUpdate()
{
case NONE:
{
m_current_protocol = std::make_shared<GetPeerAddress>(m_peer_id, this);
m_current_protocol = std::make_shared<GetPeerAddress>(m_peer_id);
m_current_protocol->requestStart();
// Pause this protocol till we receive an answer
// The GetPeerAddress protocol will change the state and
// unpause this protocol
requestPause();
m_state = RECEIVED_PEER_ADDRESS;
break;
}
case RECEIVED_PEER_ADDRESS:
{
if (m_peer_address.getIP() == 0 || m_peer_address.getPort() == 0)
// Wait until we have peer address
auto get_peer_address =
std::dynamic_pointer_cast<GetPeerAddress>(m_current_protocol);
assert(get_peer_address);
if (get_peer_address->getAddress().isUnset())
return;
m_peer_address.copy(get_peer_address->getAddress());
m_current_protocol = nullptr;
if (m_peer_address.isUnset())
{
Log::error("ConnectToPeer",
"The peer you want to connect to has hidden his address.");
m_state = DONE;
break;
}
m_current_protocol = nullptr;
// Now we know the peer address. If it's a non-local host, start
// the Ping protocol to keep the port available. We can't rely on
// STKHost::isLAN(), since we might get a LAN connection even if
// the server itself accepts connections from anywhere.
if ( (!m_is_lan &&
m_peer_address.getIP() !=
NetworkConfig::get()->getMyAddress().getIP() ) ||
NetworkConfig::m_disable_lan )
{
m_current_protocol = std::make_shared<PingProtocol>(m_peer_address,
/*time-between-ping*/2.0);
ProtocolManager::lock()->requestStart(m_current_protocol);
m_state = CONNECTING;
}
else
{
m_broadcast_count = 0;
// Make sure we trigger the broadcast operation next
m_time_last_broadcast = float(StkTime::getRealTime()-100.0f);
m_state = WAIT_FOR_LAN;
}
m_state = WAIT_FOR_CONNECTION;
resetTimer();
break;
}
case WAIT_FOR_LAN:
case WAIT_FOR_CONNECTION:
{
// Broadcast once per second
if (StkTime::getRealTime() < m_time_last_broadcast + 1.0f)
// Each 2 second for a ping or broadcast
if (m_timer > m_timer + std::chrono::seconds(2))
{
break;
}
m_time_last_broadcast = float(StkTime::getRealTime());
m_broadcast_count++;
if (m_broadcast_count > 100)
{
// Not much we can do about if we don't receive the client
// connection - it could have stopped, lost network, ...
// Terminate this protocol.
Log::error("ConnectToPeer", "Time out trying to connect to %s",
m_peer_address.toString().c_str());
requestTerminate();
}
resetTimer();
// Now we know the peer address. If it's a non-local host, start
// the Ping protocol to keep the port available. We can't rely
// on STKHost::isLAN(), since we might get a LAN connection even
// if the server itself accepts connections from anywhere.
if ((!m_is_lan &&
m_peer_address.getIP() !=
STKHost::get()->getPublicAddress().getIP()) ||
NetworkConfig::m_disable_lan)
{
BareNetworkString data;
data.addUInt8(0);
STKHost::get()->sendRawPacket(data, m_peer_address);
}
// Otherwise we are in the same LAN (same public ip address).
// Just send a broadcast packet with the string aloha_stk inside,
// the client will know our ip address and will connect
TransportAddress broadcast_address;
if(NetworkConfig::get()->isWAN())
{
broadcast_address.setIP(-1); // 255.255.255.255
broadcast_address.setPort(m_peer_address.getPort());
}
else
// Send a broadcast packet with the string aloha_stk inside,
// the client will know our ip address and will connect
// The wan remote should already start its ping message to us now
// so we can send packet directly to it.
TransportAddress broadcast_address;
broadcast_address.copy(m_peer_address);
BareNetworkString aloha(std::string("aloha_stk"));
STKHost::get()->sendRawPacket(aloha, broadcast_address);
Log::info("ConnectToPeer", "Broadcast aloha sent.");
StkTime::sleep(1);
broadcast_address.copy(m_peer_address);
broadcast_address.setIP(0x7f000001); // 127.0.0.1 (localhost)
broadcast_address.setPort(m_peer_address.getPort());
STKHost::get()->sendRawPacket(aloha, broadcast_address);
Log::info("ConnectToPeer", "Broadcast aloha to self.");
BareNetworkString aloha(std::string("aloha_stk"));
STKHost::get()->sendRawPacket(aloha, broadcast_address);
Log::info("ConnectToPeer", "Broadcast aloha sent.");
StkTime::sleep(1);
broadcast_address.setIP(0x7f000001); // 127.0.0.1 (localhost)
broadcast_address.setPort(m_peer_address.getPort());
STKHost::get()->sendRawPacket(aloha, broadcast_address);
Log::info("ConnectToPeer", "Broadcast aloha to self.");
// 30 seconds timeout
if (m_tried_connection++ > 15)
{
// Not much we can do about if we don't receive the client
// connection - it could have stopped, lost network, ...
// Terminate this protocol.
Log::error("ConnectToPeer", "Time out trying to connect to %s",
m_peer_address.toString().c_str());
requestTerminate();
}
}
break;
}
case CONNECTING: // waiting for the peer to connect
@ -193,14 +175,6 @@ void ConnectToPeer::asynchronousUpdate()
break;
case CONNECTED:
{
// If the ping protocol is there for NAT traversal terminate it.
// Ping is not running when connecting to a LAN peer.
if (m_current_protocol)
{
// Kill the ping protocol because we're connected
m_current_protocol->requestTerminate();
m_current_protocol = nullptr;
}
m_state = DONE;
break;
}
@ -212,16 +186,3 @@ void ConnectToPeer::asynchronousUpdate()
break;
}
} // asynchronousUpdate
// ----------------------------------------------------------------------------
/** Callback from the GetPeerAddress protocol. It copies the received peer
* address so that it can be used in the next states of the connection
* protocol.
*/
void ConnectToPeer::callback(Protocol *protocol)
{
assert(m_state==RECEIVED_PEER_ADDRESS);
m_peer_address.copy( ((GetPeerAddress*)protocol)->getAddress() );
// Reactivate this protocol
requestUnpause();
} // callback

View File

@ -23,49 +23,54 @@
#include "network/transport_address.hpp"
#include "utils/cpp2011.hpp"
#include <chrono>
/** One instance of this is started for every peer who tries to
* connect to this server.
*/
class ConnectToPeer : public Protocol, public CallbackObject
class ConnectToPeer : public Protocol
{
protected:
TransportAddress m_peer_address;
uint32_t m_peer_id;
/** Pointer to the protocol which is monitored for state changes. */
/** Pointer to the protocol which is monitored for state changes, this
* need to be shared_ptr because we need to get the result from
* \ref GetPeerAddress, otherwise when it terminated the result will be
* gone. */
std::shared_ptr<Protocol> m_current_protocol;
/** True if this is a LAN connection. */
bool m_is_lan;
/** We might need to broadcast several times (in case the client is not
* ready in time). This keep track of broadcastst. */
float m_time_last_broadcast;
/** Timer use for tracking broadcast. */
std::chrono::system_clock::time_point m_timer;
int m_broadcast_count;
unsigned m_tried_connection = 0;
enum STATE
{
NONE,
RECEIVED_PEER_ADDRESS,
WAIT_FOR_LAN,
WAIT_FOR_CONNECTION,
CONNECTING,
CONNECTED,
DONE,
EXITING
} m_state;
void resetTimer() { m_timer = std::chrono::system_clock::now(); }
public:
ConnectToPeer(uint32_t peer_id);
ConnectToPeer(const TransportAddress &address);
virtual ~ConnectToPeer();
virtual bool notifyEventAsynchronous(Event* event) OVERRIDE;
virtual void setup() OVERRIDE;
virtual void setup() OVERRIDE {}
virtual void update(float dt) OVERRIDE {}
virtual void asynchronousUpdate() OVERRIDE;
virtual void callback(Protocol *protocol) OVERRIDE;
}; // class ConnectToPeer
#endif // CONNECT_TO_SERVER_HPP

View File

@ -21,11 +21,9 @@
#include "config/player_manager.hpp"
#include "network/event.hpp"
#include "network/network_config.hpp"
#include "network/protocols/get_public_address.hpp"
#include "network/protocols/get_peer_address.hpp"
#include "network/protocols/hide_public_address.hpp"
#include "network/protocols/request_connection.hpp"
#include "network/protocols/ping_protocol.hpp"
#include "network/protocols/client_lobby.hpp"
#include "network/protocol_manager.hpp"
#include "network/servers_manager.hpp"
@ -82,45 +80,21 @@ ConnectToServer::~ConnectToServer()
void ConnectToServer::setup()
{
Log::info("ConnectToServer", "SETUP");
m_current_protocol = nullptr;
m_current_protocol.reset();
// In case of LAN we already have the server's and our ip address,
// so we can immediately start requesting a connection.
m_state = NetworkConfig::get()->isLAN() ? GOT_SERVER_ADDRESS : NONE;
m_state = NetworkConfig::get()->isLAN() ? GOT_SERVER_ADDRESS :
REGISTER_SELF_ADDRESS;
} // setup
// ----------------------------------------------------------------------------
/** Sets the server transport address. This is used in case of LAN networking,
* when we do not query the stk server and instead have the address from the
* LAN server directly.
* \param address Address of server to connect to.
*/
void ConnectToServer::setServerAddress(const TransportAddress &address)
{
} // setServerAddress
// ----------------------------------------------------------------------------
void ConnectToServer::asynchronousUpdate()
{
switch(m_state)
{
case NONE:
case REGISTER_SELF_ADDRESS:
{
Log::info("ConnectToServer", "Protocol starting");
// This protocol will write the public address of this
// instance to STKHost.
m_current_protocol = std::make_shared<GetPublicAddress>(this);
m_current_protocol->requestStart();
// This protocol will be unpaused in the callback from
// GetPublicAddress
requestPause();
m_state = GETTING_SELF_ADDRESS;
break;
}
case GETTING_SELF_ADDRESS:
{
// drop GetPublicAddress
m_current_protocol = nullptr;
registerWithSTKServer(); // Register us with STK server
if (m_quick_join)
@ -139,135 +113,125 @@ void ConnectToServer::asynchronousUpdate()
case GOT_SERVER_ADDRESS:
{
assert(!m_quick_join);
m_current_protocol = nullptr;
Log::info("ConnectToServer", "Server's address known");
// we're in the same lan (same public ip address) !!
if (m_server_address.getIP() ==
NetworkConfig::get()->getMyAddress().getIP())
{
Log::info("ConnectToServer",
"Server appears to be in the same LAN.");
}
m_state = REQUESTING_CONNECTION;
m_current_protocol = std::make_shared<RequestConnection>(m_server_id);
m_current_protocol->requestStart();
auto request_connection =
std::make_shared<RequestConnection>(m_server_id);
request_connection->requestStart();
m_current_protocol = request_connection;
// Reset timer for next usage
resetTimer();
break;
}
case REQUESTING_CONNECTION:
// In case of a LAN server, m_crrent_protocol is NULL
if (!m_current_protocol ||
m_current_protocol->getState() == PROTOCOL_STATE_TERMINATED)
if (!m_current_protocol.expired())
{
m_current_protocol = nullptr;
// Server knows we want to connect
Log::info("ConnectToServer", "Connection request made");
if (m_server_address.getIP() == 0 ||
m_server_address.getPort() == 0 )
{
// server data not correct, hide address and stop
m_state = HIDING_ADDRESS;
Log::error("ConnectToServer", "Server address is %s",
m_server_address.toString().c_str());
m_current_protocol = std::make_shared<HidePublicAddress>();
m_current_protocol->requestStart();
return;
}
if( ( !NetworkConfig::m_disable_lan &&
m_server_address.getIP()
== NetworkConfig::get()->getMyAddress().getIP() ) ||
NetworkConfig::get()->isLAN() )
return;
}
// Server knows we want to connect
Log::info("ConnectToServer", "Connection request made");
if (m_server_address.isUnset())
{
// server data not correct, hide address and stop
m_state = HIDING_ADDRESS;
Log::error("ConnectToServer", "Server address is %s",
m_server_address.toString().c_str());
auto hide_address = std::make_shared<HidePublicAddress>();
hide_address->requestStart();
m_current_protocol = hide_address;
return;
}
if (m_tried_connection++ > 5)
{
Log::error("ConnectToServer", "Timeout waiting for aloha");
m_state = NetworkConfig::get()->isWAN() ?
HIDING_ADDRESS : DONE;
}
if ((!NetworkConfig::m_disable_lan &&
m_server_address.getIP() ==
STKHost::get()->getPublicAddress().getIP()) ||
NetworkConfig::get()->isLAN())
{
// We're in the same lan (same public ip address).
// The state will change to CONNECTING
waitingAloha(false/*is_wan*/);
}
else
{
// Send a 1-byte datagram, the remote host can simply ignore
// this datagram, to keep the port open (2 second each)
if (m_timer > m_timer + std::chrono::seconds(2))
{
// We're in the same lan (same public ip address).
// The state will change to CONNECTING
handleSameLAN();
}
else
{
m_state = CONNECTING;
m_current_protocol = std::make_shared<PingProtocol>(m_server_address, 2.0);
m_current_protocol->requestStart();
resetTimer();
BareNetworkString data;
data.addUInt8(0);
STKHost::get()->sendRawPacket(data, m_server_address);
}
waitingAloha(true/*is_wan*/);
}
break;
case CONNECTING: // waiting the server to answer our connection
{
if (m_timer > m_timer + std::chrono::seconds(5)) // every 5 seconds
{
static double timer = 0;
if (StkTime::getRealTime() > timer+5.0) // every 5 seconds
STKHost::get()->connect(m_server_address);
resetTimer();
Log::info("ConnectToServer", "Trying to connect to %s",
m_server_address.toString().c_str());
if (m_tried_connection++ > 3)
{
STKHost::get()->connect(m_server_address);
timer = StkTime::getRealTime();
Log::info("ConnectToServer", "Trying to connect to %s",
m_server_address.toString().c_str());
Log::error("ConnectToServer", "Timeout connect to %s",
m_server_address.toString().c_str());
m_state = NetworkConfig::get()->isWAN() ?
HIDING_ADDRESS : DONE;
}
break;
}
break;
}
case CONNECTED:
{
Log::info("ConnectToServer", "Connected");
if(m_current_protocol)
{
// Kill the ping protocol because we're connected
m_current_protocol->requestTerminate();
}
m_current_protocol = nullptr;
// LAN networking does not use the stk server tables.
if(NetworkConfig::get()->isWAN())
if (NetworkConfig::get()->isWAN())
{
m_current_protocol = std::make_shared<HidePublicAddress>();
m_current_protocol->requestStart();
auto hide_address = std::make_shared<HidePublicAddress>();
hide_address->requestStart();
m_current_protocol = hide_address;
}
m_state = HIDING_ADDRESS;
break;
}
case HIDING_ADDRESS:
// Wait till we have hidden our address
if (!m_current_protocol ||
m_current_protocol->getState() == PROTOCOL_STATE_TERMINATED)
if (!m_current_protocol.expired())
{
if(m_current_protocol)
{
m_current_protocol = nullptr;
Log::info("ConnectToServer", "Address hidden");
}
m_state = DONE;
// lobby room protocol if we're connected only
if(STKHost::get()->getPeers()[0]->isConnected())
{
auto cl = LobbyProtocol::create<ClientLobby>();
cl->setAddress(m_server_address);
cl->requestStart();
}
return;
}
m_state = DONE;
// lobby room protocol if we're connected only
if (STKHost::get()->getPeers()[0]->isConnected() &&
!m_server_address.isUnset())
{
auto cl = LobbyProtocol::create<ClientLobby>();
cl->setAddress(m_server_address);
cl->requestStart();
}
break;
case DONE:
requestTerminate();
m_state = EXITING;
if (STKHost::get()->getPeerCount() == 0)
{
// Shutdown STKHost (go back to online menu too)
STKHost::get()->requestShutdown();
}
break;
case EXITING:
break;
}
} // asynchronousUpdate
// ----------------------------------------------------------------------------
/** Called when the GetPeerAddress protocol terminates.
*/
void ConnectToServer::callback(Protocol *protocol)
{
switch(m_state)
{
case GETTING_SELF_ADDRESS:
// The GetPublicAddress protocol stores our address in
// STKHost, so we only need to unpause this protocol
requestUnpause();
break;
default:
Log::error("ConnectToServer",
"Received unexpected callback while in state %d.",
m_state);
} // case m_state
} // callback
// ----------------------------------------------------------------------------
/** Register this client with the STK server.
*/
@ -275,14 +239,13 @@ void ConnectToServer::registerWithSTKServer()
{
// Our public address is now known, register details with
// STK server.
const TransportAddress& addr = NetworkConfig::get()->getMyAddress();
const TransportAddress& addr = STKHost::get()->getPublicAddress();
Online::XMLRequest *request = new Online::XMLRequest();
PlayerManager::setUserDetails(request, "set",
Online::API::SERVER_PATH);
request->addParameter("address", addr.getIP());
request->addParameter("port", addr.getPort());
request->addParameter("private_port",
NetworkConfig::get()->getClientPort());
request->addParameter("private_port", STKHost::get()->getPrivatePort());
Log::info("ConnectToServer", "Registering addr %s",
addr.toString().c_str());
@ -318,7 +281,6 @@ void ConnectToServer::handleQuickConnect()
request->executeNow();
const XMLNode * result = request->getXMLData();
delete request;
std::string success;
if(result->get("success", &success) && success=="yes")
@ -330,7 +292,7 @@ void ConnectToServer::handleQuickConnect()
uint16_t port;
// If we are using a LAN connection, we need the private (local) port
if (m_server_address.getIP() ==
NetworkConfig::get()->getMyAddress().getIP())
STKHost::get()->getPublicAddress().getIP())
{
result->get("private_port", &port);
}
@ -345,13 +307,15 @@ void ConnectToServer::handleQuickConnect()
{
Log::error("GetPeerAddress", "Failed to get address.");
}
delete request;
} // handleQuickConnect
// ----------------------------------------------------------------------------
/** Called when the server is on the same LAN. It uses broadcast to
* find and conntect to the server.
* find and conntect to the server. For WAN game, it makes sure server recieve
* request from stk addons first before continuing.
*/
void ConnectToServer::handleSameLAN()
void ConnectToServer::waitingAloha(bool is_wan)
{
// just send a broadcast packet, the client will know our
// ip address and will connect
@ -379,65 +343,70 @@ void ConnectToServer::handleSameLAN()
std::string aloha("aloha_stk");
if (received==aloha)
{
Log::info("ConnectToServer", "LAN Server found : %s",
Log::info("ConnectToServer", "Server found : %s",
sender.toString().c_str());
#ifndef WIN32
// just check if the ip is ours : if so,
// 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 (!is_wan)
{
if (ifa->ifa_addr->sa_family == AF_INET)
// just check if the ip is ours : if so,
// 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)
{
sa = (struct sockaddr_in *) ifa->ifa_addr;
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
// This interface is ours
if (ntohl(sa->sin_addr.s_addr) == sender.getIP())
sender.setIP(0x7f000001); // 127.0.0.1
}
}
}
freeifaddrs(ifap);
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
// 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)
{
sender.setIP(0x7f000001); // 127.0.0.1
break;
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;
delete[] table;
#endif
m_server_address.copy(sender);
m_server_address.copy(sender);
}
m_state = CONNECTING;
// Reset timer for next usage
resetTimer();
m_tried_connection = 0;
}
} // handleSameLAN
} // waitingAloha
// ----------------------------------------------------------------------------
@ -448,7 +417,6 @@ bool ConnectToServer::notifyEventAsynchronous(Event* event)
Log::info("ConnectToServer", "The Connect To Server protocol has "
"received an event notifying that he's connected to the peer.");
m_state = CONNECTED; // we received a message, we are connected
Server *server = ServersManager::get()->getJoinedServer();
}
return true;
} // notifyEventAsynchronous

View File

@ -22,27 +22,28 @@
#include "network/protocol.hpp"
#include "network/transport_address.hpp"
#include "utils/cpp2011.hpp"
#include <chrono>
#include <string>
class ConnectToServer : public Protocol, public CallbackObject
class ConnectToServer : public Protocol
{
private:
std::chrono::system_clock::time_point m_timer;
TransportAddress m_server_address;
uint32_t m_server_id;
uint32_t m_host_id;
unsigned m_tried_connection = 0;
/** Protocol currently being monitored. */
std::shared_ptr<Protocol> m_current_protocol;
std::weak_ptr<Protocol> m_current_protocol;
bool m_quick_join;
/** State for finite state machine. */
enum
{
NONE,
GETTING_SELF_ADDRESS,
REGISTER_SELF_ADDRESS,
GOT_SERVER_ADDRESS,
REQUESTING_CONNECTION,
QUICK_JOIN,
CONNECTING,
CONNECTED,
HIDING_ADDRESS,
@ -52,7 +53,8 @@ private:
void registerWithSTKServer();
void handleQuickConnect();
void handleSameLAN();
void waitingAloha(bool is_wan);
void resetTimer() { m_timer = std::chrono::system_clock::now(); }
public:
ConnectToServer();
@ -62,9 +64,7 @@ public:
virtual bool notifyEventAsynchronous(Event* event) OVERRIDE;
virtual void setup() OVERRIDE;
virtual void asynchronousUpdate() OVERRIDE;
virtual void callback(Protocol *protocol) OVERRIDE;
virtual void update(float dt) OVERRIDE {}
void setServerAddress(const TransportAddress &address);
}; // class ConnectToServer
#endif // CONNECT_TO_SERVER_HPP

View File

@ -21,13 +21,12 @@
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
#include "network/network_config.hpp"
#include "network/protocol_manager.hpp"
#include "network/stk_host.hpp"
#include "online/request_manager.hpp"
#include "utils/log.hpp"
GetPeerAddress::GetPeerAddress(uint32_t peer_id,
CallbackObject* callback_object)
: Protocol(PROTOCOL_SILENT, callback_object)
GetPeerAddress::GetPeerAddress(uint32_t peer_id)
: Protocol(PROTOCOL_SILENT, NULL)
{
m_peer_id = peer_id;
} // GetPeerAddress
@ -41,7 +40,6 @@ GetPeerAddress::~GetPeerAddress()
void GetPeerAddress::setup()
{
m_address.clear();
m_request = new Online::XMLRequest();
PlayerManager::setUserDetails(m_request, "get",
Online::API::SERVER_PATH);
@ -65,7 +63,7 @@ void GetPeerAddress::asynchronousUpdate()
m_address.setIP(ip);
uint16_t port;
uint32_t my_ip = NetworkConfig::get()->getMyAddress().getIP();
uint32_t my_ip = STKHost::get()->getPublicAddress().getIP();
if (m_address.getIP() == my_ip && !NetworkConfig::m_disable_lan)
result->get("private_port", &port);
else
@ -84,9 +82,3 @@ void GetPeerAddress::asynchronousUpdate()
m_request = NULL;
}
} // asynchronousUpdate
// ----------------------------------------------------------------------------
void GetPeerAddress::setPeerID(uint32_t peer_id)
{
m_peer_id = peer_id;
} // setPeerID

View File

@ -35,13 +35,12 @@ private:
* to get the result. */
TransportAddress m_address;
public:
GetPeerAddress(uint32_t peer_id, CallbackObject* callback_object);
GetPeerAddress(uint32_t peer_id);
virtual ~GetPeerAddress();
virtual void setup() OVERRIDE;
virtual void asynchronousUpdate() OVERRIDE;
void setPeerID(uint32_t m_peer_id);
void setPeerID(uint32_t peer_id) { m_peer_id = peer_id; }
// ------------------------------------------------------------------------
/** Returns the address found. */
const TransportAddress &getAddress() const { return m_address; }

View File

@ -19,6 +19,7 @@
#include "network/protocols/get_public_address.hpp"
#include "config/user_config.hpp"
#include "guiengine/message_queue.hpp"
#include "network/network.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp"
@ -28,6 +29,8 @@
#include "utils/string_utils.hpp"
#include <assert.h>
#include <algorithm>
#include <random>
#include <string>
#ifdef __MINGW32__
@ -48,36 +51,16 @@
const uint32_t GetPublicAddress::m_stun_magic_cookie = 0x2112A442;
TransportAddress GetPublicAddress::m_my_address(0, 0);
void GetPublicAddress::setMyIPAddress(const std::string &s)
{
std::vector<std::string> l = StringUtils::split(s, ':');
if (l.size() != 2)
{
Log::fatal("Invalid IP address '%s'.", s.c_str());
}
std::vector<std::string> ip = StringUtils::split(l[0], '.');
if (ip.size() != 4)
{
Log::fatal("Invalid IP address '%s'.", s.c_str());
}
uint32_t u = 0;
for (unsigned int i = 0; i < 4; i++)
{
int k;
StringUtils::fromString(ip[i], k);
u = (u << 8) + k;
}
m_my_address.setIP(u);
int p;
StringUtils::fromString(l[1], p);
m_my_address.setPort(p);
} // setMyIPAddress
// ============================================================================
GetPublicAddress::GetPublicAddress(CallbackObject *callback)
: Protocol(PROTOCOL_SILENT, callback)
GetPublicAddress::GetPublicAddress()
: Protocol(PROTOCOL_SILENT, NULL)
{
m_state = NOTHING_DONE;
m_untried_server = UserConfigParams::m_stun_servers;
// Generate random list of stun servers
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(m_untried_server.begin(), m_untried_server.end(), g);
} // GetPublicAddress
// ----------------------------------------------------------------------------
@ -85,15 +68,22 @@ GetPublicAddress::GetPublicAddress(CallbackObject *callback)
* the list stored in the config file. See
* https://tools.ietf.org/html/rfc5389#section-6
* for details on the message structure.
* The request is send through m_transaction_host, from which the answer
* The request is send through transaction_host, from which the answer
* will be retrieved by parseStunResponse()
*/
void GetPublicAddress::createStunRequest()
Network* GetPublicAddress::createStunRequest()
{
// Pick a random stun server
std::vector<std::string> stun_servers = UserConfigParams::m_stun_servers;
if (m_untried_server.empty())
{
// Notice: MessageQueue is thread safe to add
MessageQueue::add(MessageQueue::MT_ERROR,
_("Failed to get public address from stun server."));
requestTerminate();
return NULL;
}
const char* server_name = stun_servers[rand() % stun_servers.size()].c_str();
// Pick last element in untried servers
const char* server_name = m_untried_server.back().c_str();
Log::debug("GetPublicAddress", "Using STUN server %s", server_name);
struct addrinfo hints, *res;
@ -106,10 +96,12 @@ void GetPublicAddress::createStunRequest()
int status = getaddrinfo(server_name, NULL, &hints, &res);
if (status != 0)
{
Log::error("GetPublicAddress", "Error in getaddrinfo: %s",
gai_strerror(status));
return;
Log::error("GetPublicAddress", "Error in getaddrinfo for stun server"
" %s: %s", server_name, gai_strerror(status));
m_untried_server.pop_back();
return NULL;
}
m_untried_server.pop_back();
// documentation says it points to "one or more addrinfo structures"
assert(res != NULL);
struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr);
@ -119,7 +111,7 @@ void GetPublicAddress::createStunRequest()
ENetAddress addr;
addr.host = STKHost::HOST_ANY;
addr.port = STKHost::PORT_ANY;
m_transaction_host = new Network(1, 1, 0, 0, &addr);
Network* transaction_host = new Network(1, 1, 0, 0, &addr);
// Assemble the message for the stun server
BareNetworkString s(20);
@ -138,11 +130,11 @@ void GetPublicAddress::createStunRequest()
m_stun_tansaction_id[i] = random_byte;
}
m_transaction_host->sendRawPacket(s,
transaction_host->sendRawPacket(s,
TransportAddress(m_stun_server_ip,
m_stun_server_port) );
freeaddrinfo(res);
m_state = STUN_REQUEST_SENT;
return transaction_host;
} // createStunRequest
// ----------------------------------------------------------------------------
@ -151,12 +143,13 @@ void GetPublicAddress::createStunRequest()
* then parses the answer into address and port
* \return "" if the address could be parsed or an error message
*/
std::string GetPublicAddress::parseStunResponse()
std::string GetPublicAddress::parseStunResponse(Network* transaction_host)
{
TransportAddress sender;
const int LEN = 2048;
char buffer[LEN];
int len = m_transaction_host->receiveRawPacket(buffer, LEN, &sender, 2000);
int len = transaction_host->receiveRawPacket(buffer, LEN, &sender, 2000);
delete transaction_host;
if(sender.getIP()!=m_stun_server_ip)
{
@ -199,7 +192,6 @@ std::string GetPublicAddress::parseStunResponse()
// Those are the port and the address to be detected
int pos = 20;
while (true)
{
int type = datas.getUInt16();
@ -239,35 +231,27 @@ void GetPublicAddress::asynchronousUpdate()
if (m_my_address.getIP() != 0 && m_my_address.getPort() != 0)
{
NetworkConfig::get()->setMyAddress(m_my_address);
m_state = EXITING;
requestTerminate();
}
//#define LAN_TEST
#ifdef LAN_TEST
TransportAddress address(0x7f000001, 4);
NetworkConfig::get()->setMyAddress(address);
m_state = EXITING;
requestTerminate();
return;
#endif
if (m_state == NOTHING_DONE)
Network* transaction_host = createStunRequest();
if (transaction_host)
{
createStunRequest();
}
if (m_state == STUN_REQUEST_SENT)
{
std::string message = parseStunResponse();
delete m_transaction_host;
std::string message = parseStunResponse(transaction_host);
if (message != "")
{
Log::warn("GetPublicAddress", "%s", message.c_str());
m_state = NOTHING_DONE; // try again
}
else
{
// The address and the port are known, so the connection can be closed
m_state = EXITING;
requestTerminate();
}
}

View File

@ -30,8 +30,10 @@ class Network;
class GetPublicAddress : public Protocol
{
private:
void createStunRequest();
std::string parseStunResponse();
Network* createStunRequest();
std::string parseStunResponse(Network* transaction_host);
std::vector<std::string> m_untried_server;
// Constants
static const uint32_t m_stun_magic_cookie;
@ -41,20 +43,13 @@ private:
* unnecessary (though that means that the user has to take care of
* opening the firewall). */
static TransportAddress m_my_address;
enum State
{
NOTHING_DONE,
STUN_REQUEST_SENT,
EXITING
} m_state;
uint8_t m_stun_tansaction_id[12];
uint32_t m_stun_server_ip;
Network* m_transaction_host;
public:
static void setMyIPAddress(const std::string &s);
GetPublicAddress(CallbackObject *callback = NULL);
GetPublicAddress();
virtual ~GetPublicAddress() {}
virtual void asynchronousUpdate() OVERRIDE;
@ -65,7 +60,7 @@ public:
// ------------------------------------------------------------------------
virtual bool notifyEventAsynchronous(Event* event) OVERRIDE { return true; }
// ------------------------------------------------------------------------
virtual void setup() { m_state = NOTHING_DONE; }
virtual void setup() { }
// ------------------------------------------------------------------------
}; // class GetPublicAddress

View File

@ -56,7 +56,7 @@ void HidePublicAddress::asynchronousUpdate()
{
if(rec_success == "yes")
{
Log::debug("HidePublicAddress", "Address hidden successfully.");
Log::info("HidePublicAddress", "Address hidden successfully.");
}
else
{

View File

@ -26,11 +26,12 @@
* \param delay_between_pings: How often to ping.
*/
PingProtocol::PingProtocol(const TransportAddress& ping_dst,
double delay_between_pings)
double delay_between_pings, double timeout)
: Protocol(PROTOCOL_SILENT)
{
m_ping_dst.copy(ping_dst);
m_delay_between_pings = delay_between_pings;
m_timeout = timeout;
} // PingProtocol
// ----------------------------------------------------------------------------
@ -41,7 +42,7 @@ PingProtocol::~PingProtocol()
// ----------------------------------------------------------------------------
void PingProtocol::setup()
{
m_last_ping_time = 0;
m_last_ping_time = 0.0;
} // setup
// ----------------------------------------------------------------------------
@ -49,6 +50,10 @@ void PingProtocol::asynchronousUpdate()
{
if (StkTime::getRealTime() > m_last_ping_time+m_delay_between_pings)
{
if (m_last_ping_time == 0.0)
m_timeout = StkTime::getRealTime() + m_timeout;
else if (StkTime::getRealTime() > m_timeout)
requestTerminate();
m_last_ping_time = StkTime::getRealTime();
BareNetworkString data;
data.addUInt8(0);

View File

@ -16,9 +16,12 @@ private:
/** Time of last ping. */
double m_last_ping_time;
/** If longer than this, terminate this protocol. */
double m_timeout;
public:
PingProtocol(const TransportAddress& ping_dst,
double delay_between_pings);
double delay_between_pings, double timeout = 60.0);
virtual ~PingProtocol();
virtual void asynchronousUpdate() OVERRIDE;

View File

@ -115,7 +115,6 @@ void ServerLobby::setup()
m_state = NetworkConfig::get()->isLAN() ? ACCEPTING_CLIENTS
: INIT_WAN;
m_selection_enabled = false;
m_current_protocol = nullptr;
Log::info("ServerLobby", "Starting the protocol.");
// Initialise the data structures to detect if all clients and
@ -201,27 +200,20 @@ void ServerLobby::update(float dt)
switch (m_state.load())
{
case INIT_WAN:
{
// Start the protocol to find the public ip address.
m_current_protocol = std::make_shared<GetPublicAddress>(this);
m_current_protocol->requestStart();
m_state = GETTING_PUBLIC_ADDRESS;
// The callback from GetPublicAddress will wake this protocol up
requestPause();
break;
}
case GETTING_PUBLIC_ADDRESS:
{
Log::debug("ServerLobby", "Public address known.");
// Free GetPublicAddress protocol
m_current_protocol = nullptr;
// Register this server with the STK server. This will block
// this thread, but there is no need for the protocol manager
// to react to any requests before the server is registered.
registerServer();
Log::info("ServerLobby", "Server registered.");
m_state = ACCEPTING_CLIENTS;
break;
}
break;
case ACCEPTING_CLIENTS:
{
// Only poll the STK server if this is a WAN server.
@ -306,15 +298,6 @@ void ServerLobby::update(float dt)
}
} // update
//-----------------------------------------------------------------------------
/** Callback when the GetPublicAddress terminates. It will unpause this
* protocol, which triggers the next state of the finite state machine.
*/
void ServerLobby::callback(Protocol *protocol)
{
requestUnpause();
} // callback
//-----------------------------------------------------------------------------
/** Register this server (i.e. its public address) with the STK server
* so that clients can find it. It blocks till a response from the
@ -325,12 +308,12 @@ void ServerLobby::callback(Protocol *protocol)
void ServerLobby::registerServer()
{
Online::XMLRequest *request = new Online::XMLRequest();
const TransportAddress& addr = NetworkConfig::get()->getMyAddress();
const TransportAddress& addr = STKHost::get()->getPublicAddress();
PlayerManager::setUserDetails(request, "create", Online::API::SERVER_PATH);
request->addParameter("address", addr.getIP() );
request->addParameter("port", addr.getPort() );
request->addParameter("private_port",
NetworkConfig::get()->getServerPort() );
STKHost::get()->getPrivatePort() );
request->addParameter("name", NetworkConfig::get()->getServerName() );
request->addParameter("max_players",
UserConfigParams::m_server_max_players );
@ -350,9 +333,11 @@ void ServerLobby::registerServer()
{
irr::core::stringc error(request->getInfo().c_str());
Log::error("RegisterServer", "%s", error.c_str());
STKHost::get()->setErrorMessage(_("Failed to register server: %s", error.c_str()));
STKHost::get()->setErrorMessage(_("Failed to register server: %s",
error.c_str()));
STKHost::get()->requestShutdown();
}
delete request;
} // registerServer
//-----------------------------------------------------------------------------
@ -444,7 +429,7 @@ void ServerLobby::checkIncomingConnectionRequests()
PlayerManager::setUserDetails(request, "poll-connection-requests",
Online::API::SERVER_PATH);
const TransportAddress &addr = NetworkConfig::get()->getMyAddress();
const TransportAddress &addr = STKHost::get()->getPublicAddress();
request->addParameter("address", addr.getIP() );
request->addParameter("port", addr.getPort());
@ -469,7 +454,7 @@ void ServerLobby::checkIncomingConnectionRequests()
Log::debug("ServerLobby",
"User with id %d wants to connect.", id);
std::make_shared<ConnectToPeer>(id)->requestStart();
}
}
delete request;
} // checkIncomingConnectionRequests

View File

@ -9,7 +9,6 @@
#include <set>
class ServerLobby : public LobbyProtocol
, public CallbackObject
{
public:
/* The state for a small finite state machine. */
@ -57,7 +56,6 @@ private:
* seconds), which is the real time at which the server should start. */
double m_server_delay;
std::shared_ptr<Protocol> m_current_protocol;
bool m_selection_enabled;
/** Counts how many players are ready to go on. */
@ -98,7 +96,6 @@ public:
void checkRaceFinished();
void finishedLoadingWorld();
ServerState getCurrentState() const { return m_state.load(); }
virtual void callback(Protocol *protocol) OVERRIDE;
}; // class ServerLobby

View File

@ -43,7 +43,24 @@
# include <errno.h>
# include <sys/socket.h>
#endif
#ifdef __MINGW32__
# undef _WIN32_WINNT
# define _WIN32_WINNT 0x501
#endif
#ifdef WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <netdb.h>
#endif
#include <sys/types.h>
#include <algorithm>
#include <functional>
#include <random>
#include <string>
STKHost *STKHost::m_stk_host = NULL;
bool STKHost::m_enable_console = false;
@ -244,10 +261,10 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id)
// server is made.
m_host_id = 0;
init();
TransportAddress a;
a.setIP(0);
a.setPort(NetworkConfig::get()->getClientPort());
ENetAddress ea = a.toEnetAddress();
ENetAddress ea;
ea.host = STKHost::HOST_ANY;
ea.port = STKHost::PORT_ANY;
m_network = new Network(/*peer_count*/1, /*channel_limit*/2,
/*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea);
@ -257,7 +274,16 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id)
"an ENet client host.");
}
std::make_shared<ConnectToServer>(server_id, host_id)->requestStart();
setPrivatePort();
if (NetworkConfig::get()->isWAN())
{
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
// ----------------------------------------------------------------------------
@ -274,7 +300,7 @@ STKHost::STKHost(const irr::core::stringw &server_name)
ENetAddress addr;
addr.host = STKHost::HOST_ANY;
addr.port = NetworkConfig::get()->getServerPort();
addr.port = STKHost::PORT_ANY;
m_network= new Network(NetworkConfig::get()->getMaxPlayers(),
/*channel_limit*/2,
@ -286,8 +312,18 @@ STKHost::STKHost(const irr::core::stringw &server_name)
"ENet server host.");
}
startListening();
ProtocolManager::lock()->requestStart(LobbyProtocol::create<ServerLobby>());
setPrivatePort();
if (NetworkConfig::get()->isWAN())
{
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)
@ -364,6 +400,192 @@ void STKHost::shutdown()
destroy();
} // shutdown
//-----------------------------------------------------------------------------
/** Set the public address using stun protocol.
*/
void STKHost::setPublicAddress()
{
std::vector<std::string> untried_server = UserConfigParams::m_stun_servers;
// Generate random list of stun servers
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(untried_server.begin(), untried_server.end(), g);
while (!untried_server.empty())
{
// Pick last element in untried servers
const char* server_name = untried_server.back().c_str();
Log::debug("STKHost", "Using STUN server %s", server_name);
struct addrinfo hints, *res;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
// Resolve the stun server name so we can send it a STUN request
int status = getaddrinfo(server_name, NULL, &hints, &res);
if (status != 0)
{
Log::error("STKHost", "Error in getaddrinfo for stun server"
" %s: %s", server_name, gai_strerror(status));
untried_server.pop_back();
continue;
}
untried_server.pop_back();
// documentation says it points to "one or more addrinfo structures"
assert(res != NULL);
struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr);
uint32_t stun_server_ip = ntohl(current_interface->sin_addr.s_addr);
// Assemble the message for the stun server
BareNetworkString s(20);
// bytes 0-1: the type of the message
// bytes 2-3: message length added to header (attributes)
uint16_t message_type = 0x0001; // binding request
uint16_t message_length = 0x0000;
s.addUInt16(message_type).addUInt16(message_length)
.addUInt32(0x2112A442);
uint8_t stun_tansaction_id[12];
// bytes 8-19: the transaction id
for (int i = 0; i < 12; i++)
{
uint8_t random_byte = rand() % 256;
s.addUInt8(random_byte);
stun_tansaction_id[i] = random_byte;
}
m_network->sendRawPacket(s, TransportAddress(stun_server_ip, 3478));
freeaddrinfo(res);
// Recieve now
TransportAddress sender;
const int LEN = 2048;
char buffer[LEN];
int len = m_network->receiveRawPacket(buffer, LEN, &sender, 2000);
if (sender.getIP() != stun_server_ip)
{
TransportAddress stun(stun_server_ip, 3478);
Log::warn("STKHost",
"Received stun response from %s instead of %s.",
sender.toString().c_str(), stun.toString().c_str());
}
if (len < 0)
{
Log::error("STKHost", "STUN response contains no data at all");
continue;
}
// Convert to network string.
BareNetworkString datas(buffer, len);
// 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 "
"cookie");
continue;
}
int message_size = datas.getUInt16();
if (datas.getUInt32() != 0x2112A442)
{
Log::error("STKHost", "STUN response doesn't contain the magic "
"cookie");
continue;
}
for (int i = 0; i < 12; i++)
{
if (datas.getUInt8() != stun_tansaction_id[i])
{
Log::error("STKHost", "STUN response doesn't contain the "
"transaction ID");
continue;
}
}
Log::debug("GetPublicAddress",
"The STUN server responded with a valid answer");
// 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
bool found = false;
while (true)
{
int type = datas.getUInt16();
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.");
break;
}
// Cannot even read the size
if (message_size < 4)
{
Log::error("STKHost", "STUN response is invalid.");
break;
}
} // while true
// Found public address and port
if (found)
untried_server.clear();
}
// We shutdown next frame if no public address
if (m_public_address.isUnset())
requestShutdown();
} // setPublicAddress
//-----------------------------------------------------------------------------
void STKHost::setPrivatePort()
{
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
ENetHost *host = m_network->getENetHost();
if (getsockname(host->socket, (struct sockaddr *)&sin, &len) == -1)
{
Log::error("STKHost", "Error while using getsockname().");
m_private_port = 0;
}
else
m_private_port = ntohs(sin.sin_port);
} // setPrivatePort
//-----------------------------------------------------------------------------
/** A previous GameSetup is deletea and a new one is created.
* \return Newly create GameSetup object.
@ -495,9 +717,7 @@ void STKHost::mainLoop()
// A separate network connection (socket) to handle LAN requests.
Network* lan_network = NULL;
if (NetworkConfig::get()->isServer() &&
(NetworkConfig::get()->isLAN() || NetworkConfig::get()->isPublicServer()) )
if (NetworkConfig::get()->isLAN())
{
TransportAddress address(0, NetworkConfig::get()->getServerDiscoveryPort());
ENetAddress eaddr = address.toEnetAddress();
@ -610,6 +830,13 @@ void STKHost::handleDirectSocketRequest(Network* lan_network)
{
// In case of a LAN connection, we only allow connections from
// a LAN address (192.168*, ..., and 127.*).
if (!sender.isLAN())
{
Log::error("STKHost", "Client trying to connect from '%s'",
sender.toString().c_str());
Log::error("STKHost", "which is outside of LAN - rejected.");
return;
}
std::make_shared<ConnectToPeer>(sender)->requestStart();
}
else
@ -742,20 +969,6 @@ void STKHost::removePeer(const STKPeer* peer)
m_peers.size());
} // removePeer
//-----------------------------------------------------------------------------
uint16_t STKHost::getPort() const
{
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
ENetHost *host = m_network->getENetHost();
if (getsockname(host->socket, (struct sockaddr *)&sin, &len) == -1)
Log::error("STKHost", "Error while using getsockname().");
else
return ntohs(sin.sin_port);
return 0;
} // getPort
//-----------------------------------------------------------------------------
/** Sends data to all peers except the specified one.
* \param peer Peer which will not receive the message.

View File

@ -103,12 +103,22 @@ private:
* in the GUI. */
irr::core::stringw m_error_message;
/** The public address found by stun (if WAN is used). */
TransportAddress m_public_address;
/** The private port enet socket is bound. */
uint16_t m_private_port;
/** An error message, which is set by a protocol to be displayed
* in the GUI. */
STKHost(uint32_t server_id, uint32_t host_id);
STKHost(const irr::core::stringw &server_name);
virtual ~STKHost();
void init();
void handleDirectSocketRequest(Network* lan_network);
// ------------------------------------------------------------------------
void setPublicAddress();
// ------------------------------------------------------------------------
void mainLoop();
@ -140,7 +150,15 @@ public:
// ------------------------------------------------------------------------
/** Checks if the STKHost has been created. */
static bool existHost() { return m_stk_host != NULL; }
// ------------------------------------------------------------------------
const TransportAddress& getPublicAddress() const
{ return m_public_address; }
// ------------------------------------------------------------------------
uint16_t getPrivatePort() const
{ return m_private_port; }
// ------------------------------------------------------------------------
void setPrivatePort();
// ------------------------------------------------------------------------
virtual GameSetup* setupNewGame();
void abort();
void deleteAllPeers();
@ -175,7 +193,6 @@ public:
STKPeer *getPeer(ENetPeer *enet_peer);
STKPeer *getServerPeerForClient() const;
std::vector<NetworkPlayerProfile*> getMyPlayerProfiles();
uint16_t getPort() const;
void setErrorMessage(const irr::core::stringw &message);
bool isAuthorisedToControl() const;
const irr::core::stringw&

View File

@ -90,6 +90,8 @@ private:
public:
bool isLAN() const;
// ------------------------------------------------------------------------
bool isUnset() const { return m_ip == 0 || m_port == 0; }
// ------------------------------------------------------------------------
/** A copy function (to replace the copy constructor which is disabled
* using NoCopy): it copies the data from the argument into this object.*/
void copy(const TransportAddress &other)