Kicks are now temp bans

Length is determined by the "kicktime" cvar, in minutes. By default, this is set to 10, but I'm willing to adjust this. Only applies to manual kicks (in the future, maybe also name filter kicks).

The timestamp for the unban time is even saved in ban.txt, so long-term temporary bans are completely possible. (I checked, you can attempt to ban someone for up to 1902 years if you really want to.)

# Conflicts:
#	src/d_clisrv.c
#	src/d_clisrv.h
#	src/i_tcp.c
This commit is contained in:
toaster 2022-06-14 17:15:28 +01:00
parent 24181ae738
commit 8d05bf669b
5 changed files with 175 additions and 20 deletions

View file

@ -180,6 +180,8 @@ consvar_t cv_playbackspeed = {"playbackspeed", "1", 0, playbackspeed_cons_t, NUL
consvar_t cv_httpsource = {"http_source", "", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kicktime = CVAR_INIT ("kicktime", "10", CV_SAVE, CV_Unsigned, NULL);
static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
{
const size_t d = n / sizeof(ticcmd_t);
@ -2654,6 +2656,13 @@ static void Command_ShowBan(void) //Print out ban list
else
CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask);
if (I_GetUnbanTime && I_GetUnbanTime(i) != NO_BAN_TIME)
{
// todo: maybe try to actually print out the time remaining,
// and/or the datetime of the unbanning?
CONS_Printf("(temporary) ");
}
if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
CONS_Printf("(%s)\n", reason);
else
@ -2669,6 +2678,8 @@ void D_SaveBan(void)
FILE *f;
size_t i;
const char *address, *mask, *reason;
const time_t curTime = time(NULL);
time_t unbanTime = NO_BAN_TIME;
const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
f = fopen(path, "w");
@ -2681,11 +2692,24 @@ void D_SaveBan(void)
for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++)
{
if (I_GetUnbanTime)
{
unbanTime = I_GetUnbanTime(i);
}
if (unbanTime != NO_BAN_TIME && curTime >= unbanTime)
{
// Don't need to save this one anymore.
continue;
}
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
fprintf(f, "%s 0", address);
else
fprintf(f, "%s %s", address, mask);
fprintf(f, " %ld", (long)unbanTime);
if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
fprintf(f, " %s\n", reason);
else
@ -2709,6 +2733,7 @@ static void Ban_Load_File(boolean warning)
FILE *f;
size_t i;
const char *address, *mask, *reason;
time_t unbanTime = NO_BAN_TIME;
char buffer[MAX_WADPATH];
f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
@ -2726,9 +2751,12 @@ static void Ban_Load_File(boolean warning)
{
address = strtok(buffer, " \t\r\n");
mask = strtok(NULL, " \t\r\n");
unbanTime = atoi(strtok(NULL, " \t\r\n"));
reason = strtok(NULL, "\r\n");
I_SetBanAddress(address, mask);
if (I_SetUnbanTime)
I_SetUnbanTime(unbanTime);
if (I_SetBanReason)
I_SetBanReason(reason);
}
@ -3229,6 +3257,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
XBOXSTATIC char buf[3 + MAX_REASONLENGTH];
char *reason = buf;
kickreason_t kickreason = KR_KICK;
UINT32 banMinutes = 0;
pnum = READUINT8(*p);
msg = READUINT8(*p);
@ -3292,17 +3321,35 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
// Save bans here. Used to be split between here and the actual command, depending on
// whenever the server did it or a remote admin did it, but it's simply more convenient
// to keep it all in one place.
if (server && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN))
if (server)
{
if (I_Ban && !I_Ban(playernode[(INT32)pnum]))
if (msg == KICK_MSG_GO_AWAY || msg == KICK_MSG_CUSTOM_KICK)
{
CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n"));
// Kick as a temporary ban.
banMinutes = cv_kicktime.value;
}
else
if (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN || banMinutes)
{
if (I_SetBanReason)
I_SetBanReason(reason);
D_SaveBan();
if (I_Ban && !I_Ban(playernode[(INT32)pnum]))
{
CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n"));
}
else
{
if (I_SetBanReason)
I_SetBanReason(reason);
if (I_SetUnbanTime)
{
if (banMinutes)
I_SetUnbanTime(time(NULL) + (banMinutes * 60));
else
I_SetUnbanTime(NO_BAN_TIME);
}
D_SaveBan();
}
}
}
@ -3388,9 +3435,13 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
#ifdef DUMPCONSISTENCY
if (msg == KICK_MSG_CON_FAIL) SV_SavedGame();
#endif
LUAh_GameQuit(false);
D_QuitNetGame();
CL_Reset();
D_StartTitle();
if (msg == KICK_MSG_CON_FAIL)
M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING);
else if (msg == KICK_MSG_PING_HIGH)
@ -3566,6 +3617,7 @@ void D_ClientServerInit(void)
#ifndef NONET
COM_AddCommand("getplayernum", Command_GetPlayerNum);
COM_AddCommand("kick", Command_Kick);
CV_RegisterVar(&cv_kicktime);
COM_AddCommand("ban", Command_Ban);
COM_AddCommand("banip", Command_BanIP);
COM_AddCommand("clearbans", Command_ClearBans);
@ -4066,26 +4118,65 @@ static void HandleConnect(SINT8 node)
UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value);
if (bannednode && bannednode[node])
SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server"));
{
if (bannednodetimeleft && bannednodetimeleft[node] != NO_BAN_TIME)
{
int minutes = bannednodetimeleft[node] / 60;
int hours = minutes / 60;
if (hours)
{
SV_SendRefuse(node, va(M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: %d hour%s)"), hours, hours > 1 ? "s" : ""));
}
else if (minutes)
{
SV_SendRefuse(node, va(M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: %d minute%s)"), minutes, minutes > 1 ? "s" : ""));
}
else
{
SV_SendRefuse(node, M_GetText("You have been temporarily\nkicked from the server.\n(Time remaining: <1 minute)"));
}
}
else
{
SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server."));
}
}
else if (netbuffer->u.clientcfg._255 != 255 ||
netbuffer->u.clientcfg.packetversion != PACKETVERSION)
{
SV_SendRefuse(node, "Incompatible packet formats.");
}
else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION,
sizeof netbuffer->u.clientcfg.application))
SV_SendRefuse(node, "Different SRB2 modifications\nare not compatible.");
{
SV_SendRefuse(node, "Different SRB2Kart modifications\nare not compatible.");
}
else if (netbuffer->u.clientcfg.version != VERSION
|| netbuffer->u.clientcfg.subversion != SUBVERSION)
{
SV_SendRefuse(node, va(M_GetText("Different SRB2Kart versions cannot\nplay a netgame!\n(server version %d.%d)"), VERSION, SUBVERSION));
}
else if (!cv_allownewplayer.value && node)
SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment"));
{
SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment."));
}
else if (D_NumPlayers() >= maxplayers)
{
SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), maxplayers));
else if (netgame && D_NumPlayers() + netbuffer->u.clientcfg.localplayers > maxplayers)
SV_SendRefuse(node, va(M_GetText("Number of local players\nwould exceed maximum: %d"), maxplayers));
}
else if (netgame && netbuffer->u.clientcfg.localplayers > 4) // Hacked client?
{
SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
}
else if (netgame && D_NumPlayers() + netbuffer->u.clientcfg.localplayers > maxplayers)
{
SV_SendRefuse(node, va(M_GetText("Number of local players\nwould exceed maximum: %d"), maxplayers));
}
else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join?
{
SV_SendRefuse(node, M_GetText("No players from\nthis node."));
}
else
{
#ifndef NONET
@ -4319,13 +4410,13 @@ static void HandlePacketFromAwayNode(SINT8 node)
break;
}
M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
reason), NULL, MM_NOTHING);
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
reason), NULL, MM_NOTHING);
free(reason);
// Will be reset by caller. Signals refusal.

View file

@ -504,6 +504,8 @@ extern INT32 mapchangepending;
extern doomdata_t *netbuffer;
extern consvar_t cv_stunserver;
extern consvar_t cv_httpsource;
extern consvar_t cv_kicktime;
extern consvar_t cv_showjoinaddress;
extern consvar_t cv_playbackspeed;

View file

@ -80,9 +80,12 @@ const char *(*I_GetNodeAddress) (INT32 node) = NULL;
const char *(*I_GetBanAddress) (size_t ban) = NULL;
const char *(*I_GetBanMask) (size_t ban) = NULL;
const char *(*I_GetBanReason) (size_t ban) = NULL;
time_t (*I_GetUnbanTime) (size_t ban) = NULL;
boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL;
boolean (*I_SetBanReason) (const char *reason) = NULL;
boolean (*I_SetUnbanTime) (time_t timestamp) = NULL;
boolean *bannednode = NULL;
time_t *bannednodetimeleft = NULL;
// network stats

View file

@ -31,6 +31,8 @@
/// For use on the internet
#define INETPACKETLENGTH 1024
#define NO_BAN_TIME (time_t)(-1)
extern INT16 hardware_MAXPACKETLENGTH;
extern INT32 net_bandwidth; // in byte/s
@ -146,9 +148,12 @@ extern const char *(*I_GetNodeAddress) (INT32 node);
extern const char *(*I_GetBanAddress) (size_t ban);
extern const char *(*I_GetBanMask) (size_t ban);
extern const char *(*I_GetBanReason) (size_t ban);
extern time_t (*I_GetUnbanTime) (size_t ban);
extern boolean (*I_SetBanAddress) (const char *address,const char *mask);
extern boolean (*I_SetBanReason) (const char *reason);
extern boolean (*I_SetUnbanTime) (time_t timestamp);
extern boolean *bannednode;
extern time_t *bannednodetimeleft;
/// \brief Called by D_SRB2Main to be defined by extern network driver
boolean I_InitNetwork(void);

View file

@ -236,7 +236,7 @@ typedef struct
mysockaddr_t address;
UINT8 mask;
char *reason;
// TODO: timestamp, for tempbans!
time_t timestamp;
} banned_t;
static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET};
@ -254,6 +254,7 @@ static size_t numbans = 0;
static size_t banned_size = 0;
static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
static time_t SOCK_bannednodetimeleft[MAXNETNODES+1];
static boolean init_tcp_driver = false;
static const char *serverport_name = DEFAULTPORT;
@ -505,10 +506,24 @@ static const char *SOCK_GetBanReason(size_t ban)
(void)ban;
return NULL;
#else
if (ban >= numbans)
return NULL;
return banned[ban].reason;
#endif
}
static time_t SOCK_GetUnbanTime(size_t ban)
{
#ifdef NONET
(void)ban;
return NO_BAN_TIME;
#else
if (ban >= numbans)
return NO_BAN_TIME;
return banned[ban].timestamp;
#endif
}
#ifndef NONET
static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
{
@ -656,6 +671,8 @@ static boolean SOCK_Get(void)
j = getfreenode();
if (j > 0)
{
const time_t curTime = time(NULL);
M_Memcpy(&clientaddress[j], &fromaddress, fromlen);
nodesocket[j] = mysockets[n];
DEBFILE(va("New node detected: node:%d address:%s\n", j,
@ -668,13 +685,37 @@ static boolean SOCK_Get(void)
{
if (SOCK_cmpaddr(&fromaddress, &banned[i].address, banned[i].mask))
{
SOCK_bannednode[j] = true;
DEBFILE("This dude has been banned\n");
break;
if (banned[i].timestamp != NO_BAN_TIME)
{
if (curTime >= banned[i].timestamp)
{
SOCK_bannednodetimeleft[j] = NO_BAN_TIME;
SOCK_bannednode[j] = false;
DEBFILE("This dude was banned, but enough time has passed\n");
break;
}
SOCK_bannednodetimeleft[j] = banned[i].timestamp - curTime;
SOCK_bannednode[j] = true;
DEBFILE("This dude has been temporarily banned\n");
break;
}
else
{
SOCK_bannednodetimeleft[j] = NO_BAN_TIME;
SOCK_bannednode[j] = true;
DEBFILE("This dude has been banned\n");
break;
}
}
}
if (i == numbans)
{
SOCK_bannednodetimeleft[j] = NO_BAN_TIME;
SOCK_bannednode[j] = false;
}
return true;
}
else
@ -1538,7 +1579,6 @@ static boolean SOCK_SetBanReason(const char *reason)
(void)reason;
return false;
#else
if (!reason)
{
reason = "No reason given";
@ -1549,6 +1589,17 @@ static boolean SOCK_SetBanReason(const char *reason)
#endif
}
static boolean SOCK_SetUnbanTime(time_t timestamp)
{
#ifdef NONET
(void)reason;
return false;
#else
banned[numbans - 1].timestamp = timestamp;
return true;
#endif
}
static void SOCK_ClearBans(void)
{
numbans = 0;
@ -1646,9 +1697,12 @@ boolean I_InitTcpNetwork(void)
I_GetBanAddress = SOCK_GetBanAddress;
I_GetBanMask = SOCK_GetBanMask;
I_GetBanReason = SOCK_GetBanReason;
I_GetUnbanTime = SOCK_GetUnbanTime;
I_SetBanAddress = SOCK_SetBanAddress;
I_SetBanReason = SOCK_SetBanReason;
I_SetUnbanTime = SOCK_SetUnbanTime;
bannednode = SOCK_bannednode;
bannednodetimeleft = SOCK_bannednodetimeleft;
return ret;
}