]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - netconn.c
fix handling of entities which change movetype at runtime
[xonotic/darkplaces.git] / netconn.c
index b079a370d3fb2a941b0b717459cd0ac0ab5022fd..efa23d1b70dd551d749034a6240503626dd108ef 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -88,6 +88,7 @@ static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_passwor
 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
 static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
 extern cvar_t rcon_secure;
+extern cvar_t rcon_secure_challengetimeout;
 
 /* statistic counters */
 static int packetsSent = 0;
@@ -603,14 +604,15 @@ int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnet
 qboolean NetConn_CanSend(netconn_t *conn)
 {
        conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
-       conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
-       conn->outgoing_reliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
-       conn->outgoing_acksize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
+       conn->outgoing_netgraph[conn->outgoing_packetcounter].time            = realtime;
+       conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
+       conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
+       conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
        if (realtime > conn->cleartime)
                return true;
        else
        {
-               conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_CHOKEDPACKET;
+               conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
                return false;
        }
 }
@@ -622,8 +624,8 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
        // if this packet was supposedly choked, but we find ourselves sending one
        // anyway, make sure the size counting starts at zero
        // (this mostly happens on level changes and disconnects and such)
-       if (conn->outgoing_unreliablesize[conn->outgoing_packetcounter] == NETGRAPH_CHOKEDPACKET)
-               conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
+       if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
+               conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
 
        if (protocol == PROTOCOL_QUAKEWORLD)
        {
@@ -666,12 +668,12 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        return -1;
                }
 
-               conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen;
+               conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
 
                // add the reliable message if there is one
                if (sendreliable)
                {
-                       conn->outgoing_reliablesize[conn->outgoing_packetcounter] += conn->sendMessageLength;
+                       conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
                        memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
                        packetLen += conn->sendMessageLength;
                        conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
@@ -680,7 +682,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                // add the unreliable message if possible
                if (packetLen + data->cursize <= 1400)
                {
-                       conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += data->cursize;
+                       conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
                        memcpy(sendbuffer + packetLen, data->data, data->cursize);
                        packetLen += data->cursize;
                }
@@ -720,7 +722,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        header[1] = BigLong(conn->nq.sendSequence - 1);
                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
-                       conn->outgoing_reliablesize[conn->outgoing_packetcounter] += packetLen;
+                       conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
 
                        if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
                        {
@@ -771,7 +773,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        conn->nq.sendSequence++;
 
-                       conn->outgoing_reliablesize[conn->outgoing_packetcounter] += packetLen;
+                       conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
 
                        NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
 
@@ -800,7 +802,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        conn->outgoing_unreliable_sequence++;
 
-                       conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen;
+                       conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
 
                        NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
 
@@ -1021,6 +1023,8 @@ static int clientport2 = -1;
 static int hostport = -1;
 void NetConn_UpdateSockets(void)
 {
+       int i, j;
+
        if (cls.state != ca_dedicated)
        {
                if (clientport2 != cl_netport.integer)
@@ -1044,6 +1048,23 @@ void NetConn_UpdateSockets(void)
                if (sv.active)
                        Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
        }
+
+       for (j = 0;j < MAX_RCONS;j++)
+       {
+               i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
+               if(cls.rcon_commands[i][0])
+               {
+                       if(realtime > cls.rcon_timeout[i])
+                       {
+                               char s[128];
+                               LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
+                               Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
+                               cls.rcon_commands[i][0] = 0;
+                               --cls.rcon_trying;
+                               break;
+                       }
+               }
+       }
 }
 
 static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length, protocolversion_t protocol, double newtimeout)
@@ -1093,15 +1114,17 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        while (count--)
                        {
                                conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
-                               conn->incoming_unreliablesize[conn->incoming_packetcounter] = NETGRAPH_LOSTPACKET;
-                               conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
-                               conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+                               conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
+                               conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
+                               conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
+                               conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
                        }
                }
                conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
-               conn->incoming_unreliablesize[conn->incoming_packetcounter] = originallength;
-               conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
-               conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+               conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
+               conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
+               conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
+               conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
                if (reliable_ack == conn->qw.reliable_sequence)
                {
                        // received, now we will be able to send another reliable message
@@ -1152,15 +1175,17 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                                                while (count--)
                                                {
                                                        conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
-                                                       conn->incoming_unreliablesize[conn->incoming_packetcounter] = NETGRAPH_LOSTPACKET;
-                                                       conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
-                                                       conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+                                                       conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
+                                                       conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
+                                                       conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
+                                                       conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
                                                }
                                        }
                                        conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
-                                       conn->incoming_unreliablesize[conn->incoming_packetcounter] = originallength;
-                                       conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
-                                       conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+                                       conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
+                                       conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
+                                       conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
+                                       conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
                                        conn->nq.unreliableReceiveSequence = sequence + 1;
                                        conn->lastMessageTime = realtime;
                                        conn->timeout = realtime + newtimeout;
@@ -1179,7 +1204,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        }
                        else if (flags & NETFLAG_ACK)
                        {
-                               conn->incoming_acksize[conn->incoming_packetcounter] += originallength;
+                               conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
                                if (sequence == (conn->nq.sendSequence - 1))
                                {
                                        if (sequence == conn->nq.ackSequence)
@@ -1238,8 +1263,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        else if (flags & NETFLAG_DATA)
                        {
                                unsigned int temppacket[2];
-                               conn->incoming_reliablesize[conn->incoming_packetcounter] += originallength;
-                               conn->outgoing_acksize[conn->outgoing_packetcounter] += 8;
+                               conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   += originallength + 28;
+                               conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        += 8 + 28;
                                temppacket[0] = BigLong(8 | NETFLAG_ACK);
                                temppacket[1] = BigLong(sequence);
                                NetConn_Write(conn->mysocket, (unsigned char *)temppacket, 8, &conn->peeraddress);
@@ -1549,6 +1574,48 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        Com_HexDumpToConsole(data, length);
                }
 
+               if (length > 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
+               {
+                       int i, j;
+                       for (j = 0;j < MAX_RCONS;j++)
+                       {
+                               i = (cls.rcon_ringpos + j) % MAX_RCONS;
+                               if(cls.rcon_commands[i][0])
+                                       if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
+                                               break;
+                       }
+                       if (j < MAX_RCONS)
+                       {
+                               char buf[1500];
+                               char argbuf[1500];
+                               dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
+                               memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
+                               if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
+                               {
+                                       buf[45] = ' ';
+                                       strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
+                                       NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
+                                       cls.rcon_commands[i][0] = 0;
+                                       --cls.rcon_trying;
+
+                                       for (i = 0;i < MAX_RCONS;i++)
+                                               if(cls.rcon_commands[i][0])
+                                                       if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
+                                                               break;
+                                       if(i < MAX_RCONS)
+                                       {
+                                               NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
+                                               // extend the timeout on other requests as we asked for a challenge
+                                               for (i = 0;i < MAX_RCONS;i++)
+                                                       if(cls.rcon_commands[i][0])
+                                                               if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
+                                                                       cls.rcon_timeout[i] = realtime + rcon_secure_challengetimeout.value;
+                                       }
+
+                                       return true; // we used up the challenge, so we can't use this oen for connecting now anyway
+                               }
+                       }
+               }
                if (length > 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
                {
                        // darkplaces or quake3
@@ -2287,9 +2354,9 @@ void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress)
        }
 }
 
-typedef qboolean (*rcon_matchfunc_t) (const char *password, const char *hash, const char *s, int slen);
+typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
 
-qboolean hmac_mdfour_matching(const char *password, const char *hash, const char *s, int slen)
+qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
 {
        char mdfourbuf[16];
        long t1, t2;
@@ -2305,21 +2372,50 @@ qboolean hmac_mdfour_matching(const char *password, const char *hash, const char
        return !memcmp(mdfourbuf, hash, 16);
 }
 
-qboolean plaintext_matching(const char *password, const char *hash, const char *s, int slen)
+qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
+{
+       char mdfourbuf[16];
+       int i;
+
+       if(slen < (int)(sizeof(challenge[0].string)) - 1)
+               return false;
+
+       // validate the challenge
+       for (i = 0;i < MAX_CHALLENGES;i++)
+               if(challenge[i].time > 0)
+                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
+                               break;
+       // if the challenge is not recognized, drop the packet
+       if (i == MAX_CHALLENGES)
+               return false;
+
+       if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
+               return false;
+
+       if(memcmp(mdfourbuf, hash, 16))
+               return false;
+
+       // unmark challenge to prevent replay attacks
+       challenge[i].time = 0;
+
+       return true;
+}
+
+qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
 {
        return !strcmp(password, hash);
 }
 
 /// returns a string describing the user level, or NULL for auth failure
-const char *RCon_Authenticate(const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
+const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
 {
        const char *text;
        qboolean hasquotes;
 
-       if(comparator(rcon_password.string, password, cs, cslen))
+       if(comparator(peeraddress, rcon_password.string, password, cs, cslen))
                return "rcon";
        
-       if(!comparator(rcon_restricted_password.string, password, cs, cslen))
+       if(!comparator(peeraddress, rcon_restricted_password.string, password, cs, cslen))
                return NULL;
 
        for(text = s; text != endpos; ++text)
@@ -2450,8 +2546,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
                        {
-                               if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
-                                       break;
+                               if(challenge[i].time > 0)
+                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
+                                               break;
                                if (besttime > challenge[i].time)
                                        besttime = challenge[best = i].time;
                        }
@@ -2477,8 +2574,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                return true;
                        // validate the challenge
                        for (i = 0;i < MAX_CHALLENGES;i++)
-                               if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
-                                       break;
+                               if(challenge[i].time > 0)
+                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
+                                               break;
                        // if the challenge is not recognized, drop the packet
                        if (i == MAX_CHALLENGES)
                                return true;
@@ -2590,11 +2688,30 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        char *s = strchr(timeval, ' ');
                        char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
                        const char *userlevel;
+
+                       if(rcon_secure.integer > 1)
+                               return true;
+
+                       if(!s)
+                               return true; // invalid packet
+                       ++s;
+
+                       userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
+                       RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+                       return true;
+               }
+               if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
+               {
+                       char *password = string + 25;
+                       char *challenge = string + 42;
+                       char *s = strchr(challenge, ' ');
+                       char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
+                       const char *userlevel;
                        if(!s)
                                return true; // invalid packet
                        ++s;
 
-                       userlevel = RCon_Authenticate(password, s, endpos, hmac_mdfour_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
+                       userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
                        RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
                        return true;
                }
@@ -2605,7 +2722,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
                        char password[64];
 
-                       if(rcon_secure.integer)
+                       if(rcon_secure.integer > 0)
                                return true;
 
                        for (i = 0;!ISWHITESPACE(*s);s++)
@@ -2616,7 +2733,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        password[i] = 0;
                        if (!ISWHITESPACE(password[0]))
                        {
-                               const char *userlevel = RCon_Authenticate(password, s, endpos, plaintext_matching, NULL, 0);
+                               const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
                                RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
                        }
                        return true;