Extend the netpuncher: clients can ask hosts for a connection initiation to circumvent port restricted NATs

directional-lights
Julius Michaelis 2016-10-20 21:14:08 +02:00
parent 078ac47780
commit e61dcbe4c3
19 changed files with 543 additions and 133 deletions

View File

@ -991,8 +991,12 @@ src/lib/StdBuf.h
src/lib/StdCompiler.cpp
src/lib/StdCompiler.h
src/lib/StdResStr2.cpp
src/netpuncher/C4PuncherPacket.cpp
src/netpuncher/C4PuncherPacket.h
src/network/C4NetIO.cpp
src/network/C4NetIO.h
src/network/C4Network2Address.cpp
src/network/C4Network2Address.h
src/platform/StdFile.cpp
src/platform/StdFile.h
src/platform/StdRegistry.cpp
@ -1235,6 +1239,7 @@ target_link_libraries(c4group
)
add_executable(netpuncher EXCLUDE_FROM_ALL
src/netpuncher/C4PuncherHash.h
src/netpuncher/main.cpp
)

View File

@ -184,7 +184,7 @@ void C4ConfigNetwork::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt(PacketLogging, "PacketLogging", 0 ));
pComp->Value(mkNamingAdapt(s(PuncherAddress), "PuncherAddress", "netpuncher.openclonk.org:11115")); // maybe store default for this one?
pComp->Value(mkNamingAdapt(s(PuncherAddress), "PuncherAddress", "netpuncher.openclonk.org:11115"));
pComp->Value(mkNamingAdapt(mkParAdapt(LastLeagueServer, StdCompiler::RCT_All), "LastLeagueServer", "" ));
pComp->Value(mkNamingAdapt(mkParAdapt(LastLeaguePlayerName, StdCompiler::RCT_All), "LastLeaguePlayerName", "" ));
pComp->Value(mkNamingAdapt(mkParAdapt(LastLeagueAccount, StdCompiler::RCT_All), "LastLeagueAccount", "" ));

View File

@ -0,0 +1,71 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
#ifndef C4PuncherHash_H
#define C4PuncherHash_H
#include <functional>
#include "network/C4NetIO.h"
namespace {
size_t hash_combiner(size_t left, size_t right) //replacable
{ return left*2793419347^right; }
template<int index, class...types>
struct hash_impl {
size_t operator()(size_t a, const std::tuple<types...>& t) const {
typedef typename std::tuple_element<index-1, std::tuple<types...>>::type nexttype;
hash_impl<index-1, types...> next;
size_t b = std::hash<nexttype>()(std::get<index-1>(t));
return next(hash_combiner(a, b), t);
}
};
template<class...types>
struct hash_impl<1, types...> {
size_t operator()(size_t a, const std::tuple<types...>& t) const {
typedef typename std::tuple_element<0, std::tuple<types...>>::type nexttype;
size_t b = std::hash<nexttype>()(std::get<0>(t));
return hash_combiner(a, b);
}
};
template<class...types>
struct hash_impl<0, types...> {
size_t operator()(size_t a, const std::tuple<types...>& t) const {
return 0;
}
};
}
namespace std {
// Remove this when C++ gets proper tuple hashes. For now, http://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unordered-map-unordered-set will do
template<class...types>
struct hash<std::tuple<types...>> {
size_t operator()(const std::tuple<types...>& t) const {
const size_t begin = std::tuple_size<std::tuple<types...>>::value;
return hash_impl<begin, types...>()(19739, t);
}
};
template<>
struct hash<::sockaddr_in> {
size_t operator()(const ::sockaddr_in& addr) const {
auto unpack = make_tuple(addr.sin_family, addr.sin_addr.s_addr, addr.sin_port);
return hash<decltype(unpack)>()(unpack);
}
};
}
#endif

View File

@ -0,0 +1,74 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
#include "C4Include.h"
#include "netpuncher/C4PuncherPacket.h"
#include "network/C4Network2Address.h"
#include <sstream>
std::unique_ptr<C4NetpuncherPacket> C4NetpuncherPacket::Construct(const C4NetIOPacket& rpack) {
if (!rpack.getPData()) return nullptr;
try {
switch (rpack.getStatus())
{
case PID_Puncher_AssID: return uptr(new C4NetpuncherPacketAssID(rpack));
case PID_Puncher_SReq: return uptr(new C4NetpuncherPacketSReq(rpack));
case PID_Puncher_CReq: return uptr(new C4NetpuncherPacketCReq(rpack));
default: return nullptr;
}
}
catch (StdCompiler::Exception *e) { delete e; return nullptr; }
catch (...) { return nullptr; }
}
C4NetIOPacket C4NetpuncherPacket::PackTo(const C4NetIO::addr_t& addr) const {
C4NetIOPacket pkt;
pkt.SetAddr(addr);
StdBuf content(PackInto());
char type = GetType();
pkt.New(sizeof(type) + content.getSize());
pkt.Write(&type, sizeof(type));
pkt.Write(content, /*offset*/sizeof(type));
return pkt;
}
C4NetpuncherPacketCReq::C4NetpuncherPacketCReq(const C4NetIOPacket& rpack) {
C4Network2Address parse_addr;
CompileFromBuf<StdCompilerBinRead>(parse_addr, rpack.getPBuf());
if (parse_addr.getProtocol() != P_UDP) throw P_UDP;
addr.sin_addr.s_addr = parse_addr.getAddr().sin_addr.s_addr,
addr.sin_family = parse_addr.getAddr().sin_family,
addr.sin_port = parse_addr.getAddr().sin_port;
}
StdBuf C4NetpuncherPacketCReq::PackInto() const {
C4Network2Address ser_addr;
ser_addr.SetAddr(addr);
ser_addr.SetProtocol(P_UDP);
return DecompileToBuf<StdCompilerBinWrite>(ser_addr);
}
template<C4NetpuncherPacketType TYPE>
C4NetpuncherPacketID<TYPE>::C4NetpuncherPacketID(const C4NetIOPacket& rpack) {
std::istringstream iss(std::string(rpack.getPData(), rpack.getPSize()));
iss >> id;
}
template<C4NetpuncherPacketType TYPE>
StdBuf C4NetpuncherPacketID<TYPE>::PackInto() const {
std::ostringstream oss;
oss << GetID();
std::string s = oss.str();
return StdCopyBuf(s.c_str(), s.length());
}

View File

@ -0,0 +1,77 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
#ifndef C4PuncherPacket_H
#define C4PuncherPacket_H
/* I'm cooking up a seperate solution for the netpuncher because using C4Packet2.cpp would require introducing half a ton of stubs, and it seems like overkill */
#include "network/C4NetIO.h"
enum C4NetpuncherPacketType {
PID_Puncher_AssID = 0x51, // Puncher announcing ID to client
PID_Puncher_SReq = 0x52, // Client requesting to be served with punching (for an ID)
PID_Puncher_CReq = 0x53, // Puncher requesting clients to punch (towards an address)
// extend this with exchanging ICE parameters, some day?
};
typedef uint32_t C4NetpuncherID_t;
class C4NetpuncherPacket {
public:
typedef std::unique_ptr<C4NetpuncherPacket> uptr;
static std::unique_ptr<C4NetpuncherPacket> Construct(const C4NetIOPacket& rpack);
virtual ~C4NetpuncherPacket() {};
virtual C4NetpuncherPacketType GetType() const = 0;
C4NetIOPacket PackTo(const C4NetIO::addr_t&) const;
protected:
virtual StdBuf PackInto() const = 0;
typedef C4NetpuncherID_t CID;
};
template<C4NetpuncherPacketType TYPE>
class C4NetpuncherPacketID : public C4NetpuncherPacket {
private:
CID id;
StdBuf PackInto() const override;
protected:
C4NetpuncherPacketID(const C4NetIOPacket& rpack);
C4NetpuncherPacketID(CID id) : id(id) {};
public:
C4NetpuncherPacketType GetType() const final { return TYPE; }
CID GetID() const { return id; }
virtual ~C4NetpuncherPacketID() {};
};
#define PIDC(n) \
class C4NetpuncherPacket##n : public C4NetpuncherPacketID<PID_Puncher_##n> { \
public: explicit C4NetpuncherPacket##n(const C4NetIOPacket& p) : C4NetpuncherPacketID<PID_Puncher_##n>(p) {}; \
public: explicit C4NetpuncherPacket##n(CID id) : C4NetpuncherPacketID<PID_Puncher_##n>(id) {}; \
}
PIDC(AssID); PIDC(SReq);
#undef PIDC
class C4NetpuncherPacketCReq : public C4NetpuncherPacket {
private:
C4NetIO::addr_t addr;
StdBuf PackInto() const override;
public:
C4NetpuncherPacketType GetType() const final { return PID_Puncher_CReq; }
explicit C4NetpuncherPacketCReq(const C4NetIOPacket& rpack);
explicit C4NetpuncherPacketCReq(const C4NetIO::addr_t& addr) : addr(addr) {};
const C4NetIO::addr_t& GetAddr() { return addr; }
};
#endif

View File

@ -2,7 +2,7 @@
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2010-2013, The OpenClonk Team and contributors
* Copyright (c) 2010-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
@ -15,25 +15,61 @@
*/
#include "C4Include.h"
#include "netpuncher/C4PuncherHash.h"
#include "network/C4Network2.h"
#include "netpuncher/C4PuncherPacket.h"
#include <stdio.h>
#include <unordered_map>
#include <functional>
#include <random>
#include <stdexcept>
class C4PuncherServer : public C4NetIOUDP, private C4NetIO::CBClass
{
public:
C4PuncherServer() { C4NetIOUDP::SetCallback(this); }
typedef C4NetpuncherID_t CID;
C4PuncherServer() {
C4NetIOUDP::SetCallback(this);
rng = std::bind(std::uniform_int_distribution<CID>(1/*, max*/), std::ref(random_device));
}
private:
std::random_device random_device;
std::function<CID()> rng;
std::unordered_map<addr_t, CID> peer_ids;
std::unordered_map<CID, addr_t> peer_addrs;
// Event handlers
virtual bool OnConn(const addr_t &AddrPeer, const addr_t &AddrConnect, const addr_t *OwnAddr, C4NetIO *pNetIO)
{
printf("Punched back at %s:%d...\n", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port));
virtual bool OnConn(const addr_t &AddrPeer, const addr_t &AddrConnect, const addr_t *OwnAddr, C4NetIO *pNetIO) {
CID nid;
do {
nid = rng();
} while(peer_addrs.count(nid) && !nid);
peer_ids.emplace(AddrPeer, nid);
peer_addrs.emplace(nid, AddrPeer);
Send(C4NetpuncherPacketAssID(nid).PackTo(AddrPeer));
printf("Punched %s:%d... #%u\n", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port), nid);
return true;
}
virtual void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
{
// Unused
virtual void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO) {
auto& addr = rPacket.getAddr();
auto unpack = C4NetpuncherPacket::Construct(rPacket);
if (!unpack || unpack->GetType() != PID_Puncher_SReq) { Close(addr); return; }
auto other_it = peer_addrs.find(dynamic_cast<C4NetpuncherPacketSReq*>(unpack.get())->GetID());
if (other_it == peer_addrs.end()) return; // Might be nice to return some kind of error, for purposes of debugging.
Send(C4NetpuncherPacketCReq(other_it->second).PackTo(addr));
Send(C4NetpuncherPacketCReq(addr).PackTo(other_it->second));
}
virtual void OnDisconn(const addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason) {
auto it = peer_ids.find(AddrPeer);
if (it == peer_ids.end()) {
printf("ERROR: closing connection for %s:%d: (%s) but no connection is known\n", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port), szReason);
return;
}
peer_addrs.erase(it->second);
peer_ids.erase(it);
printf("Stopped punching %s:%d: %s...\n", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port), szReason);
};
} Puncher;
int main(int argc, char * argv[])

View File

@ -451,6 +451,7 @@ public:
virtual bool Close(const addr_t &addr);
virtual bool Send(const C4NetIOPacket &rPacket);
bool SendDirect(C4NetIOPacket &&rPacket); // (mt-safe)
virtual bool Broadcast(const C4NetIOPacket &rPacket);
virtual bool SetBroadcast(const addr_t &addr, bool fSet = true);
@ -739,7 +740,6 @@ protected:
// sending
bool BroadcastDirect(const Packet &rPacket, unsigned int iNr = ~0u); // (mt-safe)
bool SendDirect(C4NetIOPacket &&rPacket); // (mt-safe)
// multicast related
bool DoLoopbackTest();

View File

@ -147,7 +147,8 @@ C4Network2::C4Network2()
pVoteDialog(NULL),
fPausedForVote(false),
iLastOwnVoting(0),
fStreaming(false)
fStreaming(false),
NetpuncherGameID(0)
{
}
@ -168,6 +169,8 @@ bool C4Network2::InitHost(bool fLobby)
fChasing = false;
fAllowJoin = false;
iNextClientID = C4ClientIDStart;
NetpuncherGameID = 0;
NetpuncherAddr = ::Config.Network.PuncherAddress;
// initialize client list
Clients.Init(&Game.Clients, true);
// initialize resource list
@ -228,6 +231,8 @@ C4Network2::InitResult C4Network2::InitClient(const C4Network2Reference &Ref, bo
}
// repeat if wrong password
fWrongPassword = Ref.isPasswordNeeded();
NetpuncherGameID = Ref.getNetpuncherGameID();
NetpuncherAddr = Ref.getNetpuncherAddr();
StdStrBuf Password;
for (;;)
{
@ -295,6 +300,8 @@ C4Network2::InitResult C4Network2::InitClient(const class C4Network2Address *pAd
pControl = &::Control.Network;
// set exclusive connection mode
NetIO.SetExclusiveConnMode(true);
// warm up netpuncher
InitPuncher();
// try to connect host
StdStrBuf strAddresses; int iSuccesses = 0;
for (int i = 0; i < iAddrCount; i++)
@ -663,6 +670,7 @@ void C4Network2::Clear()
delete pVoteDialog; pVoteDialog = NULL;
fPausedForVote = false;
iLastOwnVoting = 0;
NetpuncherGameID = 0;
Votes.Clear();
// don't clear fPasswordNeeded here, it's needed by InitClient
}
@ -880,6 +888,49 @@ void C4Network2::HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C
if (pLobby) pLobby->HandlePacket(cStatus, pBasePkt, pClient);
}
bool C4Network2::HandlePuncherPacket(C4NetpuncherPacket::uptr pkt)
{
// TODO: is this all thread-safe?
assert(pkt);
#define GETPKT(c) dynamic_cast<C4NetpuncherPacket##c*>(pkt.get())
switch (pkt->GetType())
{
case PID_Puncher_CReq:
if (isHost())
{
NetIO.Punch(GETPKT(CReq)->GetAddr());
return true;
}
else
{
// The IP/Port should be already in the masterserver list, so just keep trying.
return Status.getState() == GS_Init;
}
case PID_Puncher_AssID:
if (isHost())
{
NetpuncherGameID = GETPKT(AssID)->GetID();
InvalidateReference();
}
else
{
// While we don't need the ID as a client, this nicely serves as the signal that we can start using the netpuncher
if (Status.getState() == GS_Init && getNetpuncherGameID())
NetIO.SendPuncherPacket(C4NetpuncherPacketSReq(getNetpuncherGameID()));
}
return true;
default: return false;
}
}
void C4Network2::InitPuncher()
{
// We have an internet connection, so let's punch the puncher server here in order to open an udp port
C4NetIO::addr_t PuncherAddr;
if (ResolveAddress(getNetpuncherAddr().getData(), &PuncherAddr, C4NetStdPortPuncher))
NetIO.InitPuncher(PuncherAddr);
}
void C4Network2::OnGameSynchronized()
{
// savegame needed?
@ -1978,10 +2029,7 @@ bool C4Network2::LeagueStart(bool *pCancel)
return false;
}
// We have an internet connection, so let's punch the master server here in order to open an udp port
C4NetIO::addr_t PuncherAddr;
if (ResolveAddress(Config.Network.PuncherAddress, &PuncherAddr, C4NetStdPortPuncher))
NetIO.Punch(PuncherAddr);
InitPuncher();
// Let's wait for response
StdStrBuf Message = FormatString(LoadResStr("IDS_NET_LEAGUE_REGGAME"), pLeagueClient->getServerName());

View File

@ -193,6 +193,10 @@ protected:
class C4Network2HTTPClient *pStreamer;
unsigned int iCurrentStreamAmount, iCurrentStreamPosition;
// puncher
C4NetpuncherID_t NetpuncherGameID;
StdCopyStrBuf NetpuncherAddr;
public:
// data access
@ -253,6 +257,7 @@ public:
void OnDisconn(C4Network2IOConnection *pConn);
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn);
void HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn);
bool HandlePuncherPacket(C4NetpuncherPacket::uptr);
// runtime join stuff
void OnGameSynchronized();
@ -297,6 +302,10 @@ public:
bool FinishStreaming();
bool StopStreaming();
// netpuncher
C4NetpuncherID_t getNetpuncherGameID() const { return NetpuncherGameID; }
StdStrBuf getNetpuncherAddr() const { return NetpuncherAddr; }
protected:
using C4ApplicationSec1Timer::Execute; // to avoid "virtual ... is hidden" warning
@ -350,6 +359,9 @@ protected:
// streaming
bool StreamIn(bool fFinish);
bool StreamOut();
// puncher
void InitPuncher();
};
extern C4Network2 Network;

View File

@ -0,0 +1,74 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2010-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
#include "C4Include.h"
#ifndef _WIN32
#include <arpa/inet.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <net/if.h>
#endif
#include "network/C4Network2Address.h"
// *** C4Network2Address
void C4Network2Address::CompileFunc(StdCompiler *pComp)
{
// Clear
if (pComp->isCompiler())
{
ZeroMem(&addr, sizeof(addr));
addr.sin_family = AF_INET;
}
// Write protocol
StdEnumEntry<C4Network2IOProtocol> Protocols[] =
{
{ "UDP", P_UDP },
{ "TCP", P_TCP },
{ NULL, P_NONE },
};
pComp->Value(mkEnumAdaptT<uint8_t>(eProtocol, Protocols));
pComp->Separator(StdCompiler::SEP_PART2); // ':'
// Write IP (no IP = 0.0.0.0)
in_addr zero; zero.s_addr = INADDR_ANY;
pComp->Value(mkDefaultAdapt(addr.sin_addr, zero));
pComp->Separator(StdCompiler::SEP_PART2); // ':'
// Write port
uint16_t iPort = htons(addr.sin_port);
pComp->Value(iPort);
addr.sin_port = htons(iPort);
}
StdStrBuf C4Network2Address::toString() const
{
switch (eProtocol)
{
case P_UDP: return FormatString("UDP:%s:%d", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
case P_TCP: return FormatString("TCP:%s:%d", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
default: return StdStrBuf("INVALID");
}
}
bool C4Network2Address::operator == (const C4Network2Address &addr2) const
{
return eProtocol == addr2.getProtocol() && AddrEqual(addr, addr2.getAddr());
}

View File

@ -0,0 +1,63 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2013-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
#ifndef INC_C4Network2Address
#define INC_C4Network2Address
#include "network/C4Network2IO.h"
class C4Network2Address
{
public:
C4Network2Address()
: eProtocol(P_NONE)
{ ZeroMem(&addr, sizeof(addr)); }
C4Network2Address(C4NetIO::addr_t addr, C4Network2IOProtocol eProtocol)
: addr(addr), eProtocol(eProtocol)
{ }
C4Network2Address(const C4Network2Address &addr)
: addr(addr.getAddr()), eProtocol(addr.getProtocol())
{ }
void operator = (const C4Network2Address &addr)
{ SetAddr(addr.getAddr()); SetProtocol(addr.getProtocol()); }
bool operator == (const C4Network2Address &addr) const;
protected:
C4NetIO::addr_t addr;
C4Network2IOProtocol eProtocol;
public:
const C4NetIO::addr_t &getAddr() const { return addr; }
in_addr getIPAddr() const { return addr.sin_addr; }
bool isIPNull() const { return !addr.sin_addr.s_addr; }
uint16_t getPort() const { return htons(addr.sin_port); }
C4Network2IOProtocol getProtocol() const { return eProtocol; }
StdStrBuf toString() const;
void SetAddr(C4NetIO::addr_t naddr) { addr = naddr; }
void SetIP(in_addr ip) { addr.sin_addr = ip; }
void SetPort(uint16_t iPort) { addr.sin_port = htons(iPort); }
void SetProtocol(C4Network2IOProtocol enProtocol) { eProtocol = enProtocol; }
void CompileFunc(StdCompiler *pComp);
};
#endif

View File

@ -32,55 +32,6 @@
#include <net/if.h>
#endif
// *** C4Network2Address
void C4Network2Address::CompileFunc(StdCompiler *pComp)
{
// Clear
if (pComp->isCompiler())
{
ZeroMem(&addr, sizeof(addr));
addr.sin_family = AF_INET;
}
// Write protocol
StdEnumEntry<C4Network2IOProtocol> Protocols[] =
{
{ "UDP", P_UDP },
{ "TCP", P_TCP },
{ NULL, P_NONE },
};
pComp->Value(mkEnumAdaptT<uint8_t>(eProtocol, Protocols));
pComp->Separator(StdCompiler::SEP_PART2); // ':'
// Write IP (no IP = 0.0.0.0)
in_addr zero; zero.s_addr = INADDR_ANY;
pComp->Value(mkDefaultAdapt(addr.sin_addr, zero));
pComp->Separator(StdCompiler::SEP_PART2); // ':'
// Write port
uint16_t iPort = htons(addr.sin_port);
pComp->Value(iPort);
addr.sin_port = htons(iPort);
}
StdStrBuf C4Network2Address::toString() const
{
switch (eProtocol)
{
case P_UDP: return FormatString("UDP:%s:%d", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
case P_TCP: return FormatString("TCP:%s:%d", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
default: return StdStrBuf("INVALID");
}
}
bool C4Network2Address::operator == (const C4Network2Address &addr2) const
{
return eProtocol == addr2.getProtocol() && AddrEqual(addr, addr2.getAddr());
}
// *** C4Network2Client
C4Network2Client::C4Network2Client(C4Client *pClient)
@ -201,6 +152,7 @@ bool C4Network2Client::DoConnectAttempt(C4Network2IO *pIO)
bool C4Network2Client::hasAddr(const C4Network2Address &addr) const
{
// Note that the host only knows its own address as 0.0.0.0, so if the real address is being added, that can't be sorted out.
for (int32_t i = 0; i < iAddrCnt; i++)
if (Addr[i] == addr)
return true;

View File

@ -20,6 +20,7 @@
#include "network/C4Network2IO.h"
#include "network/C4PacketBase.h"
#include "network/C4Client.h"
#include "network/C4Network2Address.h"
class C4Network2; class C4Network2IOConnection;
@ -40,47 +41,6 @@ enum C4Network2ClientStatus
NCS_Remove // client is to be removed
};
class C4Network2Address
{
public:
C4Network2Address()
: eProtocol(P_NONE)
{ ZeroMem(&addr, sizeof(addr)); }
C4Network2Address(C4NetIO::addr_t addr, C4Network2IOProtocol eProtocol)
: addr(addr), eProtocol(eProtocol)
{ }
C4Network2Address(const C4Network2Address &addr)
: addr(addr.getAddr()), eProtocol(addr.getProtocol())
{ }
void operator = (const C4Network2Address &addr)
{ SetAddr(addr.getAddr()); SetProtocol(addr.getProtocol()); }
bool operator == (const C4Network2Address &addr) const;
protected:
C4NetIO::addr_t addr;
C4Network2IOProtocol eProtocol;
public:
const C4NetIO::addr_t &getAddr() const { return addr; }
in_addr getIPAddr() const { return addr.sin_addr; }
bool isIPNull() const { return !addr.sin_addr.s_addr; }
uint16_t getPort() const { return htons(addr.sin_port); }
C4Network2IOProtocol getProtocol() const { return eProtocol; }
StdStrBuf toString() const;
void SetAddr(C4NetIO::addr_t naddr) { addr = naddr; }
void SetIP(in_addr ip) { addr.sin_addr = ip; }
void SetPort(uint16_t iPort) { addr.sin_port = htons(iPort); }
void SetProtocol(C4Network2IOProtocol enProtocol) { eProtocol = enProtocol; }
void CompileFunc(StdCompiler *pComp);
};
class C4Network2Client
{
friend class C4Network2ClientList;
@ -244,4 +204,3 @@ public:
};
#endif

View File

@ -39,7 +39,7 @@ struct C4Network2IO::NetEvPacketData
};
// compile options
#define C4NET2IO_DUMP_LEVEL 1
#define C4NET2IO_DUMP_LEVEL 3
// *** C4Network2IO
@ -455,7 +455,7 @@ bool C4Network2IO::BroadcastMsg(const C4NetIOPacket &rPkt) // by both
return fSuccess;
}
bool C4Network2IO::Punch(C4NetIO::addr_t nPuncherAddr)
bool C4Network2IO::InitPuncher(C4NetIO::addr_t nPuncherAddr)
{
// UDP must be initialized
if (!pNetIO_UDP)
@ -466,19 +466,30 @@ bool C4Network2IO::Punch(C4NetIO::addr_t nPuncherAddr)
return pNetIO_UDP->Connect(PuncherAddr);
}
void C4Network2IO::Punch(const C4NetIO::addr_t &punchee_addr) {
if (!pNetIO_UDP)
return;
C4PacketPing PktPeng;
dynamic_cast<C4NetIOUDP*>(pNetIO_UDP)->SendDirect(MkC4NetIOPacket(PID_Pong, PktPeng, punchee_addr));
}
void C4Network2IO::SendPuncherPacket(const C4NetpuncherPacket& p) {
if (!pNetIO_UDP || !PuncherAddr.sin_addr.s_addr) return;
pNetIO_UDP->Send(p.PackTo(PuncherAddr));
}
// C4NetIO interface
bool C4Network2IO::OnConn(const C4NetIO::addr_t &PeerAddr, const C4NetIO::addr_t &ConnectAddr, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO)
{
// puncher answer? We just make sure here a connection /can/ be established, so close it instantly.
if (pNetIO == pNetIO_UDP)
if (PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, ConnectAddr))
{
// got an address?
if (pOwnAddr)
OnPunch(*pOwnAddr);
// this is only a test connection - close it instantly
return false;
}
// puncher answer?
if (pNetIO == pNetIO_UDP && PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, ConnectAddr))
{
// got an address?
if (pOwnAddr)
OnPuncherConnect(*pOwnAddr);
return true;
}
#if(C4NET2IO_DUMP_LEVEL > 1)
Application.InteractiveThread.ThreadLogS("OnConn: %s %s",
C4TimeMilliseconds::Now().AsString().getData(),
@ -527,12 +538,11 @@ bool C4Network2IO::OnConn(const C4NetIO::addr_t &PeerAddr, const C4NetIO::addr_t
void C4Network2IO::OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason)
{
// punch?
if (pNetIO == pNetIO_UDP)
if (PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, addr))
{
ZeroMem(&PuncherAddr, sizeof(PuncherAddr));
return;
}
if (pNetIO == pNetIO_UDP && PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, addr))
{
ZeroMem(&PuncherAddr, sizeof(PuncherAddr));
return;
}
#if(C4NET2IO_DUMP_LEVEL > 1)
Application.InteractiveThread.ThreadLogS("OnDisconn: %s %s",
C4TimeMilliseconds::Now().AsString().getData(),
@ -570,6 +580,11 @@ void C4Network2IO::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
C4TimeMilliseconds::Now().AsString().getData(),
rPacket.getStatus(), getNetIOName(pNetIO));
#endif
if (pNetIO == pNetIO_UDP && PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, rPacket.getAddr()))
{
HandlePuncherPacket(rPacket);
return;
}
if (!rPacket.getSize()) return;
// find connection
C4Network2IOConnection *pConn = GetConnection(rPacket.getAddr(), pNetIO);
@ -1120,6 +1135,17 @@ void C4Network2IO::HandleFwdReq(const C4PacketFwd &rFwd, C4Network2IOConnection
}
}
void C4Network2IO::HandlePuncherPacket(const C4NetIOPacket& rPacket)
{
auto pkt = C4NetpuncherPacket::Construct(rPacket);
if (pkt && ::Network.HandlePuncherPacket(move(pkt)));
else
{
assert(pNetIO_UDP);
pNetIO_UDP->Close(rPacket.getAddr());
}
}
bool C4Network2IO::Ping()
{
bool fSuccess = true;
@ -1237,7 +1263,7 @@ void C4Network2IO::SendConnPackets()
}
void C4Network2IO::OnPunch(C4NetIO::addr_t addr)
void C4Network2IO::OnPuncherConnect(C4NetIO::addr_t addr)
{
// Sanity check
assert (addr.sin_family == AF_INET);
@ -1250,8 +1276,8 @@ void C4Network2IO::OnPunch(C4NetIO::addr_t addr)
// Add for local client
C4Network2Client *pLocal = ::Network.Clients.GetLocal();
if (pLocal)
if (pLocal->AddAddr(C4Network2Address(addr, P_UDP), true))
::Network.InvalidateReference();
pLocal->AddAddr(C4Network2Address(addr, P_UDP), true);
// Do not ::Network.InvalidateReference(); yet, we're expecting an ID from the netpuncher
}
// *** C4Network2IOConnection

View File

@ -19,6 +19,7 @@
#include "network/C4NetIO.h"
#include "network/C4Client.h"
#include "network/C4InteractiveThread.h"
#include "netpuncher/C4PuncherPacket.h"
class C4Network2IOConnection;
@ -135,7 +136,9 @@ public:
bool BroadcastMsg(const C4NetIOPacket &rPkt); // by both
// punch
bool Punch(C4NetIO::addr_t PuncherAddr); // by main thread
bool InitPuncher(C4NetIO::addr_t PuncherAddr); // by main thread
void SendPuncherPacket(const C4NetpuncherPacket&);
void Punch(const C4NetIO::addr_t&); // sends a ping packet
// stuff
C4NetIO *getNetIO(C4Network2IOProtocol eProt); // by both
@ -185,6 +188,7 @@ protected:
// packet handling (some are really handled here)
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn);
void HandleFwdReq(const class C4PacketFwd &rFwd, C4Network2IOConnection *pBy);
void HandlePuncherPacket(const C4NetIOPacket &rPacket);
// misc
bool Ping();
@ -193,8 +197,7 @@ protected:
void SendConnPackets();
// puncher
void OnPunch(C4NetIO::addr_t addr);
void OnPuncherConnect(C4NetIO::addr_t addr);
};
enum C4Network2IOConnStatus

View File

@ -30,7 +30,7 @@
C4Network2Reference::C4Network2Reference()
: Icon(0), GameMode(), Time(0), Frame(0), StartTime(0), LeaguePerformance(0),
JoinAllowed(true), ObservingAllowed(true), PasswordNeeded(false), OfficialServer(false),
IsEditor(false), iAddrCnt(0)
IsEditor(false), iAddrCnt(0), NetpuncherGameID(0)
{
}
@ -78,6 +78,8 @@ void C4Network2Reference::InitLocal()
ObservingAllowed = ::Network.isObservingAllowed();
PasswordNeeded = ::Network.isPassworded();
IsEditor = !!::Application.isEditor;
NetpuncherGameID = ::Network.getNetpuncherGameID();
NetpuncherAddr = ::Network.getNetpuncherAddr();
Game.Set();
// Addresses
@ -125,6 +127,8 @@ void C4Network2Reference::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt(Game.sEngineName, "Game", "None"));
pComp->Value(mkNamingAdapt(mkArrayAdaptDM(Game.iVer,0),"Version" ));
pComp->Value(mkNamingAdapt(OfficialServer, "OfficialServer", false));
pComp->Value(mkNamingAdapt(NetpuncherGameID, "NetpuncherID", 0, false, false));
pComp->Value(mkNamingAdapt(NetpuncherAddr, "NetpuncherAddr", "", false, false));
pComp->Value(Parameters);
}

View File

@ -51,6 +51,8 @@ private:
bool PasswordNeeded;
bool OfficialServer;
bool IsEditor;
C4NetpuncherID_t NetpuncherGameID;
StdCopyStrBuf NetpuncherAddr;
// Engine information
C4GameVersion Game;
@ -75,6 +77,8 @@ public:
int32_t getStartTime() const { return StartTime; }
StdStrBuf getGameGoalString() const;
bool isEditor() const { return IsEditor; }
C4NetpuncherID_t getNetpuncherGameID() const { return NetpuncherGameID; }
StdStrBuf getNetpuncherAddr() const { return NetpuncherAddr; }
void SetSourceIP(in_addr ip);

View File

@ -101,7 +101,6 @@ const C4PktHandlingData PktHandlingData[] =
{ PID_ControlPkt, PC_Network, "Control Paket", false, false, PH_C4GameControlNetwork, PKT_UNPACK(C4PacketControlPkt) },
{ PID_ExecSyncCtrl, PC_Network, "Execute Sync Control", false, false, PH_C4GameControlNetwork, PKT_UNPACK(C4PacketExecSyncCtrl)},
// Control (Isn't send over network, handled only as part of a control list)
{ CID_ClientJoin, PC_Control, "Client Join", false, true, 0, PKT_UNPACK(C4ControlClientJoin) },
{ CID_ClientUpdate, PC_Control, "Client Update", false, true, 0, PKT_UNPACK(C4ControlClientUpdate)},

View File

@ -169,6 +169,9 @@ enum C4PacketType
CID_DebugRec = CID_First | 0x40,
CID_MenuCommand = CID_First | 0x41,
// Note: There are some more packet types in src/netpuncher/C4PuncherPacket.h
// They have been picked to be distinct from these for safety, not for necessary.
};
// packet classes
@ -187,7 +190,7 @@ enum C4PacketHandlerID
PH_C4Network2ClientList = 1 << 3, // client list class
PH_C4Network2Players = 1 << 4, // player list class
PH_C4Network2ResList = 1 << 5, // resource list class
PH_C4GameControlNetwork = 1 << 6 // network control class
PH_C4GameControlNetwork = 1 << 6, // network control class
};