X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=netconn.c;h=0b1c412077603dab2519b821084fd252041b617c;hb=66d1df20a5ed5ff954d7bf9db2db23cbe304281a;hp=ae0022f720a15c7a930d23c2ffb758568efecdfb;hpb=c72641f5e795ed0e0d58a38d79860cc92597e4da;p=xonotic%2Fdarkplaces.git diff --git a/netconn.c b/netconn.c index ae0022f7..0b1c4120 100755 --- a/netconn.c +++ b/netconn.c @@ -81,11 +81,14 @@ static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to lis static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"}; static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"}; static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"}; -static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible"}; -static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode"}; +static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"}; +static cvar_t gameversion_min = {0, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"}; +static cvar_t gameversion_max = {0, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"}; +static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"}; 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; @@ -129,9 +132,13 @@ cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to" cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"}; cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"}; -char net_extresponse[NET_EXTRESPONSE_MAX][1400]; -int net_extresponse_count = 0; -int net_extresponse_last = 0; +char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400]; +int cl_net_extresponse_count = 0; +int cl_net_extresponse_last = 0; + +char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400]; +int sv_net_extresponse_count = 0; +int sv_net_extresponse_last = 0; // ServerList interface serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT]; @@ -151,7 +158,7 @@ qboolean serverlist_consoleoutput; static int nFavorites = 0; static lhnetaddress_t favorites[256]; -void NetConn_UpdateFavorites() +void NetConn_UpdateFavorites(void) { const char *p; nFavorites = 0; @@ -377,7 +384,16 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry ) lhnetaddress_t addr; // reject incompatible servers - if (entry->info.gameversion != gameversion.integer) + if( + entry->info.gameversion != gameversion.integer + && + !( + gameversion_min.integer >= 0 // min/max range set by user/mod? + && gameversion_max.integer >= 0 + && gameversion_min.integer >= entry->info.gameversion // version of server in min/max range? + && gameversion_max.integer <= entry->info.gameversion + ) + ) return; // refresh the "favorite" status @@ -592,14 +608,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; } } @@ -607,12 +624,13 @@ qboolean NetConn_CanSend(netconn_t *conn) int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables) { int totallen = 0; + int temp; // 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) { @@ -636,9 +654,11 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers sendreliable = true; } // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1) - *((int *)(sendbuffer + 0)) = LittleLong((unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31)); + temp = (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31); + *((int *)(sendbuffer + 0)) = LittleLong(temp); // last received unreliable packet number, and last received reliable packet number (0 or 1) - *((int *)(sendbuffer + 4)) = LittleLong((unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31)); + temp = (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31); + *((int *)(sendbuffer + 4)) = LittleLong(temp); packetLen = 8; conn->outgoing_unreliable_sequence++; // client sends qport in every packet @@ -655,12 +675,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; @@ -669,7 +689,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; } @@ -686,7 +706,6 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers unsigned int packetLen; unsigned int dataLen; unsigned int eom; - unsigned int *header; // if a reliable message fragment has been lost, send it again if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0) @@ -704,12 +723,11 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers packetLen = NET_HEADERSIZE + dataLen; - header = (unsigned int *)sendbuffer; - header[0] = BigLong(packetLen | (NETFLAG_DATA | eom)); - header[1] = BigLong(conn->nq.sendSequence - 1); + StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom)); + StoreBigLong(sendbuffer + 4, 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) { @@ -753,14 +771,13 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers packetLen = NET_HEADERSIZE + dataLen; - header = (unsigned int *)sendbuffer; - header[0] = BigLong(packetLen | (NETFLAG_DATA | eom)); - header[1] = BigLong(conn->nq.sendSequence); + StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom)); + StoreBigLong(sendbuffer + 4, conn->nq.sendSequence); memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen); 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); @@ -782,14 +799,13 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers return -1; } - header = (unsigned int *)sendbuffer; - header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE); - header[1] = BigLong(conn->outgoing_unreliable_sequence); + StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE); + StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence); memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize); 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); @@ -1010,6 +1026,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) @@ -1033,6 +1051,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) @@ -1082,15 +1117,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 @@ -1119,13 +1156,13 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len unsigned int sequence; int qlength; - qlength = (unsigned int)BigLong(((int *)data)[0]); + qlength = (unsigned int)BuffBigLong(data); flags = qlength & ~NETFLAG_LENGTH_MASK; qlength &= NETFLAG_LENGTH_MASK; // control packets were already handled if (!(flags & NETFLAG_CTL) && qlength == length) { - sequence = BigLong(((int *)data)[1]); + sequence = BuffBigLong(data + 4); packetsReceived++; data += 8; length -= 8; @@ -1141,15 +1178,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; @@ -1168,7 +1207,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) @@ -1183,7 +1222,6 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len unsigned int packetLen; unsigned int dataLen; unsigned int eom; - unsigned int *header; conn->sendMessageLength -= MAX_PACKETFRAGMENT; memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength); @@ -1201,9 +1239,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len packetLen = NET_HEADERSIZE + dataLen; - header = (unsigned int *)sendbuffer; - header[0] = BigLong(packetLen | (NETFLAG_DATA | eom)); - header[1] = BigLong(conn->nq.sendSequence); + StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom)); + StoreBigLong(sendbuffer + 4, conn->nq.sendSequence); memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen); conn->nq.sendSequence++; @@ -1227,10 +1264,10 @@ 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; - temppacket[0] = BigLong(8 | NETFLAG_ACK); - temppacket[1] = BigLong(sequence); + conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes += originallength + 28; + conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes += 8 + 28; + StoreBigLong(sendbuffer, 8 | NETFLAG_ACK); + StoreBigLong(sendbuffer + 4, sequence); NetConn_Write(conn->mysocket, (unsigned char *)temppacket, 8, &conn->peeraddress); if (sequence == conn->nq.receiveSequence) { @@ -1369,7 +1406,18 @@ static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n) serverlist_info_t *info = &entry->info; // update description strings for engine menu and console output dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name); - dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game, (info->gameversion != gameversion.integer) ? '1' : '4', info->mod, info->map); + dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game, + ( + info->gameversion != gameversion.integer + && + !( + gameversion_min.integer >= 0 // min/max range set by user/mod? + && gameversion_max.integer >= 0 + && gameversion_min.integer >= info->gameversion // version of server in min/max range? + && gameversion_max.integer <= info->gameversion + ) + ) ? '1' : '4', + info->mod, info->map); if (entry->query == SQS_QUERIED) { if(!serverlist_paused) @@ -1527,6 +1575,54 @@ 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]; + const char *e; + int n; + dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]); + memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29); + + e = strchr(rcon_password.string, ' '); + n = e ? e-rcon_password.string : (int)strlen(rcon_password.string); + + if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n)) + { + 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 @@ -1696,11 +1792,11 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat } if (!strncmp(string, "extResponse ", 12)) { - ++net_extresponse_count; - if(net_extresponse_count > NET_EXTRESPONSE_MAX) - net_extresponse_count = NET_EXTRESPONSE_MAX; - net_extresponse_last = (net_extresponse_last + 1) % NET_EXTRESPONSE_MAX; - dpsnprintf(net_extresponse[net_extresponse_last], sizeof(net_extresponse[net_extresponse_last]), "'%s' %s", addressstring2, string + 12); + ++cl_net_extresponse_count; + if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX) + cl_net_extresponse_count = NET_EXTRESPONSE_MAX; + cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX; + dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12); return true; } if (!strncmp(string, "ping", 4)) @@ -1796,7 +1892,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat return ret; } // netquake control packets, supported for compatibility only - if (length >= 5 && (control = BigLong(*((int *)data))) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length) + if (length >= 5 && (control = BuffBigLong(data)) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length) { int n; serverlist_info_t *info; @@ -1892,7 +1988,7 @@ void NetConn_QueryQueueFrame(void) if(!net_slist_pause.integer && serverlist_paused) ServerList_RebuildViewList(); - serverlist_paused = net_slist_pause.integer; + serverlist_paused = net_slist_pause.integer != 0; if (serverlist_querysleep) return; @@ -1997,7 +2093,7 @@ void NetConn_ClientFrame(void) MSG_WriteByte(&net_message, CCREQ_CONNECT); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); - *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address); SZ_Clear(&net_message); } @@ -2265,9 +2361,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; @@ -2283,23 +2379,88 @@ 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; + const char *text, *userpass_start, *userpass_end, *userpass_startpass; + char buf[MAX_INPUTLINE]; qboolean hasquotes; + qboolean restricted = false; + qboolean have_usernames = false; + + userpass_start = rcon_password.string; + while((userpass_end = strchr(userpass_start, ' '))) + { + have_usernames = true; + strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1)); + if(buf[0]) + if(comparator(peeraddress, buf, password, cs, cslen)) + goto allow; + userpass_start = userpass_end + 1; + } + if(userpass_start[0]) + { + userpass_end = userpass_start + strlen(userpass_start); + if(comparator(peeraddress, userpass_start, password, cs, cslen)) + goto allow; + } - if(comparator(rcon_password.string, password, cs, cslen)) - return "rcon"; + restricted = true; + have_usernames = false; + userpass_start = rcon_restricted_password.string; + while((userpass_end = strchr(userpass_start, ' '))) + { + have_usernames = true; + strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1)); + if(buf[0]) + if(comparator(peeraddress, buf, password, cs, cslen)) + goto check; + userpass_start = userpass_end + 1; + } + if(userpass_start[0]) + { + userpass_end = userpass_start + strlen(userpass_start); + if(comparator(peeraddress, userpass_start, password, cs, cslen)) + goto check; + } - if(!comparator(rcon_restricted_password.string, password, cs, cslen)) - return NULL; + return NULL; // DENIED +check: for(text = s; text != endpos; ++text) if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';')) return NULL; // block possible exploits against the parser/alias expansion @@ -2342,6 +2503,13 @@ match: s += l + 1; } +allow: + userpass_startpass = strchr(userpass_start, ':'); + if(have_usernames && userpass_startpass && userpass_startpass < userpass_end) + return va("%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start); + else + return va("%srcon", restricted ? "restricted " : ""); + return "restricted rcon"; } @@ -2428,8 +2596,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; } @@ -2455,8 +2624,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; @@ -2568,11 +2738,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; } @@ -2583,7 +2772,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++) @@ -2594,11 +2783,20 @@ 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; } + if (!strncmp(string, "extResponse ", 12)) + { + ++sv_net_extresponse_count; + if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX) + sv_net_extresponse_count = NET_EXTRESPONSE_MAX; + sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX; + dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12); + return true; + } if (!strncmp(string, "ping", 4)) { if (developer.integer >= 10) @@ -2617,7 +2815,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat // protocol // (this protects more modern protocols against being used for // Quake packet flood Denial Of Service attacks) - if (length >= 5 && (i = BigLong(*((int *)data))) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)) + if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)) { int c; int protocolnumber; @@ -2647,7 +2845,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Incompatible version.\n"); - *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); break; @@ -2670,7 +2868,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket))); - *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); @@ -2707,7 +2905,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket))); - *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); // now set up the client struct @@ -2727,7 +2925,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Server is full.\n"); - *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); break; @@ -2757,7 +2955,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat MSG_WriteByte(&net_message, numclients); MSG_WriteByte(&net_message, svs.maxclients); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); - *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); } @@ -2789,7 +2987,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat MSG_WriteLong(&net_message, client->frags); MSG_WriteLong(&net_message, (int)(realtime - client->connecttime)); MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient"); - *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); } @@ -2819,7 +3017,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat MSG_WriteString(&net_message, var->name); MSG_WriteString(&net_message, var->string); } - *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress); SZ_Clear(&net_message); } @@ -2904,7 +3102,7 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw) MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); - *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); + StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); NetConn_Write(cl_sockets[i], net_message.data, net_message.cursize, &broadcastaddress); SZ_Clear(&net_message); @@ -3142,6 +3340,8 @@ void NetConn_Init(void) for (i = 0;sv_masters[i].name;i++) Cvar_RegisterVariable(&sv_masters[i]); Cvar_RegisterVariable(&gameversion); + Cvar_RegisterVariable(&gameversion_min); + Cvar_RegisterVariable(&gameversion_max); // COMMANDLINEOPTION: Server: -ip sets the ip address of this machine for purposes of networking (default 0.0.0.0 also known as INADDR_ANY), use only if you have multiple network adapters and need to choose one specifically. if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc) {