From c8d4ef64f986bea422717695b9cbd0086bf77c3d Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 12 Nov 2020 17:07:19 -0800 Subject: [PATCH 1/3] Hole punching --- src/d_clisrv.c | 25 +++++++++ src/d_net.c | 5 ++ src/i_net.h | 17 +++++++ src/i_tcp.c | 134 ++++++++++++++++++++++++++++++++++++++++--------- src/mserv.c | 2 + src/mserv.h | 1 + 6 files changed, 161 insertions(+), 23 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a3f7b2522..16b850a4b 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1990,6 +1990,8 @@ 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)); + + I_NetRequestHolePunch(); } serverelem_t serverlist[MAXSERVERLIST]; @@ -6092,6 +6094,19 @@ static void UpdatePingTable(void) } } +static void RenewHolePunch(void) +{ + 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) { @@ -6130,6 +6145,11 @@ void NetKeepAlive(void) MasterClient_Ticker(); #endif + if (serverrunning) + { + RenewHolePunch(); + } + if (client) { // send keep alive @@ -6189,6 +6209,11 @@ void NetUpdate(void) MasterClient_Ticker(); // Acking the Master Server #endif + if (serverrunning) + { + RenewHolePunch(); + } + if (client) { if (!resynch_local_inprogress) diff --git a/src/d_net.c b/src/d_net.c index d4e1f4d03..8af1889bc 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -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; @@ -1347,6 +1351,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 if (M_CheckParm("-debugfile")) diff --git a/src/i_net.h b/src/i_net.h index 5d93f191e..8caa0edcc 100644 --- a/src/i_net.h +++ b/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); diff --git a/src/i_tcp.c b/src/i_tcp.c index a9ffe5f3b..e99672d14 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -204,6 +204,7 @@ static size_t broadcastaddresses = 0; static boolean nodeconnected[MAXNETNODES+1]; static mysockaddr_t banned[MAXBANS]; static UINT8 bannedmask[MAXBANS]; +static const INT32 hole_punch_magic = MSBF_LONG (0x52eb11); #endif static size_t numbans = 0; @@ -560,6 +561,27 @@ void Command_Numnodes(void) #endif #ifndef NONET +static boolean hole_punch(ssize_t c) +{ + if (c == 10 && holepunchpacket->magic == hole_punch_magic) + { + 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) { @@ -583,6 +605,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 { @@ -1265,17 +1292,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; @@ -1283,30 +1307,91 @@ 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; +} + +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 @@ -1333,6 +1418,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(); diff --git a/src/mserv.c b/src/mserv.c index 88cede440..c91c59167 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -68,6 +68,7 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { }; consvar_t cv_masterserver = CVAR_INIT ("masterserver", "https://ms.kartkrew.org/ms/api", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange); +consvar_t cv_rendezvousserver = CVAR_INIT ("rendezvousserver", "jart-dev.jameds.org", CV_SAVE, NULL, NULL); consvar_t cv_servername = CVAR_INIT ("servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters); consvar_t cv_server_contact = CVAR_INIT ("server_contact", "", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters); @@ -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 diff --git a/src/mserv.h b/src/mserv.h index 2a0afd1b3..eb1152876 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -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; From b4e6bc6e67c679cadef07a905781fbf0e1d603c2 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 12 Nov 2020 17:07:34 -0800 Subject: [PATCH 2/3] Ignore zero length packets --- src/i_tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i_tcp.c b/src/i_tcp.c index e99672d14..483062793 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -596,7 +596,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)) From ed96a8b43234b19c1e13010bb2e87515c77b0292 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 12 Nov 2020 17:31:16 -0800 Subject: [PATCH 3/3] Don't request hole punch to self, and don't register local server for hole punching --- src/d_clisrv.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 16b850a4b..e4e19d259 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1991,7 +1991,8 @@ static void SendAskInfo(INT32 node) // our address to the host, it'll be able to speak to us. HSendPacket(node, false, 0, sizeof (askinfo_pak)); - I_NetRequestHolePunch(); + if (node != 0 && node != BROADCASTADDR) + I_NetRequestHolePunch(); } serverelem_t serverlist[MAXSERVERLIST]; @@ -6145,7 +6146,7 @@ void NetKeepAlive(void) MasterClient_Ticker(); #endif - if (serverrunning) + if (netgame && serverrunning) { RenewHolePunch(); } @@ -6209,7 +6210,7 @@ void NetUpdate(void) MasterClient_Ticker(); // Acking the Master Server #endif - if (serverrunning) + if (netgame && serverrunning) { RenewHolePunch(); }