773 // Wrong status for this packet, give a warning to client, and close connection |
773 // Wrong status for this packet, give a warning to client, and close connection |
774 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED); |
774 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED); |
775 } |
775 } |
776 } |
776 } |
777 |
777 |
|
778 static inline const char* GetPlayerIP(const NetworkClientInfo *ci) {return inet_ntoa(*(struct in_addr *)&ci->client_ip);} |
|
779 |
|
780 /** Enforce the command flags. |
|
781 * Eg a server-only command can only be executed by a server, etc. |
|
782 * @param *cp the commandpacket that is going to be checked |
|
783 * @param *ci client information for debugging output to console |
|
784 */ |
|
785 static bool CheckCommandFlags(const CommandPacket *cp, const NetworkClientInfo *ci) |
|
786 { |
|
787 byte flags = GetCommandFlags(cp->cmd); |
|
788 |
|
789 if (flags & CMD_SERVER && ci->client_index != NETWORK_SERVER_INDEX) { |
|
790 IConsolePrintF(_iconsole_color_error, "WARNING: server only command from player %d (IP: %s), kicking...", ci->client_playas, GetPlayerIP(ci)); |
|
791 return false; |
|
792 } |
|
793 |
|
794 if (flags & CMD_OFFLINE) { |
|
795 IConsolePrintF(_iconsole_color_error, "WARNING: offline only command from player %d (IP: %s), kicking...", ci->client_playas, GetPlayerIP(ci)); |
|
796 return false; |
|
797 } |
|
798 return true; |
|
799 } |
|
800 |
|
801 /** The client has done a command and wants us to handle it |
|
802 * @param *cs the connected client that has sent the command |
|
803 * @param *p the packet in which the command was sent |
|
804 */ |
778 DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) |
805 DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND) |
779 { |
806 { |
780 // The client has done a command and wants us to handle it |
807 NetworkClientState *new_cs; |
|
808 const NetworkClientInfo *ci; |
|
809 char *dparam_char; |
781 uint i; |
810 uint i; |
782 byte callback; |
811 byte callback; |
783 NetworkClientState *new_cs; |
|
784 NetworkClientInfo *ci; |
|
785 char *dparam_char; |
|
786 |
812 |
787 CommandPacket *cp = malloc(sizeof(CommandPacket)); |
813 CommandPacket *cp = malloc(sizeof(CommandPacket)); |
788 |
814 |
789 // The client was never joined.. so this is impossible, right? |
815 // The client was never joined.. so this is impossible, right? |
790 // Ignore the packet, give the client a warning, and close his connection |
816 // Ignore the packet, give the client a warning, and close his connection |
796 cp->player = NetworkRecv_uint8(cs, p); |
822 cp->player = NetworkRecv_uint8(cs, p); |
797 cp->cmd = NetworkRecv_uint32(cs, p); |
823 cp->cmd = NetworkRecv_uint32(cs, p); |
798 cp->p1 = NetworkRecv_uint32(cs, p); |
824 cp->p1 = NetworkRecv_uint32(cs, p); |
799 cp->p2 = NetworkRecv_uint32(cs, p); |
825 cp->p2 = NetworkRecv_uint32(cs, p); |
800 cp->tile = NetworkRecv_uint32(cs, p); |
826 cp->tile = NetworkRecv_uint32(cs, p); |
801 /* We are going to send them byte by byte, because dparam is misused |
827 /** @todo We are going to send dparams byte by byte, because dparam is misused |
802 for chars (if it is used), and else we have a BigEndian / LittleEndian |
828 * for charstrings (if it is used), and else we have a Big/Little Endian |
803 problem.. we should fix the misuse of dparam... -- TrueLight */ |
829 * problem.. we should fix the misuse of dparam... -- TrueLight |
|
830 */ |
804 dparam_char = (char *)&cp->dp[0]; |
831 dparam_char = (char *)&cp->dp[0]; |
805 for (i = 0; i < lengthof(cp->dp) * 4; i++) { |
832 for (i = 0; i < lengthof(cp->dp) * 4; i++) { |
806 *dparam_char = NetworkRecv_uint8(cs, p); |
833 *dparam_char = NetworkRecv_uint8(cs, p); |
807 dparam_char++; |
834 dparam_char++; |
808 } |
835 } |
809 |
836 |
810 callback = NetworkRecv_uint8(cs, p); |
837 callback = NetworkRecv_uint8(cs, p); |
811 |
838 |
812 if (cs->quited) |
839 if (cs->quited) return; |
813 return; |
840 |
|
841 ci = DEREF_CLIENT_INFO(cs); |
814 |
842 |
815 /* Check if cp->cmd is valid */ |
843 /* Check if cp->cmd is valid */ |
816 if (!IsValidCommand(cp->cmd)) { |
844 if (!IsValidCommand(cp->cmd)) { |
|
845 IConsolePrintF(_iconsole_color_error, "WARNING: invalid command from player %d (IP: %s).", ci->client_playas, GetPlayerIP(ci)); |
817 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED); |
846 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED); |
818 return; |
847 return; |
819 } |
848 } |
820 |
849 |
821 ci = DEREF_CLIENT_INFO(cs); |
850 if (!CheckCommandFlags(cp, ci)) { |
822 // Only CMD_PLAYER_CTRL is always allowed, for the rest, playas needs |
851 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_KICKED); |
823 // to match the player in the packet |
852 return; |
824 if (!(cp->cmd == CMD_PLAYER_CTRL && cp->p1 == 0) && ci->client_playas-1 != cp->player) { |
853 } |
|
854 |
|
855 /** Only CMD_PLAYER_CTRL is always allowed, for the rest, playas needs |
|
856 * to match the player in the packet. If it doesn't, the client has done |
|
857 * something pretty naughty (or a bug), and will be kicked |
|
858 */ |
|
859 if (!(cp->cmd == CMD_PLAYER_CTRL && cp->p1 == 0) && ci->client_playas - 1 != cp->player) { |
825 IConsolePrintF(_iconsole_color_error, "WARNING: player %d (IP: %s) tried to execute a command as player %d, kicking...", |
860 IConsolePrintF(_iconsole_color_error, "WARNING: player %d (IP: %s) tried to execute a command as player %d, kicking...", |
826 ci->client_playas - 1, inet_ntoa(*(struct in_addr *)&ci->client_ip), cp->player); |
861 ci->client_playas, GetPlayerIP(ci), cp->player); |
827 // The player did a command with the wrong player_id.. bad!! |
|
828 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_PLAYER_MISMATCH); |
862 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_PLAYER_MISMATCH); |
829 return; |
863 return; |
830 } |
864 } |
831 switch (cp->cmd) { |
865 |
832 /* Player_ctrl is not always allowed */ |
866 /** @todo CMD_PLAYER_CTRL with p1 = 0 announces a new player to the server. To give the |
833 case CMD_PLAYER_CTRL: |
867 * player the correct ID, the server injects p2 and executes the command. Any other p1 |
834 { |
868 * is prohibited. Pretty ugly and should be redone together with its function. |
835 /* cp->p1 == 0, is allowed */ |
869 * @see CmdPlayerCtrl() players.c:655 |
836 if (cp->p1 == 0) { |
870 */ |
837 // UGLY! p2 is mis-used to get the client-id in CmdPlayerCtrl |
871 if (cp->cmd == CMD_PLAYER_CTRL) { |
838 cp->p2 = cs - _clients; |
872 if (cp->p1 != 0) { |
839 } else { |
873 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_CHEATER); |
840 /* The rest are cheats */ |
874 return; |
841 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_CHEATER); |
875 } |
842 return; |
876 |
843 } |
877 // XXX - UGLY! p2 is mis-used to get the client-id in CmdPlayerCtrl |
844 } break; |
878 cp->p2 = cs - _clients; |
845 |
879 } |
846 /* Don't allow those commands if server == advertising (considered cheating) */ |
|
847 case CMD_MONEY_CHEAT: |
|
848 { |
|
849 if (_network_advertise) { |
|
850 SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_CHEATER); |
|
851 return; |
|
852 } |
|
853 } break; |
|
854 } |
|
855 |
|
856 |
880 |
857 // The frame can be executed in the same frame as the next frame-packet |
881 // The frame can be executed in the same frame as the next frame-packet |
858 // That frame just before that frame is saved in _frame_counter_max |
882 // That frame just before that frame is saved in _frame_counter_max |
859 cp->frame = _frame_counter_max + 1; |
883 cp->frame = _frame_counter_max + 1; |
860 cp->next = NULL; |
884 cp->next = NULL; |