X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_net.pas;h=95686fe911a14e226c33f234963369657a6954b1;hb=ee582ed196b2c56776d407e22bdaaffc5b1ac6ff;hp=07d9713477ed7da1251d54bfffac53b3cfd5d9b7;hpb=10f31fd950820bcd8b83aec43debcfc260b714b2;p=d2df-sdl.git diff --git a/src/game/g_net.pas b/src/game/g_net.pas index 07d9713..95686fe 100644 --- a/src/game/g_net.pas +++ b/src/game/g_net.pas @@ -21,7 +21,7 @@ uses e_log, e_msg, utils, ENet, Classes, md5, MAPDEF{$IFDEF USE_MINIUPNPC}, miniupnpc;{$ELSE};{$ENDIF} const - NET_PROTOCOL_VER = 185; + NET_PROTOCOL_VER = 187; NET_MAXCLIENTS = 24; NET_CHANS = 12; @@ -105,6 +105,9 @@ type WaitForFirstSpawn: Boolean; // set to `true` in server, used to spawn a player on first full state request RCONAuth: Boolean; Voted: Boolean; + Crimes: Integer; + AuthTime: LongWord; + MsgTime: LongWord; Transfer: TNetFileTransfer; // only one transfer may be active NetOut: array [0..1] of TMsg; end; @@ -154,6 +157,13 @@ var NetMaxClients: Byte = 255; NetBannedHosts: array of TBanRecord; + NetAutoBanLimit: Integer = 5; + NetAutoBanPerm: Boolean = True; + NetAutoBanWarn: Boolean = False; + + NetAuthTimeout: Integer = 36 * 15; + NetPacketTimeout: Integer = 36 * 30; + NetState: Integer = NET_STATE_NONE; NetMyID: Integer = -1; @@ -172,6 +182,8 @@ var NetGotEverything: Boolean = False; NetGotKeys: Boolean = False; + NetDeafLevel: Integer = 0; + {$IFDEF USE_MINIUPNPC} NetPortForwarded: Word = 0; NetPongForwarded: Boolean = False; @@ -221,6 +233,8 @@ function g_Net_UnbanHost(IP: LongWord): Boolean; overload; procedure g_Net_UnbanNonPermHosts(); procedure g_Net_SaveBanList(); +procedure g_Net_Penalize(C: pTNetClient; Reason: string); + procedure g_Net_DumpStart(); procedure g_Net_DumpSendBuffer(); procedure g_Net_DumpRecvBuffer(Buf: penet_uint8; Len: LongWord); @@ -271,7 +285,10 @@ begin end; procedure g_Net_InitLowLevel (); + var v: ENetVersion; begin + v := enet_linked_version(); + e_LogWritefln('ENet Version: %s.%s.%s', [ENET_VERSION_GET_MAJOR(v), ENET_VERSION_GET_MINOR(v), ENET_VERSION_GET_PATCH(v)]); if enet_init_success then raise Exception.Create('wuta?!'); enet_init_success := (enet_initialize() = 0); end; @@ -493,6 +510,12 @@ begin exit; end; + // don't time out clients during a file transfer + if (NetAuthTimeout > 0) then + nc^.AuthTime := gTime + NetAuthTimeout; + if (NetPacketTimeout > 0) then + nc^.MsgTime := gTime + NetPacketTimeout; + tf := @NetClients[nid].Transfer; tf.lastAckTime := GetTimerMS(); @@ -1492,8 +1515,6 @@ begin NetAddr.host := IPAddr; NetAddr.port := Port; - if NetForwardPorts then NetPortThread := BeginThread(ForwardThread); - NetHost := enet_host_create(@NetAddr, NET_MAXCLIENTS, NET_CHANS, 0, 0); if (NetHost = nil) then @@ -1504,6 +1525,8 @@ begin Exit; end; + if NetForwardPorts then NetPortThread := BeginThread(ForwardThread); + NetPongSock := enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM); if NetPongSock <> ENET_SOCKET_NULL then begin @@ -1550,6 +1573,10 @@ begin enet_peer_reset(NetClients[I].Peer); NetClients[I].Peer := nil; NetClients[I].Used := False; + NetClients[I].Player := 0; + NetClients[I].Crimes := 0; + NetClients[I].AuthTime := 0; + NetClients[I].MsgTime := 0; NetClients[I].NetOut[NET_UNRELIABLE].Free(); NetClients[I].NetOut[NET_RELIABLE].Free(); end; @@ -1606,7 +1633,7 @@ var Ping: array [0..9] of Byte; NPl: Byte; begin - if NetPongSock = ENET_SOCKET_NULL then Exit; + if (NetPongSock = ENET_SOCKET_NULL) or (NetHost = nil) then Exit; Buf.data := Addr(Ping[0]); Buf.dataLength := 2+8; @@ -1640,6 +1667,32 @@ begin end; end; +procedure g_Net_Host_CheckTimeouts(); +var + ID: Integer; +begin + // auth timeout + for ID := Low(NetClients) to High(NetClients) do + begin + with NetClients[ID] do + begin + if (Peer = nil) or (State = NET_STATE_NONE) then continue; + if (State = NET_STATE_AUTH) and (AuthTime > 0) and (AuthTime <= gTime) then + begin + g_Net_Penalize(@NetClients[ID], 'auth taking too long'); + AuthTime := gTime + 18; // do it twice a second to give them a chance + end + else if (State = NET_STATE_GAME) and (MsgTime > 0) and (MsgTime <= gTime) then + begin + g_Net_Penalize(@NetClients[ID], 'message timeout'); + AuthTime := gTime + 18; // do it twice a second to give them a chance + end; + end; + end; + + +end; + function g_Net_Host_Update(): enet_size_t; var @@ -1654,6 +1707,7 @@ begin if NetUseMaster then g_Net_Slist_Pulse(); g_Net_Host_CheckPings(); + g_Net_Host_CheckTimeouts(); while (enet_host_service(NetHost, @NetEvent, 0) > 0) do begin @@ -1664,11 +1718,14 @@ begin Port := NetEvent.Peer^.address.port; g_Console_Add(_lc[I_NET_MSG] + Format(_lc[I_NET_MSG_HOST_CONN], [IP, Port])); + e_WriteLog('NET: Connection request from ' + IP + '.', TMsgType.Notify); if (NetEvent.data <> NET_PROTOCOL_VER) then begin g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] + _lc[I_NET_DISC_PROTOCOL]); + e_WriteLog('NET: Connection request from ' + IP + ' rejected: version mismatch', + TMsgType.Notify); NetEvent.peer^.data := GetMemory(SizeOf(Byte)); Byte(NetEvent.peer^.data^) := 255; enet_peer_disconnect(NetEvent.peer, NET_DISC_PROTOCOL); @@ -1676,12 +1733,27 @@ begin Exit; end; + if g_Net_IsHostBanned(NetEvent.Peer^.address.host) then + begin + g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] + + _lc[I_NET_DISC_BAN]); + e_WriteLog('NET: Connection request from ' + IP + ' rejected: banned', + TMsgType.Notify); + NetEvent.peer^.data := GetMemory(SizeOf(Byte)); + Byte(NetEvent.peer^.data^) := 255; + enet_peer_disconnect(NetEvent.Peer, NET_DISC_BAN); + enet_host_flush(NetHost); + Exit; + end; + ID := g_Net_FindSlot(); if ID < 0 then begin g_Console_Add(_lc[I_NET_MSG] + _lc[I_NET_MSG_HOST_REJECT] + _lc[I_NET_DISC_FULL]); + e_WriteLog('NET: Connection request from ' + IP + ' rejected: server full', + TMsgType.Notify); NetEvent.Peer^.data := GetMemory(SizeOf(Byte)); Byte(NetEvent.peer^.data^) := 255; enet_peer_disconnect(NetEvent.peer, NET_DISC_FULL); @@ -1693,9 +1765,19 @@ begin NetClients[ID].Peer^.data := GetMemory(SizeOf(Byte)); Byte(NetClients[ID].Peer^.data^) := ID; NetClients[ID].State := NET_STATE_AUTH; + NetClients[ID].Player := 0; + NetClients[ID].Crimes := 0; NetClients[ID].RCONAuth := False; NetClients[ID].NetOut[NET_UNRELIABLE].Alloc(NET_BUFSIZE*2); NetClients[ID].NetOut[NET_RELIABLE].Alloc(NET_BUFSIZE*2); + if (NetAuthTimeout > 0) then + NetClients[ID].AuthTime := gTime + NetAuthTimeout + else + NetClients[ID].AuthTime := 0; + if (NetPacketTimeout > 0) then + NetClients[ID].MsgTime := gTime + NetPacketTimeout + else + NetClients[ID].MsgTime := 0; clearNetClientTransfers(NetClients[ID]); // just in case enet_peer_timeout(NetEvent.peer, ENET_PEER_TIMEOUT_LIMIT * 2, ENET_PEER_TIMEOUT_MINIMUM * 2, ENET_PEER_TIMEOUT_MAXIMUM * 2); @@ -1717,6 +1799,9 @@ begin if ID > High(NetClients) then Exit; TC := @NetClients[ID]; + if (NetPacketTimeout > 0) then + TC^.MsgTime := gTime + NetPacketTimeout; + if NetDump then g_Net_DumpRecvBuffer(NetEvent.packet^.data, NetEvent.packet^.dataLength); g_Net_Host_HandlePacket(TC, NetEvent.packet, g_Net_HostMsgHandler); end; @@ -1747,6 +1832,9 @@ begin TC^.State := NET_STATE_NONE; TC^.Peer := nil; TC^.Player := 0; + TC^.Crimes := 0; + TC^.AuthTime := 0; + TC^.MsgTime := 0; TC^.RequestedFullUpdate := False; TC^.WaitForFirstSpawn := False; TC^.NetOut[NET_UNRELIABLE].Free(); @@ -2195,6 +2283,31 @@ begin end end; +procedure g_Net_Penalize(C: pTNetClient; Reason: string); +var + s: string; +begin + e_LogWritefln('NET: client #%u (cid #%u) triggered a penalty (%d/%d): %s', + [C^.ID, C^.Player, C^.Crimes, NetAutoBanLimit, Reason]); + + if (NetAutoBanLimit <= 0) then Exit; + + Inc(C^.Crimes); + + if (NetAutoBanWarn) then + MH_SEND_Chat('You have been warned by the server: ' + Reason, NET_CHAT_SYSTEM, C^.ID); + + if (C^.Crimes >= NetAutoBanLimit) then + begin + s := '#' + IntToStr(C^.ID); // can't be arsed + g_Net_BanHost(C^.Peer^.address.host, NetAutoBanPerm); + enet_peer_disconnect(C^.Peer, NET_DISC_BAN); + g_Console_Add(Format(_lc[I_PLAYER_BAN], [s])); + MH_SEND_GameEvent(NET_EV_PLAYER_BAN, 0, s); + g_Net_Slist_ServerPlayerLeaves(); + end; +end; + procedure g_Net_DumpStart(); begin if NetMode = NET_SERVER then @@ -2238,6 +2351,9 @@ var begin Result := False; + if NetHost = nil then + exit; + if NetPortForwarded = NetHost.address.port then begin Result := True; @@ -2436,6 +2552,7 @@ initialization conRegVar('cl_interp', @NetInterpLevel, '', 'net player interpolation steps'); conRegVar('cl_last_ip', @NetClientIP, '', 'address of the last you have connected to'); conRegVar('cl_last_port', @NetClientPort, '', 'port of the last server you have connected to'); + conRegVar('cl_deafen', @NetDeafLevel, '', 'filter server messages (0-3)'); conRegVar('sv_forwardports', @NetForwardPorts, '', 'forward server port using miniupnpc (requires server restart)'); conRegVar('sv_rcon', @NetAllowRCON, '', 'enable remote console'); @@ -2444,6 +2561,13 @@ initialization conRegVar('sv_reliable_interval', @NetRelupdRate, '', 'reliable update interval'); conRegVar('sv_master_interval', @NetMasterRate, '', 'master server update interval'); + conRegVar('sv_autoban_threshold', @NetAutoBanLimit, '', 'max crimes before autoban (0 = no autoban)'); + conRegVar('sv_autoban_permanent', @NetAutoBanPerm, '', 'whether autobans are permanent'); + conRegVar('sv_autoban_warn', @NetAutoBanWarn, '', 'send warnings to the client when he triggers penalties'); + + conRegVar('sv_auth_timeout', @NetAuthTimeout, '', 'number of frames in which connecting clients must complete auth (0 = unlimited)'); + conRegVar('sv_packet_timeout', @NetPacketTimeout, '', 'number of frames the client must idle to be kicked (0 = unlimited)'); + conRegVar('net_master_list', @NetMasterList, '', 'list of master servers'); SetLength(NetClients, 0);