From 503ccc81c919cbe36143e48ead8bb5caee2aef7a Mon Sep 17 00:00:00 2001 From: truelight Date: Wed, 5 Jan 2005 14:39:48 +0000 Subject: [PATCH] (svn r1389) -Add: [Network] Added packet protection. No longer a client or server reads beyond the size of the packet -Fix: [Network] A server no longer crashes when a client sends an invalid DoCommand, but drops the client instead. --- command.c | 11 +++++ command.h | 1 + network.c | 6 ++- network_client.c | 122 +++++++++++++++++++++++++---------------------- network_data.c | 58 ++++++++++++++++++++-- network_data.h | 10 ++-- network_server.c | 68 ++++++++++++++++---------- network_udp.c | 53 ++++++++++++-------- 8 files changed, 213 insertions(+), 116 deletions(-) diff --git a/command.c b/command.c index 6c0e0d7c9b..cf4ac79445 100644 --- a/command.c +++ b/command.c @@ -312,6 +312,17 @@ static CommandProc * const _command_proc_table[] = { CmdReplaceVehicle, /* 114 */ }; +/* This function range-checks a cmd, and checks if the cmd is not NULL */ +bool IsValidCommand(int cmd) +{ + cmd = cmd & 0xFF; + + if (cmd < 0 || cmd >= lengthof(_command_proc_table) || _command_proc_table[cmd] == NULL) + return false; + + return true; +} + int32 DoCommandByTile(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc) { return DoCommand(GET_TILE_X(tile)*16, GET_TILE_Y(tile)*16, p1, p2, flags, procc); diff --git a/command.h b/command.h index c2aad5a2fb..c6c80756a3 100644 --- a/command.h +++ b/command.h @@ -182,6 +182,7 @@ enum { int32 DoCommand(int x, int y, uint32 p1, uint32 p2, uint32 flags, uint procc); int32 DoCommandByTile(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc); +bool IsValidCommand(int cmd); int32 GetAvailableMoneyForCommand(); #endif /* COMMAND_H */ diff --git a/network.c b/network.c index 31b3b09aab..26fd3b1cea 100644 --- a/network.c +++ b/network.c @@ -482,7 +482,10 @@ void NetworkCloseClient(NetworkClientState *cs) { NetworkClientInfo *ci; // Socket is already dead - if (cs->socket == INVALID_SOCKET) return; + if (cs->socket == INVALID_SOCKET) { + cs->quited = true; + return; + } DEBUG(net, 1) ("[NET] Closed client connection"); @@ -509,6 +512,7 @@ void NetworkCloseClient(NetworkClientState *cs) closesocket(cs->socket); cs->writable = false; + cs->quited = true; // Free all pending and partially received packets while (cs->packet_queue != NULL) { diff --git a/network_client.c b/network_client.c index f367a6cad0..a7b917b9ce 100644 --- a/network_client.c +++ b/network_client.c @@ -22,7 +22,7 @@ extern const char _openttd_revision[]; static uint32 last_ack_frame; -void NetworkRecvPatchSettings(Packet *p); +void NetworkRecvPatchSettings(NetworkClientState *cs, Packet *p); // ********** // Sending functions @@ -277,36 +277,36 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO) byte company_info_version; int i; - company_info_version = NetworkRecv_uint8(p); + company_info_version = NetworkRecv_uint8(MY_CLIENT, p); - if (company_info_version == 1) { + if (!MY_CLIENT->quited && company_info_version == 1) { byte total; byte current; - total = NetworkRecv_uint8(p); + total = NetworkRecv_uint8(MY_CLIENT, p); // There is no data at all.. if (total == 0) return NETWORK_RECV_STATUS_CLOSE_QUERY; - current = NetworkRecv_uint8(p); + current = NetworkRecv_uint8(MY_CLIENT, p); if (current >= MAX_PLAYERS) return NETWORK_RECV_STATUS_CLOSE_QUERY; _network_lobby_company_count++; - NetworkRecv_string(p, _network_player_info[current].company_name, sizeof(_network_player_info[current].company_name)); - _network_player_info[current].inaugurated_year = NetworkRecv_uint8(p); - _network_player_info[current].company_value = NetworkRecv_uint64(p); - _network_player_info[current].money = NetworkRecv_uint64(p); - _network_player_info[current].income = NetworkRecv_uint64(p); - _network_player_info[current].performance = NetworkRecv_uint16(p); + NetworkRecv_string(MY_CLIENT, p, _network_player_info[current].company_name, sizeof(_network_player_info[current].company_name)); + _network_player_info[current].inaugurated_year = NetworkRecv_uint8(MY_CLIENT, p); + _network_player_info[current].company_value = NetworkRecv_uint64(MY_CLIENT, p); + _network_player_info[current].money = NetworkRecv_uint64(MY_CLIENT, p); + _network_player_info[current].income = NetworkRecv_uint64(MY_CLIENT, p); + _network_player_info[current].performance = NetworkRecv_uint16(MY_CLIENT, p); for (i = 0; i < NETWORK_VEHICLE_TYPES; i++) - _network_player_info[current].num_vehicle[i] = NetworkRecv_uint16(p); + _network_player_info[current].num_vehicle[i] = NetworkRecv_uint16(MY_CLIENT, p); for (i = 0; i < NETWORK_STATION_TYPES; i++) - _network_player_info[current].num_station[i] = NetworkRecv_uint16(p); + _network_player_info[current].num_station[i] = NetworkRecv_uint16(MY_CLIENT, p); - NetworkRecv_string(p, _network_player_info[current].players, sizeof(_network_player_info[current].players)); + NetworkRecv_string(MY_CLIENT, p, _network_player_info[current].players, sizeof(_network_player_info[current].players)); InvalidateWindow(WC_NETWORK_WINDOW, 0); @@ -322,13 +322,16 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO) DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO) { NetworkClientInfo *ci; - uint16 index = NetworkRecv_uint16(p); - byte playas = NetworkRecv_uint8(p); + uint16 index = NetworkRecv_uint16(MY_CLIENT, p); + byte playas = NetworkRecv_uint8(MY_CLIENT, p); char name[NETWORK_NAME_LENGTH]; char unique_id[NETWORK_NAME_LENGTH]; - NetworkRecv_string(p, name, sizeof(name)); - NetworkRecv_string(p, unique_id, sizeof(unique_id)); + NetworkRecv_string(MY_CLIENT, p, name, sizeof(name)); + NetworkRecv_string(MY_CLIENT, p, unique_id, sizeof(unique_id)); + + if (MY_CLIENT->quited) + return NETWORK_RECV_STATUS_CONN_LOST; /* Do we receive a change of data? Most likely we changed playas */ if (index == _network_own_client_index) @@ -372,7 +375,7 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO) DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR) { - NetworkErrorCode error = NetworkRecv_uint8(p); + NetworkErrorCode error = NetworkRecv_uint8(MY_CLIENT, p); if (error == NETWORK_ERROR_NOT_AUTHORIZED || error == NETWORK_ERROR_NOT_EXPECTED || error == NETWORK_ERROR_PLAYER_MISMATCH) { @@ -398,7 +401,7 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR) DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD) { NetworkPasswordType type; - type = NetworkRecv_uint8(p); + type = NetworkRecv_uint8(MY_CLIENT, p); if (type == NETWORK_GAME_PASSWORD) { ShowNetworkNeedGamePassword(); @@ -413,7 +416,7 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD) DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WELCOME) { - _network_own_client_index = NetworkRecv_uint16(p); + _network_own_client_index = NetworkRecv_uint16(MY_CLIENT, p); // Start receiving the map SEND_COMMAND(PACKET_CLIENT_GETMAP)(); @@ -423,7 +426,7 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WELCOME) DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WAIT) { _network_join_status = NETWORK_JOIN_STATUS_WAITING; - _network_join_waiting = NetworkRecv_uint8(p); + _network_join_waiting = NetworkRecv_uint8(MY_CLIENT, p); InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0); // We are put on hold for receiving the map.. we need GUI for this ;) @@ -440,7 +443,10 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) byte maptype; - maptype = NetworkRecv_uint8(p); + maptype = NetworkRecv_uint8(MY_CLIENT, p); + + if (MY_CLIENT->quited) + return NETWORK_RECV_STATUS_CONN_LOST; // First packet, init some stuff if (maptype == MAP_PACKET_START) { @@ -453,11 +459,11 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) return NETWORK_RECV_STATUS_SAVEGAME; } - _frame_counter = _frame_counter_server = _frame_counter_max = NetworkRecv_uint32(p); + _frame_counter = _frame_counter_server = _frame_counter_max = NetworkRecv_uint32(MY_CLIENT, p); _network_join_status = NETWORK_JOIN_STATUS_DOWNLOADING; _network_join_kbytes = 0; - _network_join_kbytes_total = NetworkRecv_uint32(p) / 1024; + _network_join_kbytes_total = NetworkRecv_uint32(MY_CLIENT, p) / 1024; InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0); // The first packet does not contain any more data @@ -473,7 +479,7 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) } if (maptype == MAP_PACKET_PATCH) { - NetworkRecvPatchSettings(p); + NetworkRecvPatchSettings(MY_CLIENT, p); } // Check if this was the last packet @@ -481,8 +487,8 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) // We also get, very nice, the player_seeds in this packet int i; for (i = 0; i < MAX_PLAYERS; i++) { - _player_seeds[i][0] = NetworkRecv_uint32(p); - _player_seeds[i][1] = NetworkRecv_uint32(p); + _player_seeds[i][0] = NetworkRecv_uint32(MY_CLIENT, p); + _player_seeds[i][1] = NetworkRecv_uint32(MY_CLIENT, p); } fclose(file_pointer); @@ -527,16 +533,16 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FRAME) { - _frame_counter_server = NetworkRecv_uint32(p); - _frame_counter_max = NetworkRecv_uint32(p); + _frame_counter_server = NetworkRecv_uint32(MY_CLIENT, p); + _frame_counter_max = NetworkRecv_uint32(MY_CLIENT, p); #ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME // Test if the server supports this option // and if we are at the frame the server is if (p->pos < p->size) { _sync_frame = _frame_counter_server; - _sync_seed_1 = NetworkRecv_uint32(p); + _sync_seed_1 = NetworkRecv_uint32(MY_CLIENT, p); #ifdef NETWORK_SEND_DOUBLE_SEED - _sync_seed_2 = NetworkRecv_uint32(p); + _sync_seed_2 = NetworkRecv_uint32(MY_CLIENT, p); #endif } #endif @@ -555,10 +561,10 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FRAME) DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_SYNC) { - _sync_frame = NetworkRecv_uint32(p); - _sync_seed_1 = NetworkRecv_uint32(p); + _sync_frame = NetworkRecv_uint32(MY_CLIENT, p); + _sync_seed_1 = NetworkRecv_uint32(MY_CLIENT, p); #ifdef NETWORK_SEND_DOUBLE_SEED - _sync_seed_2 = NetworkRecv_uint32(p); + _sync_seed_2 = NetworkRecv_uint32(MY_CLIENT, p); #endif return NETWORK_RECV_STATUS_OKAY; @@ -569,21 +575,21 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMMAND) int i; char *dparam_char; CommandPacket *cp = malloc(sizeof(CommandPacket)); - cp->player = NetworkRecv_uint8(p); - cp->cmd = NetworkRecv_uint32(p); - cp->p1 = NetworkRecv_uint32(p); - cp->p2 = NetworkRecv_uint32(p); - cp->tile = NetworkRecv_uint32(p); + cp->player = NetworkRecv_uint8(MY_CLIENT, p); + cp->cmd = NetworkRecv_uint32(MY_CLIENT, p); + cp->p1 = NetworkRecv_uint32(MY_CLIENT, p); + cp->p2 = NetworkRecv_uint32(MY_CLIENT, p); + cp->tile = NetworkRecv_uint32(MY_CLIENT, p); /* We are going to send them byte by byte, because dparam is misused for chars (if it is used), and else we have a BigEndian / LittleEndian problem.. we should fix the misuse of dparam... -- TrueLight */ dparam_char = (char *)&cp->dp[0]; for (i = 0; i < lengthof(cp->dp) * 4; i++) { - *dparam_char = NetworkRecv_uint8(p); + *dparam_char = NetworkRecv_uint8(MY_CLIENT, p); dparam_char++; } - cp->callback = NetworkRecv_uint8(p); - cp->frame = NetworkRecv_uint32(p); + cp->callback = NetworkRecv_uint8(MY_CLIENT, p); + cp->frame = NetworkRecv_uint32(MY_CLIENT, p); cp->next = NULL; // The server did send us this command.. @@ -603,16 +609,16 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMMAND) DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CHAT) { - NetworkAction action = NetworkRecv_uint8(p); + NetworkAction action = NetworkRecv_uint8(MY_CLIENT, p); char msg[MAX_TEXT_MSG_LEN]; NetworkClientInfo *ci = NULL, *ci_to; uint16 index; char name[NETWORK_NAME_LENGTH]; bool self_send; - index = NetworkRecv_uint16(p); - self_send = NetworkRecv_uint8(p); - NetworkRecv_string(p, msg, MAX_TEXT_MSG_LEN); + index = NetworkRecv_uint16(MY_CLIENT, p); + self_send = NetworkRecv_uint8(MY_CLIENT, p); + NetworkRecv_string(MY_CLIENT, p, msg, MAX_TEXT_MSG_LEN); ci_to = NetworkFindClientInfoFromIndex(index); if (ci_to == NULL) return NETWORK_RECV_STATUS_OKAY; @@ -656,8 +662,8 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT) uint16 index; NetworkClientInfo *ci; - index = NetworkRecv_uint16(p); - errorno = NetworkRecv_uint8(p); + index = NetworkRecv_uint16(MY_CLIENT, p); + errorno = NetworkRecv_uint8(MY_CLIENT, p); GetString(str, STR_NETWORK_ERR_CLIENT_GENERAL + errorno); @@ -680,8 +686,8 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_QUIT) uint16 index; NetworkClientInfo *ci; - index = NetworkRecv_uint16(p); - NetworkRecv_string(p, str, 100); + index = NetworkRecv_uint16(MY_CLIENT, p); + NetworkRecv_string(MY_CLIENT, p, str, 100); ci = NetworkFindClientInfoFromIndex(index); if (ci != NULL) { @@ -704,7 +710,7 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_JOIN) uint16 index; NetworkClientInfo *ci; - index = NetworkRecv_uint16(p); + index = NetworkRecv_uint16(MY_CLIENT, p); ci = NetworkFindClientInfoFromIndex(index); if (ci != NULL) @@ -785,7 +791,7 @@ extern const SettingDesc patch_settings[]; // This is a TEMPORARY solution to get the patch-settings // to the client. When the patch-settings are saved in the savegame // this should be removed!! -void NetworkRecvPatchSettings(Packet *p) +void NetworkRecvPatchSettings(NetworkClientState *cs, Packet *p) { const SettingDesc *item; @@ -796,15 +802,15 @@ void NetworkRecvPatchSettings(Packet *p) case SDT_BOOL: case SDT_INT8: case SDT_UINT8: - *(uint8 *)(item->ptr) = NetworkRecv_uint8(p); + *(uint8 *)(item->ptr) = NetworkRecv_uint8(cs, p); break; case SDT_INT16: case SDT_UINT16: - *(uint16 *)(item->ptr) = NetworkRecv_uint16(p); + *(uint16 *)(item->ptr) = NetworkRecv_uint16(cs, p); break; case SDT_INT32: case SDT_UINT32: - *(uint32 *)(item->ptr) = NetworkRecv_uint32(p); + *(uint32 *)(item->ptr) = NetworkRecv_uint32(cs, p); break; } item++; @@ -829,8 +835,8 @@ NetworkRecvStatus NetworkClient_ReadPackets(NetworkClientState *cs) NetworkRecvStatus res = NETWORK_RECV_STATUS_OKAY; while (res == NETWORK_RECV_STATUS_OKAY && (p = NetworkRecv_Packet(cs, &res)) != NULL) { - byte type = NetworkRecv_uint8(p); - if (type < PACKET_END && _network_client_packet[type] != NULL) { + byte type = NetworkRecv_uint8(MY_CLIENT, p); + if (type < PACKET_END && _network_client_packet[type] != NULL && !MY_CLIENT->quited) { res = _network_client_packet[type](p); } else { res = NETWORK_RECV_STATUS_MALFORMED_PACKET; diff --git a/network_data.c b/network_data.c index fbe8db32e7..bc8074eb2a 100644 --- a/network_data.c +++ b/network_data.c @@ -183,22 +183,54 @@ bool NetworkSend_Packets(NetworkClientState *cs) // Receiving commands // Again, the next couple of functions are endian-safe // see the comment around NetworkSend_uint8 for more info. -uint8 NetworkRecv_uint8(Packet *packet) +uint8 NetworkRecv_uint8(NetworkClientState *cs, Packet *packet) { + /* Don't allow reading from a closed socket */ + if (cs->quited) + return 0; + + /* Check if variable is within packet-size */ + if (packet->pos + 1 > packet->size) { + CloseConnection(cs); + return 0; + } + return packet->buffer[packet->pos++]; } -uint16 NetworkRecv_uint16(Packet *packet) +uint16 NetworkRecv_uint16(NetworkClientState *cs, Packet *packet) { uint16 n; + + /* Don't allow reading from a closed socket */ + if (cs->quited) + return 0; + + /* Check if variable is within packet-size */ + if (packet->pos + 2 > packet->size) { + CloseConnection(cs); + return 0; + } + n = (uint16)packet->buffer[packet->pos++]; n += (uint16)packet->buffer[packet->pos++] << 8; return n; } -uint32 NetworkRecv_uint32(Packet *packet) +uint32 NetworkRecv_uint32(NetworkClientState *cs, Packet *packet) { uint32 n; + + /* Don't allow reading from a closed socket */ + if (cs->quited) + return 0; + + /* Check if variable is within packet-size */ + if (packet->pos + 4 > packet->size) { + CloseConnection(cs); + return 0; + } + n = (uint32)packet->buffer[packet->pos++]; n += (uint32)packet->buffer[packet->pos++] << 8; n += (uint32)packet->buffer[packet->pos++] << 16; @@ -206,9 +238,20 @@ uint32 NetworkRecv_uint32(Packet *packet) return n; } -uint64 NetworkRecv_uint64(Packet *packet) +uint64 NetworkRecv_uint64(NetworkClientState *cs, Packet *packet) { uint64 n; + + /* Don't allow reading from a closed socket */ + if (cs->quited) + return 0; + + /* Check if variable is within packet-size */ + if (packet->pos + 8 > packet->size) { + CloseConnection(cs); + return 0; + } + n = (uint64)packet->buffer[packet->pos++]; n += (uint64)packet->buffer[packet->pos++] << 8; n += (uint64)packet->buffer[packet->pos++] << 16; @@ -221,9 +264,14 @@ uint64 NetworkRecv_uint64(Packet *packet) } // Reads a string till it finds a '\0' in the stream -void NetworkRecv_string(Packet *p, char* buffer, size_t size) +void NetworkRecv_string(NetworkClientState *cs, Packet *p, char* buffer, size_t size) { int pos; + + /* Don't allow reading from a closed socket */ + if (cs->quited) + return; + pos = p->pos; while (--size > 0 && pos < p->size && (*buffer++ = p->buffer[pos++]) != '\0') {} if (size == 0 || pos == p->size) diff --git a/network_data.h b/network_data.h index 2143be5418..13da758231 100644 --- a/network_data.h +++ b/network_data.h @@ -203,11 +203,11 @@ void NetworkSend_uint64(Packet *packet, uint64 data); void NetworkSend_string(Packet *packet, const char* data); void NetworkSend_Packet(Packet *packet, NetworkClientState *cs); -uint8 NetworkRecv_uint8(Packet *packet); -uint16 NetworkRecv_uint16(Packet *packet); -uint32 NetworkRecv_uint32(Packet *packet); -uint64 NetworkRecv_uint64(Packet *packet); -void NetworkRecv_string(Packet *packet, char* buffer, size_t size); +uint8 NetworkRecv_uint8(NetworkClientState *cs, Packet *packet); +uint16 NetworkRecv_uint16(NetworkClientState *cs, Packet *packet); +uint32 NetworkRecv_uint32(NetworkClientState *cs, Packet *packet); +uint64 NetworkRecv_uint64(NetworkClientState *cs, Packet *packet); +void NetworkRecv_string(NetworkClientState *cs, Packet *packet, char* buffer, size_t size); Packet *NetworkRecv_Packet(NetworkClientState *cs, NetworkRecvStatus *status); bool NetworkSend_Packets(NetworkClientState *cs); diff --git a/network_server.c b/network_server.c index a294ea5180..edd2aab6b0 100644 --- a/network_server.c +++ b/network_server.c @@ -580,7 +580,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_JOIN) char client_revision[NETWORK_REVISION_LENGTH]; - NetworkRecv_string(p, client_revision, sizeof(client_revision)); + NetworkRecv_string(cs, p, client_revision, sizeof(client_revision)); #if defined(WITH_REV) || defined(WITH_REV_HACK) // Check if the client has revision control enabled @@ -594,10 +594,13 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_JOIN) } #endif - NetworkRecv_string(p, name, sizeof(name)); - playas = NetworkRecv_uint8(p); - client_lang = NetworkRecv_uint8(p); - NetworkRecv_string(p, unique_id, sizeof(unique_id)); + NetworkRecv_string(cs, p, name, sizeof(name)); + playas = NetworkRecv_uint8(cs, p); + client_lang = NetworkRecv_uint8(cs, p); + NetworkRecv_string(cs, p, unique_id, sizeof(unique_id)); + + if (cs->quited) + return; // Check if someone else already has that name snprintf(test_name, sizeof(test_name), "%s", name); @@ -644,8 +647,8 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_PASSWORD) char password[NETWORK_PASSWORD_LENGTH]; NetworkClientInfo *ci; - type = NetworkRecv_uint8(p); - NetworkRecv_string(p, password, sizeof(password)); + type = NetworkRecv_uint8(cs, p); + NetworkRecv_string(cs, p, password, sizeof(password)); if (cs->status == STATUS_INACTIVE && type == NETWORK_GAME_PASSWORD) { // Check game-password @@ -761,21 +764,30 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) return; } - cp->player = NetworkRecv_uint8(p); - cp->cmd = NetworkRecv_uint32(p); - cp->p1 = NetworkRecv_uint32(p); - cp->p2 = NetworkRecv_uint32(p); - cp->tile = NetworkRecv_uint32(p); + cp->player = NetworkRecv_uint8(cs, p); + cp->cmd = NetworkRecv_uint32(cs, p); + cp->p1 = NetworkRecv_uint32(cs, p); + cp->p2 = NetworkRecv_uint32(cs, p); + cp->tile = NetworkRecv_uint32(cs, p); /* We are going to send them byte by byte, because dparam is misused for chars (if it is used), and else we have a BigEndian / LittleEndian problem.. we should fix the misuse of dparam... -- TrueLight */ dparam_char = (char *)&cp->dp[0]; for (i = 0; i < lengthof(cp->dp) * 4; i++) { - *dparam_char = NetworkRecv_uint8(p); + *dparam_char = NetworkRecv_uint8(cs, p); dparam_char++; } - callback = NetworkRecv_uint8(p); + callback = NetworkRecv_uint8(cs, p); + + if (cs->quited) + return; + + /* Check if cp->cmd is valid */ + if (!IsValidCommand(cp->cmd)) { + SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED); + return; + } ci = DEREF_CLIENT_INFO(cs); // Only CMD_PLAYER_CTRL is always allowed, for the rest, playas needs @@ -814,7 +826,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) // The frame can be executed in the same frame as the next frame-packet // That frame just before that frame is saved in _frame_counter_max cp->frame = _frame_counter_max + 1; - cp->next = NULL; + cp->next = NULL; // Queue the command for the clients (are send at the end of the frame // if they can handle it ;)) @@ -826,6 +838,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) cp->callback = 0; else cp->callback = callback; + NetworkAddCommandQueue(new_cs, cp); } } @@ -847,7 +860,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ERROR) // This packets means a client noticed an error and is reporting this // to us. Display the error and report it to the other clients NetworkClientState *new_cs; - byte errorno = NetworkRecv_uint8(p); + byte errorno = NetworkRecv_uint8(cs, p); char str[100]; char client_name[NETWORK_CLIENT_NAME_LENGTH]; @@ -888,7 +901,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_QUIT) return; } - NetworkRecv_string(p, str, 100); + NetworkRecv_string(cs, p, str, 100); NetworkGetClientName(client_name, sizeof(client_name), cs); @@ -906,7 +919,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_QUIT) DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ACK) { // The client received the frame, make note of it - cs->last_frame = NetworkRecv_uint32(p); + cs->last_frame = NetworkRecv_uint32(cs, p); // With those 2 values we can calculate the lag realtime cs->last_frame_server = _frame_counter; @@ -1018,12 +1031,12 @@ void NetworkServer_HandleChat(NetworkAction action, DestType desttype, int dest, DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_CHAT) { - NetworkAction action = NetworkRecv_uint8(p); - DestType desttype = NetworkRecv_uint8(p); - int dest = NetworkRecv_uint8(p); + NetworkAction action = NetworkRecv_uint8(cs, p); + DestType desttype = NetworkRecv_uint8(cs, p); + int dest = NetworkRecv_uint8(cs, p); char msg[MAX_TEXT_MSG_LEN]; - NetworkRecv_string(p, msg, MAX_TEXT_MSG_LEN); + NetworkRecv_string(cs, p, msg, MAX_TEXT_MSG_LEN); NetworkServer_HandleChat(action, desttype, dest, msg, cs->index); } @@ -1033,7 +1046,7 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_SET_PASSWORD) char password[NETWORK_PASSWORD_LENGTH]; NetworkClientInfo *ci; - NetworkRecv_string(p, password, sizeof(password)); + NetworkRecv_string(cs, p, password, sizeof(password)); ci = DEREF_CLIENT_INFO(cs); if (ci->client_playas <= MAX_PLAYERS) { @@ -1046,9 +1059,12 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_SET_NAME) char client_name[NETWORK_CLIENT_NAME_LENGTH]; NetworkClientInfo *ci; - NetworkRecv_string(p, client_name, sizeof(client_name)); + NetworkRecv_string(cs, p, client_name, sizeof(client_name)); ci = DEREF_CLIENT_INFO(cs); + if (cs->quited) + return; + if (ci != NULL) { // Display change if (NetworkFindName(client_name)) { @@ -1388,8 +1404,8 @@ bool NetworkServer_ReadPackets(NetworkClientState *cs) Packet *p; NetworkRecvStatus res; while((p = NetworkRecv_Packet(cs, &res)) != NULL) { - byte type = NetworkRecv_uint8(p); - if (type < PACKET_END && _network_server_packet[type] != NULL) + byte type = NetworkRecv_uint8(cs, p); + if (type < PACKET_END && _network_server_packet[type] != NULL && !cs->quited) _network_server_packet[type](cs, p); else DEBUG(net, 0)("[NET][Server] Received invalid packet type %d", type); diff --git a/network_udp.c b/network_udp.c index 8abf9171bc..bd319322ae 100644 --- a/network_udp.c +++ b/network_udp.c @@ -38,6 +38,8 @@ enum { #define DEF_UDP_RECEIVE_COMMAND(type) void NetworkPacketReceive_ ## type ## _command(Packet *p, struct sockaddr_in *client_addr) void NetworkSendUDP_Packet(SOCKET udp, Packet *p, struct sockaddr_in *recv); +NetworkClientState _udp_cs; + DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER) { Packet *packet; @@ -86,8 +88,10 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE) if (_network_udp_server) return; - game_info_version = NetworkRecv_uint8(p); + game_info_version = NetworkRecv_uint8(&_udp_cs, p); + if (_udp_cs.quited) + return; DEBUG(net, 6)("[NET][UDP] Server response from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port)); @@ -95,20 +99,20 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE) item = NetworkGameListAddItem(inet_addr(inet_ntoa(client_addr->sin_addr)), ntohs(client_addr->sin_port)); if (game_info_version == 1) { - NetworkRecv_string(p, item->info.server_name, sizeof(item->info.server_name)); - NetworkRecv_string(p, item->info.server_revision, sizeof(item->info.server_revision)); - item->info.server_lang = NetworkRecv_uint8(p); - item->info.use_password = NetworkRecv_uint8(p); - item->info.clients_max = NetworkRecv_uint8(p); - item->info.clients_on = NetworkRecv_uint8(p); - item->info.spectators_on = NetworkRecv_uint8(p); - item->info.game_date = NetworkRecv_uint16(p); - item->info.start_date = NetworkRecv_uint16(p); - NetworkRecv_string(p, item->info.map_name, sizeof(item->info.map_name)); - item->info.map_width = NetworkRecv_uint16(p); - item->info.map_height = NetworkRecv_uint16(p); - item->info.map_set = NetworkRecv_uint8(p); - item->info.dedicated = NetworkRecv_uint8(p); + NetworkRecv_string(&_udp_cs, p, item->info.server_name, sizeof(item->info.server_name)); + NetworkRecv_string(&_udp_cs, p, item->info.server_revision, sizeof(item->info.server_revision)); + item->info.server_lang = NetworkRecv_uint8(&_udp_cs, p); + item->info.use_password = NetworkRecv_uint8(&_udp_cs, p); + item->info.clients_max = NetworkRecv_uint8(&_udp_cs, p); + item->info.clients_on = NetworkRecv_uint8(&_udp_cs, p); + item->info.spectators_on = NetworkRecv_uint8(&_udp_cs, p); + item->info.game_date = NetworkRecv_uint16(&_udp_cs, p); + item->info.start_date = NetworkRecv_uint16(&_udp_cs, p); + NetworkRecv_string(&_udp_cs, p, item->info.map_name, sizeof(item->info.map_name)); + item->info.map_width = NetworkRecv_uint16(&_udp_cs, p); + item->info.map_height = NetworkRecv_uint16(&_udp_cs, p); + item->info.map_set = NetworkRecv_uint8(&_udp_cs, p); + item->info.dedicated = NetworkRecv_uint8(&_udp_cs, p); if (item->info.hostname[0] == '\0') snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", inet_ntoa(client_addr->sin_addr)); @@ -237,12 +241,15 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST) { * an uint32 (ip) and an uint16 (port) for each pair */ - ver = NetworkRecv_uint8(p); + ver = NetworkRecv_uint8(&_udp_cs, p); + + if (_udp_cs.quited) + return; if (ver == 1) { - for (i = NetworkRecv_uint16(p); i != 0 ; i--) { - ip.s_addr = TO_LE32(NetworkRecv_uint32(p)); - port = NetworkRecv_uint16(p); + for (i = NetworkRecv_uint16(&_udp_cs, p); i != 0 ; i--) { + ip.s_addr = TO_LE32(NetworkRecv_uint32(&_udp_cs, p)); + port = NetworkRecv_uint16(&_udp_cs, p); NetworkUDPQueryServer(inet_ntoa(ip), port); } } @@ -282,9 +289,13 @@ void NetworkHandleUDPPacket(Packet *p, struct sockaddr_in *client_addr) { byte type; - type = NetworkRecv_uint8(p); + /* Fake a client, so we can see when there is an illegal packet */ + _udp_cs.socket = INVALID_SOCKET; + _udp_cs.quited = false; + + type = NetworkRecv_uint8(&_udp_cs, p); - if (type < PACKET_UDP_END && _network_udp_packet[type] != NULL) { + if (type < PACKET_UDP_END && _network_udp_packet[type] != NULL && !_udp_cs.quited) { _network_udp_packet[type](p, client_addr); } else { DEBUG(net, 0)("[NET][UDP] Received invalid packet type %d", type);