mirror of
https://git.do.srb2.org/KartKrew/Kart-Public.git
synced 2025-02-04 15:31:05 +00:00
Merge branch 'hole-punch-backport' into 'public_next'
Hole punching backport See merge request KartKrew/Kart!445
This commit is contained in:
commit
8fa22d562f
7 changed files with 234 additions and 24 deletions
34
doc/Holepunch-Protocol.txt
Normal file
34
doc/Holepunch-Protocol.txt
Normal file
|
@ -0,0 +1,34 @@
|
|||
Bird's Hole Punching Protocol
|
||||
|
||||
|
||||
Hole punch - a mechanism to bypass a firewall
|
||||
Server - a third party which is not behind a firewall
|
||||
Client - anything contacting the server
|
||||
Magic - the four bytes 00 52 EB 11
|
||||
Address - an IPv4 address
|
||||
|
||||
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
+-------+-------+----+
|
||||
| Magic |Address|Port|
|
||||
+-------+-------+----+
|
||||
Relay Packet
|
||||
|
||||
|
||||
A client that expects to be the target of a hole punch
|
||||
must contact the server frequently, to keep a UDP
|
||||
"connection" open, so that the server may relay hole
|
||||
punching requests to them.
|
||||
|
||||
A client makes a hole punching request to another client
|
||||
by sending a Relay Packet to the server. The server then
|
||||
sends another Relay Packet to the client described by the
|
||||
first packet. The second packet is filled with the source
|
||||
address and port of the first packet.
|
||||
|
||||
Once a client receives a Relay Packet, this protocol's
|
||||
purpose is fulfilled and the client is aware that another
|
||||
client requests a hole punch.
|
||||
|
||||
|
||||
vim: noai
|
|
@ -1916,6 +1916,12 @@ static void SendAskInfo(INT32 node)
|
|||
// now allowed traffic from the host to us in, so once the MS relays
|
||||
// our address to the host, it'll be able to speak to us.
|
||||
HSendPacket(node, false, 0, sizeof (askinfo_pak));
|
||||
|
||||
if (node != 0 && node != BROADCASTADDR &&
|
||||
cv_rendezvousserver.string[0])
|
||||
{
|
||||
I_NetRequestHolePunch();
|
||||
}
|
||||
}
|
||||
|
||||
serverelem_t serverlist[MAXSERVERLIST];
|
||||
|
@ -5754,6 +5760,22 @@ static void UpdatePingTable(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void RenewHolePunch(void)
|
||||
{
|
||||
if (cv_rendezvousserver.string[0])
|
||||
{
|
||||
static time_t past;
|
||||
|
||||
const time_t now = time(NULL);
|
||||
|
||||
if ((now - past) > 20)
|
||||
{
|
||||
I_NetRegisterHolePunch();
|
||||
past = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle timeouts to prevent definitive freezes from happenning
|
||||
static void HandleNodeTimeouts(void)
|
||||
{
|
||||
|
@ -5788,6 +5810,11 @@ FILESTAMP
|
|||
MasterClient_Ticker();
|
||||
#endif
|
||||
|
||||
if (netgame && serverrunning)
|
||||
{
|
||||
RenewHolePunch();
|
||||
}
|
||||
|
||||
if (client)
|
||||
{
|
||||
// send keep alive
|
||||
|
@ -5847,6 +5874,11 @@ FILESTAMP
|
|||
MasterClient_Ticker(); // Acking the Master Server
|
||||
#endif
|
||||
|
||||
if (netgame && serverrunning)
|
||||
{
|
||||
RenewHolePunch();
|
||||
}
|
||||
|
||||
if (client)
|
||||
{
|
||||
if (!resynch_local_inprogress)
|
||||
|
|
|
@ -49,6 +49,8 @@ tic_t connectiontimeout = (10*TICRATE);
|
|||
doomcom_t *doomcom = NULL;
|
||||
/// \brief network packet data, points inside doomcom
|
||||
doomdata_t *netbuffer = NULL;
|
||||
/// \brief hole punching packet, also points inside doomcom
|
||||
holepunch_t *holepunchpacket = NULL;
|
||||
|
||||
#ifdef DEBUGFILE
|
||||
FILE *debugfile = NULL; // put some net info in a file during the game
|
||||
|
@ -72,6 +74,8 @@ boolean (*I_NetCanGet)(void) = NULL;
|
|||
void (*I_NetCloseSocket)(void) = NULL;
|
||||
void (*I_NetFreeNodenum)(INT32 nodenum) = NULL;
|
||||
SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL;
|
||||
void (*I_NetRequestHolePunch)(void) = NULL;
|
||||
void (*I_NetRegisterHolePunch)(void) = NULL;
|
||||
boolean (*I_NetOpenSocket)(void) = NULL;
|
||||
boolean (*I_Ban) (INT32 node) = NULL;
|
||||
void (*I_ClearBans)(void) = NULL;
|
||||
|
@ -1335,6 +1339,7 @@ boolean D_CheckNetGame(void)
|
|||
I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES);
|
||||
|
||||
netbuffer = (doomdata_t *)(void *)&doomcom->data;
|
||||
holepunchpacket = (holepunch_t *)(void *)&doomcom->data;
|
||||
|
||||
#ifdef DEBUGFILE
|
||||
#ifdef _arch_dreamcast
|
||||
|
|
17
src/i_net.h
17
src/i_net.h
|
@ -77,11 +77,19 @@ typedef struct
|
|||
char data[MAXPACKETLENGTH];
|
||||
} ATTRPACK doomcom_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
INT32 magic;
|
||||
INT32 addr;
|
||||
INT16 port;
|
||||
} ATTRPACK holepunch_t;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
extern doomcom_t *doomcom;
|
||||
extern holepunch_t *holepunchpacket;
|
||||
|
||||
/** \brief return packet in doomcom struct
|
||||
*/
|
||||
|
@ -140,6 +148,15 @@ extern boolean (*I_NetOpenSocket)(void);
|
|||
extern void (*I_NetCloseSocket)(void);
|
||||
|
||||
|
||||
/** \brief send a hole punching request
|
||||
*/
|
||||
extern void (*I_NetRequestHolePunch)(void);
|
||||
|
||||
/** \brief register this machine on the hole punching server
|
||||
*/
|
||||
extern void (*I_NetRegisterHolePunch)(void);
|
||||
|
||||
|
||||
extern boolean (*I_Ban) (INT32 node);
|
||||
extern void (*I_ClearBans)(void);
|
||||
extern const char *(*I_GetNodeAddress) (INT32 node);
|
||||
|
|
167
src/i_tcp.c
167
src/i_tcp.c
|
@ -241,6 +241,8 @@ static size_t broadcastaddresses = 0;
|
|||
static boolean nodeconnected[MAXNETNODES+1];
|
||||
static mysockaddr_t banned[MAXBANS];
|
||||
static UINT8 bannedmask[MAXBANS];
|
||||
/* See ../doc/Holepunch-Protocol.txt */
|
||||
static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11);
|
||||
#endif
|
||||
|
||||
static size_t numbans = 0;
|
||||
|
@ -597,6 +599,55 @@ void Command_Numnodes(void)
|
|||
#endif
|
||||
|
||||
#ifndef NONET
|
||||
/* not one of the reserved "local" addresses */
|
||||
static boolean
|
||||
is_external_address (UINT32 p)
|
||||
{
|
||||
UINT8 a = (p & 255);
|
||||
UINT8 b = ((p >> 8) & 255);
|
||||
|
||||
if (p == (UINT32)~0)/* 255.255.255.255 */
|
||||
return 0;
|
||||
|
||||
switch (a)
|
||||
{
|
||||
case 0:
|
||||
case 10:
|
||||
case 127:
|
||||
return false;
|
||||
case 172:
|
||||
return (b & ~15) != 16;/* 16 - 31 */
|
||||
case 192:
|
||||
return b != 168;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean hole_punch(ssize_t c)
|
||||
{
|
||||
/* See ../doc/Holepunch-Protocol.txt */
|
||||
if (cv_rendezvousserver.string[0] &&
|
||||
c == 10 && holepunchpacket->magic == hole_punch_magic &&
|
||||
is_external_address(ntohl(holepunchpacket->addr)))
|
||||
{
|
||||
mysockaddr_t addr;
|
||||
addr.ip4.sin_family = AF_INET;
|
||||
addr.ip4.sin_addr.s_addr = holepunchpacket->addr;
|
||||
addr.ip4.sin_port = holepunchpacket->port;
|
||||
sendto(mysockets[0], NULL, 0, 0, &addr.any, sizeof addr.ip4);
|
||||
|
||||
CONS_Debug(DBG_NETPLAY,
|
||||
"hole punching request from %s\n", SOCK_AddrToStr(&addr));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if a packet was received from a new node, false in all other cases
|
||||
static boolean SOCK_Get(void)
|
||||
{
|
||||
|
@ -611,7 +662,7 @@ static boolean SOCK_Get(void)
|
|||
fromlen = (socklen_t)sizeof(fromaddress);
|
||||
c = recvfrom(mysockets[n], (char *)&doomcom->data, MAXPACKETLENGTH, 0,
|
||||
(void *)&fromaddress, &fromlen);
|
||||
if (c != ERRSOCKET)
|
||||
if (c > 0)
|
||||
{
|
||||
#ifdef USE_STUN
|
||||
if (STUN_got_response(doomcom->data, c))
|
||||
|
@ -620,6 +671,11 @@ static boolean SOCK_Get(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (hole_punch(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// find remote node number
|
||||
for (j = 1; j <= MAXNETNODES; j++) //include LAN
|
||||
{
|
||||
|
@ -1319,17 +1375,14 @@ void I_ShutdownTcpDriver(void)
|
|||
}
|
||||
|
||||
#ifndef NONET
|
||||
static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
|
||||
static boolean SOCK_GetAddr(struct sockaddr_in *sin, const char *address, const char *port, boolean test)
|
||||
{
|
||||
SINT8 newnode = -1;
|
||||
struct my_addrinfo *ai = NULL, *runp, hints;
|
||||
int gaie;
|
||||
|
||||
if (!port || !port[0])
|
||||
if (!port || !port[0])
|
||||
port = DEFAULTPORT;
|
||||
|
||||
DEBFILE(va("Creating new node: %s@%s\n", address, port));
|
||||
|
||||
memset (&hints, 0x00, sizeof (hints));
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
@ -1337,30 +1390,93 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
|
|||
hints.ai_protocol = IPPROTO_UDP;
|
||||
|
||||
gaie = I_getaddrinfo(address, port, &hints, &ai);
|
||||
if (gaie == 0)
|
||||
{
|
||||
newnode = getfreenode();
|
||||
}
|
||||
if (newnode == -1)
|
||||
|
||||
if (gaie != 0)
|
||||
{
|
||||
I_freeaddrinfo(ai);
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
runp = ai;
|
||||
|
||||
if (test)
|
||||
{
|
||||
while (runp != NULL)
|
||||
{
|
||||
if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0)
|
||||
break;
|
||||
|
||||
runp = runp->ai_next;
|
||||
}
|
||||
}
|
||||
|
||||
if (runp != NULL)
|
||||
memcpy(sin, runp->ai_addr, runp->ai_addrlen);
|
||||
|
||||
I_freeaddrinfo(ai);
|
||||
|
||||
return (runp != NULL);
|
||||
}
|
||||
|
||||
static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
|
||||
{
|
||||
SINT8 newnode = getfreenode();
|
||||
|
||||
DEBFILE(va("Creating new node: %s@%s\n", address, port));
|
||||
|
||||
if (newnode != -1)
|
||||
{
|
||||
if (!SOCK_GetAddr(&clientaddress[newnode].ip4, address, port, true))
|
||||
{
|
||||
nodeconnected[newnode] = false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/* See ../doc/Holepunch-Protocol.txt */
|
||||
|
||||
static void rendezvous(int size)
|
||||
{
|
||||
char *addrs = strdup(cv_rendezvousserver.string);
|
||||
|
||||
char *host = strtok(addrs, ":");
|
||||
char *port = strtok(NULL, ":");
|
||||
|
||||
mysockaddr_t rzv;
|
||||
|
||||
if (SOCK_GetAddr(&rzv.ip4, host, (port ? port : "7777"), false))
|
||||
{
|
||||
holepunchpacket->magic = hole_punch_magic;
|
||||
sendto(mysockets[0], doomcom->data, size, 0, &rzv.any, sizeof rzv.ip4);
|
||||
}
|
||||
else
|
||||
runp = ai;
|
||||
|
||||
while (runp != NULL)
|
||||
{
|
||||
// find ip of the server
|
||||
if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0)
|
||||
{
|
||||
memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
|
||||
break;
|
||||
}
|
||||
runp = runp->ai_next;
|
||||
CONS_Alert(CONS_ERROR, "Failed to contact rendezvous server (%s).\n",
|
||||
cv_rendezvousserver.string);
|
||||
}
|
||||
I_freeaddrinfo(ai);
|
||||
return newnode;
|
||||
|
||||
free(addrs);
|
||||
}
|
||||
|
||||
static void SOCK_RequestHolePunch(void)
|
||||
{
|
||||
mysockaddr_t * addr = &clientaddress[doomcom->remotenode];
|
||||
|
||||
holepunchpacket->addr = addr->ip4.sin_addr.s_addr;
|
||||
holepunchpacket->port = addr->ip4.sin_port;
|
||||
|
||||
CONS_Debug(DBG_NETPLAY,
|
||||
"requesting hole punch to node %s\n", SOCK_AddrToStr(addr));
|
||||
|
||||
rendezvous(10);
|
||||
}
|
||||
|
||||
static void SOCK_RegisterHolePunch(void)
|
||||
{
|
||||
rendezvous(4);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1387,6 +1503,9 @@ static boolean SOCK_OpenSocket(void)
|
|||
I_NetCanGet = SOCK_CanGet;
|
||||
#endif
|
||||
|
||||
I_NetRequestHolePunch = SOCK_RequestHolePunch;
|
||||
I_NetRegisterHolePunch = SOCK_RegisterHolePunch;
|
||||
|
||||
// build the socket but close it first
|
||||
SOCK_CloseSocket();
|
||||
return UDP_Socket();
|
||||
|
|
|
@ -68,6 +68,7 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = {
|
|||
};
|
||||
|
||||
consvar_t cv_masterserver = {"masterserver", "https://ms.kartkrew.org/ms/api", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_rendezvousserver = {"rendezvousserver", "relay.kartkrew.org", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_server_contact = {"server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
|
@ -99,6 +100,7 @@ void AddMServCommands(void)
|
|||
CV_RegisterVar(&cv_masterserver_debug);
|
||||
CV_RegisterVar(&cv_masterserver_token);
|
||||
CV_RegisterVar(&cv_advertise);
|
||||
CV_RegisterVar(&cv_rendezvousserver);
|
||||
CV_RegisterVar(&cv_servername);
|
||||
CV_RegisterVar(&cv_server_contact);
|
||||
#ifdef MASTERSERVER
|
||||
|
|
|
@ -58,6 +58,7 @@ extern consvar_t cv_masterserver_update_rate;
|
|||
extern consvar_t cv_masterserver_timeout;
|
||||
extern consvar_t cv_masterserver_debug;
|
||||
extern consvar_t cv_masterserver_token;
|
||||
extern consvar_t cv_rendezvousserver;
|
||||
|
||||
extern consvar_t cv_advertise;
|
||||
|
||||
|
|
Loading…
Reference in a new issue