mirror of
https://git.do.srb2.org/KartKrew/Kart-Public.git
synced 2025-01-14 05:41:02 +00:00
Merge branch 'better-bans' into 'next'
Better bans See merge request KartKrew/Kart-Public!285
This commit is contained in:
commit
b1deb35564
8 changed files with 646 additions and 185 deletions
510
src/d_clisrv.c
510
src/d_clisrv.c
|
@ -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_httpsource = {"http_source", "", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||||
|
|
||||||
|
consvar_t cv_kicktime = {"kicktime", "10", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||||
|
|
||||||
static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
|
static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
|
||||||
{
|
{
|
||||||
const size_t d = n / sizeof(ticcmd_t);
|
const size_t d = n / sizeof(ticcmd_t);
|
||||||
|
@ -2637,57 +2639,87 @@ static void CL_ConnectToServer(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NONET
|
#ifndef NONET
|
||||||
typedef struct banreason_s
|
|
||||||
{
|
|
||||||
char *reason;
|
|
||||||
struct banreason_s *prev; //-1
|
|
||||||
struct banreason_s *next; //+1
|
|
||||||
} banreason_t;
|
|
||||||
|
|
||||||
static banreason_t *reasontail = NULL; //last entry, use prev
|
|
||||||
static banreason_t *reasonhead = NULL; //1st entry, use next
|
|
||||||
|
|
||||||
static void Command_ShowBan(void) //Print out ban list
|
static void Command_ShowBan(void) //Print out ban list
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
const char *address, *mask;
|
const char *address, *mask, *reason, *username;
|
||||||
banreason_t *reasonlist = reasonhead;
|
time_t unbanTime = NO_BAN_TIME;
|
||||||
|
const time_t curTime = time(NULL);
|
||||||
|
|
||||||
if (I_GetBanAddress)
|
if (I_GetBanAddress)
|
||||||
CONS_Printf(M_GetText("Ban List:\n"));
|
CONS_Printf(M_GetText("Ban List:\n"));
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
|
for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++)
|
||||||
{
|
{
|
||||||
|
unbanTime = NO_BAN_TIME;
|
||||||
|
if (I_GetUnbanTime)
|
||||||
|
unbanTime = I_GetUnbanTime(i);
|
||||||
|
|
||||||
|
if (unbanTime != NO_BAN_TIME && curTime >= unbanTime)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CONS_Printf("%s: ", sizeu1(i+1));
|
||||||
|
|
||||||
|
if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL)
|
||||||
|
CONS_Printf("%s - ", username);
|
||||||
|
|
||||||
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
|
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
|
||||||
CONS_Printf("%s: %s ", sizeu1(i+1), address);
|
CONS_Printf("%s", address);
|
||||||
else
|
else
|
||||||
CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask);
|
CONS_Printf("%s/%s", address, mask);
|
||||||
|
|
||||||
if (reasonlist && reasonlist->reason)
|
if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
|
||||||
CONS_Printf("(%s)\n", reasonlist->reason);
|
CONS_Printf(" - %s", reason);
|
||||||
else
|
|
||||||
CONS_Printf("\n");
|
|
||||||
|
|
||||||
if (reasonlist) reasonlist = reasonlist->next;
|
if (unbanTime != NO_BAN_TIME)
|
||||||
|
{
|
||||||
|
// these are fudged a little to match what a joiner sees
|
||||||
|
int minutes = ((unbanTime - curTime) + 30) / 60;
|
||||||
|
int hours = (minutes + 1) / 60;
|
||||||
|
int days = (hours + 1) / 24;
|
||||||
|
if (days)
|
||||||
|
CONS_Printf(" (%d day%s)", days, days > 1 ? "s" : "");
|
||||||
|
else if (hours)
|
||||||
|
CONS_Printf(" (%d hour%s)", hours, hours > 1 ? "s" : "");
|
||||||
|
else if (minutes)
|
||||||
|
CONS_Printf(" (%d minute%s)", minutes, minutes > 1 ? "s" : "");
|
||||||
|
else
|
||||||
|
CONS_Printf(" (<1 minute)");
|
||||||
|
}
|
||||||
|
|
||||||
|
CONS_Printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 0 && !address)
|
if (i == 0 && !address)
|
||||||
CONS_Printf(M_GetText("(empty)\n"));
|
CONS_Printf(M_GetText("(empty)\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean bansLoaded = false;
|
||||||
|
// If you're a community contributor looking to improve how bans are written, please
|
||||||
|
// offer your changes back to our Git repository. Kart Krew reserve the right to
|
||||||
|
// utilise format numbers in use by community builds for different layouts.
|
||||||
|
#define BANFORMAT 1
|
||||||
|
|
||||||
void D_SaveBan(void)
|
void D_SaveBan(void)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
size_t i;
|
size_t i;
|
||||||
banreason_t *reasonlist = reasonhead;
|
|
||||||
const char *address, *mask;
|
const char *address, *mask;
|
||||||
|
const char *username, *reason;
|
||||||
|
const time_t curTime = time(NULL);
|
||||||
|
time_t unbanTime = NO_BAN_TIME;
|
||||||
|
const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
|
||||||
|
|
||||||
if (!reasonhead)
|
if (bansLoaded != true)
|
||||||
|
{
|
||||||
|
// You didn't even get to ATTEMPT to load bans.txt.
|
||||||
|
// Don't immediately save nothing over it.
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "w");
|
f = fopen(path, "w");
|
||||||
|
|
||||||
if (!f)
|
if (!f)
|
||||||
{
|
{
|
||||||
|
@ -2695,67 +2727,76 @@ void D_SaveBan(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
|
// Add header.
|
||||||
|
fprintf(f, "BANFORMAT %d\n", BANFORMAT);
|
||||||
|
|
||||||
|
for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++)
|
||||||
{
|
{
|
||||||
|
if (I_GetUnbanTime)
|
||||||
|
{
|
||||||
|
unbanTime = I_GetUnbanTime(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unbanTime = NO_BAN_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unbanTime != NO_BAN_TIME && curTime >= unbanTime)
|
||||||
|
{
|
||||||
|
// This one has served their sentence.
|
||||||
|
// We don't need to save them in the file anymore.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask = NULL;
|
||||||
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
|
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
|
||||||
fprintf(f, "%s 0", address);
|
fprintf(f, "%s/0", address);
|
||||||
else
|
else
|
||||||
fprintf(f, "%s %s", address, mask);
|
fprintf(f, "%s/%s", address, mask);
|
||||||
|
|
||||||
if (reasonlist && reasonlist->reason)
|
fprintf(f, " %ld", (long)unbanTime);
|
||||||
fprintf(f, " %s\n", reasonlist->reason);
|
|
||||||
|
username = NULL;
|
||||||
|
if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL)
|
||||||
|
fprintf(f, " \"%s\"", username);
|
||||||
else
|
else
|
||||||
fprintf(f, " %s\n", "NA");
|
fprintf(f, " \"%s\"", "Direct IP ban");
|
||||||
|
|
||||||
if (reasonlist) reasonlist = reasonlist->next;
|
reason = NULL;
|
||||||
|
if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
|
||||||
|
fprintf(f, " \"%s\"\n", reason);
|
||||||
|
else
|
||||||
|
fprintf(f, " \"%s\"\n", "No reason given");
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Ban_Add(const char *reason)
|
|
||||||
{
|
|
||||||
banreason_t *reasonlist = malloc(sizeof(*reasonlist));
|
|
||||||
|
|
||||||
if (!reasonlist)
|
|
||||||
return;
|
|
||||||
if (!reason)
|
|
||||||
reason = "NA";
|
|
||||||
|
|
||||||
reasonlist->next = NULL;
|
|
||||||
reasonlist->reason = Z_StrDup(reason);
|
|
||||||
if ((reasonlist->prev = reasontail) == NULL)
|
|
||||||
reasonhead = reasonlist;
|
|
||||||
else
|
|
||||||
reasontail->next = reasonlist;
|
|
||||||
reasontail = reasonlist;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Command_ClearBans(void)
|
static void Command_ClearBans(void)
|
||||||
{
|
{
|
||||||
banreason_t *temp;
|
|
||||||
|
|
||||||
if (!I_ClearBans)
|
if (!I_ClearBans)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
I_ClearBans();
|
I_ClearBans();
|
||||||
D_SaveBan();
|
D_SaveBan();
|
||||||
reasontail = NULL;
|
|
||||||
while (reasonhead)
|
|
||||||
{
|
|
||||||
temp = reasonhead->next;
|
|
||||||
Z_Free(reasonhead->reason);
|
|
||||||
free(reasonhead);
|
|
||||||
reasonhead = temp;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Ban_Load_File(boolean warning)
|
void D_LoadBan(boolean warning)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
size_t i;
|
size_t i, j;
|
||||||
const char *address, *mask;
|
char *address, *mask;
|
||||||
|
char *username, *reason;
|
||||||
|
time_t unbanTime = NO_BAN_TIME;
|
||||||
char buffer[MAX_WADPATH];
|
char buffer[MAX_WADPATH];
|
||||||
|
UINT8 banmode = 0;
|
||||||
|
boolean malformed = false;
|
||||||
|
|
||||||
|
if (!I_ClearBans)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We at least attempted loading bans.txt
|
||||||
|
bansLoaded = true;
|
||||||
|
|
||||||
f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
|
f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
|
||||||
|
|
||||||
|
@ -2766,30 +2807,115 @@ static void Ban_Load_File(boolean warning)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (I_ClearBans)
|
I_ClearBans();
|
||||||
Command_ClearBans();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fclose(f);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
|
for (i = 0; fgets(buffer, (int)sizeof(buffer), f); i++)
|
||||||
{
|
{
|
||||||
address = strtok(buffer, " \t\r\n");
|
address = strtok(buffer, " /\t\r\n");
|
||||||
mask = strtok(NULL, " \t\r\n");
|
mask = strtok(NULL, " \t\r\n");
|
||||||
|
|
||||||
I_SetBanAddress(address, mask);
|
if (i == 0 && !strncmp(address, "BANFORMAT", 9))
|
||||||
|
{
|
||||||
|
if (mask)
|
||||||
|
{
|
||||||
|
banmode = atoi(mask);
|
||||||
|
}
|
||||||
|
switch (banmode)
|
||||||
|
{
|
||||||
|
case BANFORMAT: // currently supported format
|
||||||
|
//case 0: -- permitted only when BANFORMAT string not present
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
fclose(f);
|
||||||
|
CONS_Alert(CONS_WARNING, "Could not load unknown ban.txt for ban list (BANFORMAT %s, expected %d)\n", mask, BANFORMAT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Ban_Add(strtok(NULL, "\r\n"));
|
if (I_SetBanAddress(address, mask) == false) // invalid IP input?
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_WARNING, "\"%s/%s\" is not a valid IP address, discarding...\n", address, mask);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One-way legacy format conversion -- the game will crash otherwise
|
||||||
|
if (banmode == 0)
|
||||||
|
{
|
||||||
|
unbanTime = NO_BAN_TIME;
|
||||||
|
username = NULL; // not guaranteed to be accurate, but only sane substitute
|
||||||
|
reason = strtok(NULL, "\r\n");
|
||||||
|
if (reason && reason[0] == 'N' && reason[1] == 'A' && reason[2] == '\0')
|
||||||
|
{
|
||||||
|
reason = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reason = strtok(NULL, " \"\t\r\n");
|
||||||
|
if (reason)
|
||||||
|
{
|
||||||
|
unbanTime = atoi(reason);
|
||||||
|
reason = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unbanTime = NO_BAN_TIME;
|
||||||
|
malformed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
username = strtok(NULL, "\"\t\r\n"); // go until next "
|
||||||
|
if (!username)
|
||||||
|
{
|
||||||
|
malformed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
strtok(NULL, "\"\t\r\n"); // remove first "
|
||||||
|
reason = strtok(NULL, "\"\r\n"); // go until next "
|
||||||
|
if (!reason)
|
||||||
|
{
|
||||||
|
malformed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce MAX_REASONLENGTH.
|
||||||
|
if (reason)
|
||||||
|
{
|
||||||
|
j = 0;
|
||||||
|
while (reason[j] != '\0')
|
||||||
|
{
|
||||||
|
if ((j++) < MAX_REASONLENGTH)
|
||||||
|
continue;
|
||||||
|
reason[j] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (I_SetUnbanTime)
|
||||||
|
I_SetUnbanTime(unbanTime);
|
||||||
|
|
||||||
|
if (I_SetBanUsername)
|
||||||
|
I_SetBanUsername(username);
|
||||||
|
|
||||||
|
if (I_SetBanReason)
|
||||||
|
I_SetBanReason(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (malformed)
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_WARNING, "One or more lines of ban.txt are malformed. The game can correct for this, but some data may be lost.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef BANFORMAT
|
||||||
|
|
||||||
static void Command_ReloadBan(void) //recheck ban.txt
|
static void Command_ReloadBan(void) //recheck ban.txt
|
||||||
{
|
{
|
||||||
Ban_Load_File(true);
|
D_LoadBan(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Command_connect(void)
|
static void Command_connect(void)
|
||||||
|
@ -3125,7 +3251,7 @@ static void Command_Ban(void)
|
||||||
{
|
{
|
||||||
if (COM_Argc() < 2)
|
if (COM_Argc() < 2)
|
||||||
{
|
{
|
||||||
CONS_Printf(M_GetText("Ban <playername/playernum> <reason>: ban and kick a player\n"));
|
CONS_Printf(M_GetText("ban <playername/playernum> <reason>: ban and kick a player\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3140,83 +3266,95 @@ static void Command_Ban(void)
|
||||||
XBOXSTATIC UINT8 buf[3 + MAX_REASONLENGTH];
|
XBOXSTATIC UINT8 buf[3 + MAX_REASONLENGTH];
|
||||||
UINT8 *p = buf;
|
UINT8 *p = buf;
|
||||||
const SINT8 pn = nametonum(COM_Argv(1));
|
const SINT8 pn = nametonum(COM_Argv(1));
|
||||||
const INT32 node = playernode[(INT32)pn];
|
|
||||||
|
|
||||||
if (pn == -1 || pn == 0)
|
if (pn == -1 || pn == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
WRITEUINT8(p, pn);
|
WRITEUINT8(p, pn);
|
||||||
|
|
||||||
if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now
|
if (COM_Argc() == 2)
|
||||||
{
|
{
|
||||||
CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
|
WRITEUINT8(p, KICK_MSG_BANNED);
|
||||||
WRITEUINT8(p, KICK_MSG_GO_AWAY);
|
|
||||||
SendNetXCmd(XD_KICK, &buf, 2);
|
SendNetXCmd(XD_KICK, &buf, 2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (server) // only the server is allowed to do this right now
|
size_t i, j = COM_Argc();
|
||||||
|
char message[MAX_REASONLENGTH];
|
||||||
|
|
||||||
|
//Steal from the motd code so you don't have to put the reason in quotes.
|
||||||
|
strlcpy(message, COM_Argv(2), sizeof message);
|
||||||
|
for (i = 3; i < j; i++)
|
||||||
{
|
{
|
||||||
Ban_Add(COM_Argv(2));
|
strlcat(message, " ", sizeof message);
|
||||||
D_SaveBan(); // save the ban list
|
strlcat(message, COM_Argv(i), sizeof message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (COM_Argc() == 2)
|
WRITEUINT8(p, KICK_MSG_CUSTOM_BAN);
|
||||||
{
|
WRITESTRINGN(p, message, MAX_REASONLENGTH);
|
||||||
WRITEUINT8(p, KICK_MSG_BANNED);
|
SendNetXCmd(XD_KICK, &buf, p - buf);
|
||||||
SendNetXCmd(XD_KICK, &buf, 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t i, j = COM_Argc();
|
|
||||||
char message[MAX_REASONLENGTH];
|
|
||||||
|
|
||||||
//Steal from the motd code so you don't have to put the reason in quotes.
|
|
||||||
strlcpy(message, COM_Argv(2), sizeof message);
|
|
||||||
for (i = 3; i < j; i++)
|
|
||||||
{
|
|
||||||
strlcat(message, " ", sizeof message);
|
|
||||||
strlcat(message, COM_Argv(i), sizeof message);
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITEUINT8(p, KICK_MSG_CUSTOM_BAN);
|
|
||||||
WRITESTRINGN(p, message, MAX_REASONLENGTH);
|
|
||||||
SendNetXCmd(XD_KICK, &buf, p - buf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
|
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Command_BanIP(void)
|
static void Command_BanIP(void)
|
||||||
{
|
{
|
||||||
if (COM_Argc() < 2)
|
size_t ac = COM_Argc();
|
||||||
|
|
||||||
|
if (ac < 2)
|
||||||
{
|
{
|
||||||
CONS_Printf(M_GetText("banip <ip> <reason>: ban an ip address\n"));
|
CONS_Printf(M_GetText("banip <ip> [<reason>]: ban an ip address\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server) // Only the server can use this, otherwise does nothing.
|
if (server) // Only the server can use this, otherwise does nothing.
|
||||||
{
|
{
|
||||||
const char *address = (COM_Argv(1));
|
char *addressInput = Z_StrDup(COM_Argv(1));
|
||||||
const char *reason;
|
|
||||||
|
|
||||||
if (COM_Argc() == 2)
|
const char *address = NULL;
|
||||||
reason = NULL;
|
const char *mask = NULL;
|
||||||
else
|
|
||||||
|
const char *reason = NULL;
|
||||||
|
|
||||||
|
address = strtok(addressInput, "/");
|
||||||
|
mask = strtok(NULL, "");
|
||||||
|
|
||||||
|
if (ac > 2)
|
||||||
|
{
|
||||||
reason = COM_Argv(2);
|
reason = COM_Argv(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (I_SetBanAddress && I_SetBanAddress(address, mask))
|
||||||
if (I_SetBanAddress && I_SetBanAddress(address, NULL))
|
|
||||||
{
|
{
|
||||||
if (reason)
|
if (reason)
|
||||||
CONS_Printf("Banned IP address %s for: %s\n", address, reason);
|
{
|
||||||
|
CONS_Printf(
|
||||||
|
"Banned IP address %s%s for: %s\n",
|
||||||
|
address,
|
||||||
|
(mask && (strlen(mask) > 0)) ? va("/%s", mask) : "",
|
||||||
|
reason
|
||||||
|
);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
CONS_Printf("Banned IP address %s\n", address);
|
{
|
||||||
|
CONS_Printf(
|
||||||
|
"Banned IP address %s%s\n",
|
||||||
|
address,
|
||||||
|
(mask && (strlen(mask) > 0)) ? va("/%s", mask) : ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (I_SetUnbanTime)
|
||||||
|
I_SetUnbanTime(NO_BAN_TIME);
|
||||||
|
|
||||||
|
if (I_SetBanUsername)
|
||||||
|
I_SetBanUsername(NULL);
|
||||||
|
|
||||||
|
if (I_SetBanReason)
|
||||||
|
I_SetBanReason(reason);
|
||||||
|
|
||||||
Ban_Add(reason);
|
|
||||||
D_SaveBan();
|
D_SaveBan();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -3297,6 +3435,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
|
||||||
XBOXSTATIC char buf[3 + MAX_REASONLENGTH];
|
XBOXSTATIC char buf[3 + MAX_REASONLENGTH];
|
||||||
char *reason = buf;
|
char *reason = buf;
|
||||||
kickreason_t kickreason = KR_KICK;
|
kickreason_t kickreason = KR_KICK;
|
||||||
|
UINT32 banMinutes = 0;
|
||||||
|
|
||||||
pnum = READUINT8(*p);
|
pnum = READUINT8(*p);
|
||||||
msg = READUINT8(*p);
|
msg = READUINT8(*p);
|
||||||
|
@ -3355,18 +3494,49 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
|
||||||
msg = KICK_MSG_CON_FAIL;
|
msg = KICK_MSG_CON_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg == KICK_MSG_CUSTOM_BAN || msg == KICK_MSG_CUSTOM_KICK)
|
||||||
|
{
|
||||||
|
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
|
||||||
|
}
|
||||||
|
|
||||||
//CONS_Printf("\x82%s ", player_names[pnum]);
|
//CONS_Printf("\x82%s ", player_names[pnum]);
|
||||||
|
|
||||||
// If a verified admin banned someone, the server needs to know about it.
|
// Save bans here. Used to be split between here and the actual command, depending on
|
||||||
// If the playernum isn't zero (the server) then the server needs to record the ban.
|
// whenever the server did it or a remote admin did it, but it's simply more convenient
|
||||||
if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN))
|
// to keep it all in one place.
|
||||||
|
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("Too many bans! Geez, that's a lot of people you're excluding...\n"));
|
{
|
||||||
#ifndef NONET
|
// Kick as a temporary ban.
|
||||||
else
|
banMinutes = cv_kicktime.value;
|
||||||
Ban_Add(reason);
|
}
|
||||||
#endif
|
|
||||||
|
if (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN || banMinutes)
|
||||||
|
{
|
||||||
|
if (I_Ban && !I_Ban(playernode[(INT32)pnum]))
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (I_SetBanUsername)
|
||||||
|
I_SetBanUsername(player_names[pnum]);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg == KICK_MSG_PLAYER_QUIT)
|
if (msg == KICK_MSG_PLAYER_QUIT)
|
||||||
|
@ -3435,12 +3605,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
|
||||||
kickreason = KR_BAN;
|
kickreason = KR_BAN;
|
||||||
break;
|
break;
|
||||||
case KICK_MSG_CUSTOM_KICK:
|
case KICK_MSG_CUSTOM_KICK:
|
||||||
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
|
|
||||||
HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false);
|
HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false);
|
||||||
kickreason = KR_KICK;
|
kickreason = KR_KICK;
|
||||||
break;
|
break;
|
||||||
case KICK_MSG_CUSTOM_BAN:
|
case KICK_MSG_CUSTOM_BAN:
|
||||||
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
|
|
||||||
HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false);
|
HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false);
|
||||||
kickreason = KR_BAN;
|
kickreason = KR_BAN;
|
||||||
break;
|
break;
|
||||||
|
@ -3454,6 +3622,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
|
||||||
D_QuitNetGame();
|
D_QuitNetGame();
|
||||||
CL_Reset();
|
CL_Reset();
|
||||||
D_StartTitle();
|
D_StartTitle();
|
||||||
|
|
||||||
if (msg == KICK_MSG_CON_FAIL)
|
if (msg == KICK_MSG_CON_FAIL)
|
||||||
M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING);
|
M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING);
|
||||||
else if (msg == KICK_MSG_PING_HIGH)
|
else if (msg == KICK_MSG_PING_HIGH)
|
||||||
|
@ -3461,9 +3630,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
|
||||||
else if (msg == KICK_MSG_BANNED)
|
else if (msg == KICK_MSG_BANNED)
|
||||||
M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
|
M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
|
||||||
else if (msg == KICK_MSG_CUSTOM_KICK)
|
else if (msg == KICK_MSG_CUSTOM_KICK)
|
||||||
M_StartMessage(va(M_GetText("You have been kicked\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING);
|
M_StartMessage(va(M_GetText("You have been kicked\n(%s)\n\nPress ESC\n"), reason), NULL, MM_NOTHING);
|
||||||
else if (msg == KICK_MSG_CUSTOM_BAN)
|
else if (msg == KICK_MSG_CUSTOM_BAN)
|
||||||
M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING);
|
M_StartMessage(va(M_GetText("You have been banned\n(%s)\n\nPress ESC\n"), reason), NULL, MM_NOTHING);
|
||||||
else
|
else
|
||||||
M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
|
M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
|
||||||
}
|
}
|
||||||
|
@ -3629,6 +3798,7 @@ void D_ClientServerInit(void)
|
||||||
#ifndef NONET
|
#ifndef NONET
|
||||||
COM_AddCommand("getplayernum", Command_GetPlayerNum);
|
COM_AddCommand("getplayernum", Command_GetPlayerNum);
|
||||||
COM_AddCommand("kick", Command_Kick);
|
COM_AddCommand("kick", Command_Kick);
|
||||||
|
CV_RegisterVar(&cv_kicktime);
|
||||||
COM_AddCommand("ban", Command_Ban);
|
COM_AddCommand("ban", Command_Ban);
|
||||||
COM_AddCommand("banip", Command_BanIP);
|
COM_AddCommand("banip", Command_BanIP);
|
||||||
COM_AddCommand("clearbans", Command_ClearBans);
|
COM_AddCommand("clearbans", Command_ClearBans);
|
||||||
|
@ -3656,7 +3826,7 @@ void D_ClientServerInit(void)
|
||||||
#ifdef DUMPCONSISTENCY
|
#ifdef DUMPCONSISTENCY
|
||||||
CV_RegisterVar(&cv_dumpconsistency);
|
CV_RegisterVar(&cv_dumpconsistency);
|
||||||
#endif
|
#endif
|
||||||
Ban_Load_File(false);
|
D_LoadBan(false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gametic = 0;
|
gametic = 0;
|
||||||
|
@ -3681,6 +3851,8 @@ static void ResetNode(INT32 node)
|
||||||
nodewaiting[node] = 0;
|
nodewaiting[node] = 0;
|
||||||
playerpernode[node] = 0;
|
playerpernode[node] = 0;
|
||||||
sendingsavegame[node] = false;
|
sendingsavegame[node] = false;
|
||||||
|
bannednode[node].banid = SIZE_MAX;
|
||||||
|
bannednode[node].timeleft = NO_BAN_TIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SV_ResetServer(void)
|
void SV_ResetServer(void)
|
||||||
|
@ -4128,27 +4300,78 @@ static void HandleConnect(SINT8 node)
|
||||||
// It's too much effort to legimately fix right now. Just prevent it from reaching that state.
|
// It's too much effort to legimately fix right now. Just prevent it from reaching that state.
|
||||||
UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value);
|
UINT8 maxplayers = min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value);
|
||||||
|
|
||||||
if (bannednode && bannednode[node])
|
if (bannednode && bannednode[node].banid != SIZE_MAX)
|
||||||
SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server"));
|
{
|
||||||
|
const char *reason = NULL;
|
||||||
|
|
||||||
|
// Get the reason...
|
||||||
|
if (!I_GetBanReason || (reason = I_GetBanReason(bannednode[node].banid)) == NULL)
|
||||||
|
reason = "No reason given";
|
||||||
|
|
||||||
|
if (bannednode[node].timeleft != NO_BAN_TIME)
|
||||||
|
{
|
||||||
|
// these are fudged a little to allow it to sink in for impatient rejoiners
|
||||||
|
int minutes = (bannednode[node].timeleft + 30) / 60;
|
||||||
|
int hours = (minutes + 1) / 60;
|
||||||
|
int days = (hours + 1) / 24;
|
||||||
|
|
||||||
|
if (days)
|
||||||
|
{
|
||||||
|
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d day%s)", reason, days, days > 1 ? "s" : ""));
|
||||||
|
}
|
||||||
|
else if (hours)
|
||||||
|
{
|
||||||
|
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d hour%s)", reason, hours, hours > 1 ? "s" : ""));
|
||||||
|
}
|
||||||
|
else if (minutes)
|
||||||
|
{
|
||||||
|
SV_SendRefuse(node, va("K|%s\n(Time remaining: %d minute%s)", reason, minutes, minutes > 1 ? "s" : ""));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SV_SendRefuse(node, va("K|%s\n(Time remaining: <1 minute)", reason));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SV_SendRefuse(node, va("B|%s", reason));
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (netbuffer->u.clientcfg._255 != 255 ||
|
else if (netbuffer->u.clientcfg._255 != 255 ||
|
||||||
netbuffer->u.clientcfg.packetversion != PACKETVERSION)
|
netbuffer->u.clientcfg.packetversion != PACKETVERSION)
|
||||||
|
{
|
||||||
SV_SendRefuse(node, "Incompatible packet formats.");
|
SV_SendRefuse(node, "Incompatible packet formats.");
|
||||||
|
}
|
||||||
else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION,
|
else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION,
|
||||||
sizeof netbuffer->u.clientcfg.application))
|
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
|
else if (netbuffer->u.clientcfg.version != VERSION
|
||||||
|| netbuffer->u.clientcfg.subversion != SUBVERSION)
|
|| 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));
|
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)
|
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)
|
else if (D_NumPlayers() >= maxplayers)
|
||||||
|
{
|
||||||
SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), 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?
|
else if (netgame && netbuffer->u.clientcfg.localplayers > 4) // Hacked client?
|
||||||
|
{
|
||||||
SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
|
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?
|
else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join?
|
||||||
|
{
|
||||||
SV_SendRefuse(node, M_GetText("No players from\nthis node."));
|
SV_SendRefuse(node, M_GetText("No players from\nthis node."));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifndef NONET
|
#ifndef NONET
|
||||||
|
@ -4382,13 +4605,22 @@ static void HandlePacketFromAwayNode(SINT8 node)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
|
|
||||||
reason), NULL, MM_NOTHING);
|
|
||||||
|
|
||||||
D_QuitNetGame();
|
D_QuitNetGame();
|
||||||
CL_Reset();
|
CL_Reset();
|
||||||
D_StartTitle();
|
D_StartTitle();
|
||||||
|
|
||||||
|
if (reason[1] == '|')
|
||||||
|
{
|
||||||
|
M_StartMessage(va("You have been %sfrom the server\n\nReason:\n%s",
|
||||||
|
(reason[0] == 'B') ? "banned\n" : "temporarily\nkicked ",
|
||||||
|
reason+2), NULL, MM_NOTHING);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
|
||||||
|
reason), NULL, MM_NOTHING);
|
||||||
|
}
|
||||||
|
|
||||||
free(reason);
|
free(reason);
|
||||||
|
|
||||||
// Will be reset by caller. Signals refusal.
|
// Will be reset by caller. Signals refusal.
|
||||||
|
|
|
@ -504,6 +504,8 @@ extern INT32 mapchangepending;
|
||||||
extern doomdata_t *netbuffer;
|
extern doomdata_t *netbuffer;
|
||||||
extern consvar_t cv_stunserver;
|
extern consvar_t cv_stunserver;
|
||||||
extern consvar_t cv_httpsource;
|
extern consvar_t cv_httpsource;
|
||||||
|
extern consvar_t cv_kicktime;
|
||||||
|
|
||||||
extern consvar_t cv_showjoinaddress;
|
extern consvar_t cv_showjoinaddress;
|
||||||
extern consvar_t cv_playbackspeed;
|
extern consvar_t cv_playbackspeed;
|
||||||
|
|
||||||
|
|
|
@ -79,8 +79,14 @@ void (*I_ClearBans)(void) = NULL;
|
||||||
const char *(*I_GetNodeAddress) (INT32 node) = NULL;
|
const char *(*I_GetNodeAddress) (INT32 node) = NULL;
|
||||||
const char *(*I_GetBanAddress) (size_t ban) = NULL;
|
const char *(*I_GetBanAddress) (size_t ban) = NULL;
|
||||||
const char *(*I_GetBanMask) (size_t ban) = NULL;
|
const char *(*I_GetBanMask) (size_t ban) = NULL;
|
||||||
|
const char *(*I_GetBanUsername) (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_SetBanAddress) (const char *address, const char *mask) = NULL;
|
||||||
boolean *bannednode = NULL;
|
boolean (*I_SetBanUsername) (const char *username) = NULL;
|
||||||
|
boolean (*I_SetBanReason) (const char *reason) = NULL;
|
||||||
|
boolean (*I_SetUnbanTime) (time_t timestamp) = NULL;
|
||||||
|
bannednode_t *bannednode = NULL;
|
||||||
|
|
||||||
|
|
||||||
// network stats
|
// network stats
|
||||||
|
|
|
@ -55,6 +55,7 @@ boolean HGetPacket(void);
|
||||||
void D_SetDoomcom(void);
|
void D_SetDoomcom(void);
|
||||||
#ifndef NONET
|
#ifndef NONET
|
||||||
void D_SaveBan(void);
|
void D_SaveBan(void);
|
||||||
|
void D_LoadBan(boolean warning);
|
||||||
#endif
|
#endif
|
||||||
boolean D_CheckNetGame(void);
|
boolean D_CheckNetGame(void);
|
||||||
void D_CloseConnection(void);
|
void D_CloseConnection(void);
|
||||||
|
|
|
@ -1035,7 +1035,16 @@ void D_RegisterClientCommands(void)
|
||||||
* \sa CleanupPlayerName, SetPlayerName, Got_NameAndColor
|
* \sa CleanupPlayerName, SetPlayerName, Got_NameAndColor
|
||||||
* \author Graue <graue@oceanbase.org>
|
* \author Graue <graue@oceanbase.org>
|
||||||
*/
|
*/
|
||||||
static boolean IsNameGood(char *name, INT32 playernum)
|
|
||||||
|
static boolean AllowedPlayerNameChar(char ch)
|
||||||
|
{
|
||||||
|
if (!isprint(ch) || ch == ';' || ch == '"' || (UINT8)(ch) >= 0x80)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean EnsurePlayerNameIsGood(char *name, INT32 playernum)
|
||||||
{
|
{
|
||||||
INT32 ix;
|
INT32 ix;
|
||||||
|
|
||||||
|
@ -1056,7 +1065,7 @@ static boolean IsNameGood(char *name, INT32 playernum)
|
||||||
// Also, anything over 0x80 is disallowed too, since compilers love to
|
// Also, anything over 0x80 is disallowed too, since compilers love to
|
||||||
// differ on whether they're printable characters or not.
|
// differ on whether they're printable characters or not.
|
||||||
for (ix = 0; name[ix] != '\0'; ix++)
|
for (ix = 0; name[ix] != '\0'; ix++)
|
||||||
if (!isprint(name[ix]) || name[ix] == ';' || (UINT8)(name[ix]) >= 0x80)
|
if (!AllowedPlayerNameChar(name[ix]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check if a player is currently using the name, case-insensitively.
|
// Check if a player is currently using the name, case-insensitively.
|
||||||
|
@ -1076,14 +1085,14 @@ static boolean IsNameGood(char *name, INT32 playernum)
|
||||||
if (len > 1)
|
if (len > 1)
|
||||||
{
|
{
|
||||||
name[len-1] = '\0';
|
name[len-1] = '\0';
|
||||||
if (!IsNameGood (name, playernum))
|
if (!EnsurePlayerNameIsGood(name, playernum))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (len == 1) // Agh!
|
else if (len == 1) // Agh!
|
||||||
{
|
{
|
||||||
// Last ditch effort...
|
// Last ditch effort...
|
||||||
sprintf(name, "%d", M_RandomKey(10));
|
sprintf(name, "%d", M_RandomKey(10));
|
||||||
if (!IsNameGood (name, playernum))
|
if (!EnsurePlayerNameIsGood(name, playernum))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1142,6 +1151,16 @@ static void CleanupPlayerName(INT32 playernum, const char *newname)
|
||||||
|
|
||||||
tmpname = p;
|
tmpname = p;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!AllowedPlayerNameChar(*p))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (*++p) ;
|
||||||
|
|
||||||
|
if (*p)/* bad char found */
|
||||||
|
break;
|
||||||
|
|
||||||
// Remove trailing spaces.
|
// Remove trailing spaces.
|
||||||
p = &tmpname[strlen(tmpname)-1]; // last character
|
p = &tmpname[strlen(tmpname)-1]; // last character
|
||||||
while (*p == ' ' && p >= tmpname)
|
while (*p == ' ' && p >= tmpname)
|
||||||
|
@ -1213,12 +1232,12 @@ static void CleanupPlayerName(INT32 playernum, const char *newname)
|
||||||
* \param newname New name for that player. Should be good, but won't
|
* \param newname New name for that player. Should be good, but won't
|
||||||
* necessarily be if the client is maliciously modified or
|
* necessarily be if the client is maliciously modified or
|
||||||
* buggy.
|
* buggy.
|
||||||
* \sa CleanupPlayerName, IsNameGood
|
* \sa CleanupPlayerName, EnsurePlayerNameIsGood
|
||||||
* \author Graue <graue@oceanbase.org>
|
* \author Graue <graue@oceanbase.org>
|
||||||
*/
|
*/
|
||||||
static void SetPlayerName(INT32 playernum, char *newname)
|
static void SetPlayerName(INT32 playernum, char *newname)
|
||||||
{
|
{
|
||||||
if (IsNameGood(newname, playernum))
|
if (EnsurePlayerNameIsGood(newname, playernum))
|
||||||
{
|
{
|
||||||
if (strcasecmp(newname, player_names[playernum]) != 0)
|
if (strcasecmp(newname, player_names[playernum]) != 0)
|
||||||
{
|
{
|
||||||
|
|
16
src/i_net.h
16
src/i_net.h
|
@ -31,6 +31,8 @@
|
||||||
/// For use on the internet
|
/// For use on the internet
|
||||||
#define INETPACKETLENGTH 1024
|
#define INETPACKETLENGTH 1024
|
||||||
|
|
||||||
|
#define NO_BAN_TIME (time_t)(-1)
|
||||||
|
|
||||||
extern INT16 hardware_MAXPACKETLENGTH;
|
extern INT16 hardware_MAXPACKETLENGTH;
|
||||||
extern INT32 net_bandwidth; // in byte/s
|
extern INT32 net_bandwidth; // in byte/s
|
||||||
|
|
||||||
|
@ -145,8 +147,20 @@ extern void (*I_ClearBans)(void);
|
||||||
extern const char *(*I_GetNodeAddress) (INT32 node);
|
extern const char *(*I_GetNodeAddress) (INT32 node);
|
||||||
extern const char *(*I_GetBanAddress) (size_t ban);
|
extern const char *(*I_GetBanAddress) (size_t ban);
|
||||||
extern const char *(*I_GetBanMask) (size_t ban);
|
extern const char *(*I_GetBanMask) (size_t ban);
|
||||||
|
extern const char *(*I_GetBanUsername) (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_SetBanAddress) (const char *address,const char *mask);
|
||||||
extern boolean *bannednode;
|
extern boolean (*I_SetBanUsername) (const char *username);
|
||||||
|
extern boolean (*I_SetBanReason) (const char *reason);
|
||||||
|
extern boolean (*I_SetUnbanTime) (time_t timestamp);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
size_t banid;
|
||||||
|
time_t timeleft;
|
||||||
|
} bannednode_t;
|
||||||
|
extern bannednode_t *bannednode;
|
||||||
|
|
||||||
/// \brief Called by D_SRB2Main to be defined by extern network driver
|
/// \brief Called by D_SRB2Main to be defined by extern network driver
|
||||||
boolean I_InitNetwork(void);
|
boolean I_InitNetwork(void);
|
||||||
|
|
259
src/i_tcp.c
259
src/i_tcp.c
|
@ -174,8 +174,6 @@ static UINT8 UPNP_support = TRUE;
|
||||||
|
|
||||||
#endif // !NONET
|
#endif // !NONET
|
||||||
|
|
||||||
#define MAXBANS 100
|
|
||||||
|
|
||||||
#include "i_system.h"
|
#include "i_system.h"
|
||||||
#include "i_net.h"
|
#include "i_net.h"
|
||||||
#include "d_net.h"
|
#include "d_net.h"
|
||||||
|
@ -183,6 +181,7 @@ static UINT8 UPNP_support = TRUE;
|
||||||
#include "i_tcp.h"
|
#include "i_tcp.h"
|
||||||
#include "m_argv.h"
|
#include "m_argv.h"
|
||||||
#include "stun.h"
|
#include "stun.h"
|
||||||
|
#include "z_zone.h"
|
||||||
|
|
||||||
#include "doomstat.h"
|
#include "doomstat.h"
|
||||||
|
|
||||||
|
@ -231,6 +230,16 @@ typedef int socklen_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NONET
|
#ifndef NONET
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
mysockaddr_t address;
|
||||||
|
UINT8 mask;
|
||||||
|
char *username;
|
||||||
|
char *reason;
|
||||||
|
time_t timestamp;
|
||||||
|
} banned_t;
|
||||||
|
|
||||||
static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET};
|
static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET};
|
||||||
static size_t mysocketses = 0;
|
static size_t mysocketses = 0;
|
||||||
static int myfamily[MAXNETNODES+1] = {0};
|
static int myfamily[MAXNETNODES+1] = {0};
|
||||||
|
@ -239,12 +248,13 @@ static mysockaddr_t clientaddress[MAXNETNODES+1];
|
||||||
static mysockaddr_t broadcastaddress[MAXNETNODES+1];
|
static mysockaddr_t broadcastaddress[MAXNETNODES+1];
|
||||||
static size_t broadcastaddresses = 0;
|
static size_t broadcastaddresses = 0;
|
||||||
static boolean nodeconnected[MAXNETNODES+1];
|
static boolean nodeconnected[MAXNETNODES+1];
|
||||||
static mysockaddr_t banned[MAXBANS];
|
static banned_t *banned;
|
||||||
static UINT8 bannedmask[MAXBANS];
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static size_t numbans = 0;
|
static size_t numbans = 0;
|
||||||
static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
|
static size_t banned_size = 0;
|
||||||
|
|
||||||
|
static bannednode_t SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
|
||||||
static boolean init_tcp_driver = false;
|
static boolean init_tcp_driver = false;
|
||||||
|
|
||||||
static const char *serverport_name = DEFAULTPORT;
|
static const char *serverport_name = DEFAULTPORT;
|
||||||
|
@ -472,7 +482,7 @@ static const char *SOCK_GetBanAddress(size_t ban)
|
||||||
#ifdef NONET
|
#ifdef NONET
|
||||||
return NULL;
|
return NULL;
|
||||||
#else
|
#else
|
||||||
return SOCK_AddrToStr(&banned[ban]);
|
return SOCK_AddrToStr(&banned[ban].address);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,12 +494,48 @@ static const char *SOCK_GetBanMask(size_t ban)
|
||||||
static char s[16]; //255.255.255.255 netmask? no, just CDIR for only
|
static char s[16]; //255.255.255.255 netmask? no, just CDIR for only
|
||||||
if (ban >= numbans)
|
if (ban >= numbans)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (sprintf(s,"%d",bannedmask[ban]) > 0)
|
if (sprintf(s,"%d",banned[ban].mask) > 0)
|
||||||
return s;
|
return s;
|
||||||
#endif
|
#endif
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *SOCK_GetBanUsername(size_t ban)
|
||||||
|
{
|
||||||
|
#ifdef NONET
|
||||||
|
(void)ban;
|
||||||
|
return NULL;
|
||||||
|
#else
|
||||||
|
if (ban >= numbans)
|
||||||
|
return NULL;
|
||||||
|
return banned[ban].username;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *SOCK_GetBanReason(size_t ban)
|
||||||
|
{
|
||||||
|
#ifdef NONET
|
||||||
|
(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
|
#ifndef NONET
|
||||||
static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
|
static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
|
||||||
{
|
{
|
||||||
|
@ -637,6 +683,8 @@ static boolean SOCK_Get(void)
|
||||||
j = getfreenode();
|
j = getfreenode();
|
||||||
if (j > 0)
|
if (j > 0)
|
||||||
{
|
{
|
||||||
|
const time_t curTime = time(NULL);
|
||||||
|
|
||||||
M_Memcpy(&clientaddress[j], &fromaddress, fromlen);
|
M_Memcpy(&clientaddress[j], &fromaddress, fromlen);
|
||||||
nodesocket[j] = mysockets[n];
|
nodesocket[j] = mysockets[n];
|
||||||
DEBFILE(va("New node detected: node:%d address:%s\n", j,
|
DEBFILE(va("New node detected: node:%d address:%s\n", j,
|
||||||
|
@ -647,15 +695,39 @@ static boolean SOCK_Get(void)
|
||||||
// check if it's a banned dude so we can send a refusal later
|
// check if it's a banned dude so we can send a refusal later
|
||||||
for (i = 0; i < numbans; i++)
|
for (i = 0; i < numbans; i++)
|
||||||
{
|
{
|
||||||
if (SOCK_cmpaddr(&fromaddress, &banned[i], bannedmask[i]))
|
if (SOCK_cmpaddr(&fromaddress, &banned[i].address, banned[i].mask))
|
||||||
{
|
{
|
||||||
SOCK_bannednode[j] = true;
|
if (banned[i].timestamp != NO_BAN_TIME)
|
||||||
DEBFILE("This dude has been banned\n");
|
{
|
||||||
break;
|
if (curTime >= banned[i].timestamp)
|
||||||
|
{
|
||||||
|
SOCK_bannednode[j].timeleft = NO_BAN_TIME;
|
||||||
|
SOCK_bannednode[j].banid = SIZE_MAX;
|
||||||
|
DEBFILE("This dude was banned, but enough time has passed\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCK_bannednode[j].timeleft = banned[i].timestamp - curTime;
|
||||||
|
SOCK_bannednode[j].banid = i;
|
||||||
|
DEBFILE("This dude has been temporarily banned\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SOCK_bannednode[j].timeleft = NO_BAN_TIME;
|
||||||
|
SOCK_bannednode[j].banid = i;
|
||||||
|
DEBFILE("This dude has been banned\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == numbans)
|
if (i == numbans)
|
||||||
SOCK_bannednode[j] = false;
|
{
|
||||||
|
SOCK_bannednode[j].timeleft = NO_BAN_TIME;
|
||||||
|
SOCK_bannednode[j].banid = SIZE_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1395,30 +1467,116 @@ static boolean SOCK_OpenSocket(void)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void AddBannedIndex(void)
|
||||||
|
{
|
||||||
|
if (numbans >= banned_size)
|
||||||
|
{
|
||||||
|
if (banned_size == 0)
|
||||||
|
{
|
||||||
|
banned_size = 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
banned_size *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
banned = Z_ReallocAlign(
|
||||||
|
(void*) banned,
|
||||||
|
sizeof(banned_t) * banned_size,
|
||||||
|
PU_STATIC,
|
||||||
|
NULL,
|
||||||
|
sizeof(banned_t) * 8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
numbans++;
|
||||||
|
}
|
||||||
|
|
||||||
static boolean SOCK_Ban(INT32 node)
|
static boolean SOCK_Ban(INT32 node)
|
||||||
{
|
{
|
||||||
|
INT32 ban;
|
||||||
|
|
||||||
if (node > MAXNETNODES)
|
if (node > MAXNETNODES)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef NONET
|
#ifdef NONET
|
||||||
|
(void)ban;
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
if (numbans == MAXBANS)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
M_Memcpy(&banned[numbans], &clientaddress[node], sizeof (mysockaddr_t));
|
ban = numbans;
|
||||||
if (banned[numbans].any.sa_family == AF_INET)
|
AddBannedIndex();
|
||||||
|
|
||||||
|
M_Memcpy(&banned[ban].address, &clientaddress[node], sizeof (mysockaddr_t));
|
||||||
|
|
||||||
|
if (banned[ban].address.any.sa_family == AF_INET)
|
||||||
{
|
{
|
||||||
banned[numbans].ip4.sin_port = 0;
|
banned[ban].address.ip4.sin_port = 0;
|
||||||
bannedmask[numbans] = 32;
|
banned[ban].mask = 32;
|
||||||
}
|
}
|
||||||
#ifdef HAVE_IPV6
|
#ifdef HAVE_IPV6
|
||||||
else if (banned[numbans].any.sa_family == AF_INET6)
|
else if (banned[ban].address.any.sa_family == AF_INET6)
|
||||||
{
|
{
|
||||||
banned[numbans].ip6.sin6_port = 0;
|
banned[ban].address.ip6.sin6_port = 0;
|
||||||
bannedmask[numbans] = 128;
|
banned[ban].mask = 128;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
numbans++;
|
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean SOCK_SetBanUsername(const char *username)
|
||||||
|
{
|
||||||
|
#ifdef NONET
|
||||||
|
(void)username;
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
if (username == NULL || strlen(username) == 0)
|
||||||
|
{
|
||||||
|
username = "Direct IP ban";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (banned[numbans - 1].username)
|
||||||
|
{
|
||||||
|
Z_Free(banned[numbans - 1].username);
|
||||||
|
banned[numbans - 1].username = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
banned[numbans - 1].username = Z_StrDup(username);
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean SOCK_SetBanReason(const char *reason)
|
||||||
|
{
|
||||||
|
#ifdef NONET
|
||||||
|
(void)reason;
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
if (reason == NULL || strlen(reason) == 0)
|
||||||
|
{
|
||||||
|
reason = "No reason given";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (banned[numbans - 1].reason)
|
||||||
|
{
|
||||||
|
Z_Free(banned[numbans - 1].reason);
|
||||||
|
banned[numbans - 1].reason = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
banned[numbans - 1].reason = Z_StrDup(reason);
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean SOCK_SetUnbanTime(time_t timestamp)
|
||||||
|
{
|
||||||
|
#ifdef NONET
|
||||||
|
(void)reason;
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
banned[numbans - 1].timestamp = timestamp;
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -1433,7 +1591,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
|
||||||
struct my_addrinfo *ai, *runp, hints;
|
struct my_addrinfo *ai, *runp, hints;
|
||||||
int gaie;
|
int gaie;
|
||||||
|
|
||||||
if (numbans == MAXBANS || !address)
|
if (!address)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
memset(&hints, 0x00, sizeof(hints));
|
memset(&hints, 0x00, sizeof(hints));
|
||||||
|
@ -1448,26 +1606,42 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
|
||||||
|
|
||||||
runp = ai;
|
runp = ai;
|
||||||
|
|
||||||
while(runp != NULL && numbans != MAXBANS)
|
while (runp != NULL)
|
||||||
{
|
{
|
||||||
memcpy(&banned[numbans], runp->ai_addr, runp->ai_addrlen);
|
INT32 ban;
|
||||||
|
UINT8 numericalmask;
|
||||||
|
|
||||||
|
ban = numbans;
|
||||||
|
AddBannedIndex();
|
||||||
|
|
||||||
|
memcpy(&banned[ban].address, runp->ai_addr, runp->ai_addrlen);
|
||||||
|
|
||||||
|
#ifdef HAVE_IPV6
|
||||||
|
if (runp->ai_family == AF_INET6)
|
||||||
|
banned[ban].mask = 128;
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
banned[ban].mask = 32;
|
||||||
|
|
||||||
if (mask)
|
if (mask)
|
||||||
bannedmask[numbans] = (UINT8)atoi(mask);
|
{
|
||||||
#ifdef HAVE_IPV6
|
numericalmask = (UINT8)atoi(mask);
|
||||||
else if (runp->ai_family == AF_INET6)
|
}
|
||||||
bannedmask[numbans] = 128;
|
|
||||||
#endif
|
|
||||||
else
|
else
|
||||||
bannedmask[numbans] = 32;
|
{
|
||||||
|
numericalmask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numericalmask > 0 && numericalmask < banned[ban].mask)
|
||||||
|
{
|
||||||
|
banned[ban].mask = numericalmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set defaults, in case anything funny happens.
|
||||||
|
SOCK_SetBanUsername(NULL);
|
||||||
|
SOCK_SetBanReason(NULL);
|
||||||
|
SOCK_SetUnbanTime(NO_BAN_TIME);
|
||||||
|
|
||||||
if (bannedmask[numbans] > 32 && runp->ai_family == AF_INET)
|
|
||||||
bannedmask[numbans] = 32;
|
|
||||||
#ifdef HAVE_IPV6
|
|
||||||
else if (bannedmask[numbans] > 128 && runp->ai_family == AF_INET6)
|
|
||||||
bannedmask[numbans] = 128;
|
|
||||||
#endif
|
|
||||||
numbans++;
|
|
||||||
runp = runp->ai_next;
|
runp = runp->ai_next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1480,6 +1654,9 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
|
||||||
static void SOCK_ClearBans(void)
|
static void SOCK_ClearBans(void)
|
||||||
{
|
{
|
||||||
numbans = 0;
|
numbans = 0;
|
||||||
|
banned_size = 0;
|
||||||
|
Z_Free(banned);
|
||||||
|
banned = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean I_InitTcpNetwork(void)
|
boolean I_InitTcpNetwork(void)
|
||||||
|
@ -1571,7 +1748,13 @@ boolean I_InitTcpNetwork(void)
|
||||||
I_GetNodeAddress = SOCK_GetNodeAddress;
|
I_GetNodeAddress = SOCK_GetNodeAddress;
|
||||||
I_GetBanAddress = SOCK_GetBanAddress;
|
I_GetBanAddress = SOCK_GetBanAddress;
|
||||||
I_GetBanMask = SOCK_GetBanMask;
|
I_GetBanMask = SOCK_GetBanMask;
|
||||||
|
I_GetBanUsername = SOCK_GetBanUsername;
|
||||||
|
I_GetBanReason = SOCK_GetBanReason;
|
||||||
|
I_GetUnbanTime = SOCK_GetUnbanTime;
|
||||||
I_SetBanAddress = SOCK_SetBanAddress;
|
I_SetBanAddress = SOCK_SetBanAddress;
|
||||||
|
I_SetBanUsername = SOCK_SetBanUsername;
|
||||||
|
I_SetBanReason = SOCK_SetBanReason;
|
||||||
|
I_SetUnbanTime = SOCK_SetUnbanTime;
|
||||||
bannednode = SOCK_bannednode;
|
bannednode = SOCK_bannednode;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -4567,7 +4567,11 @@ void M_StartMessage(const char *string, void *routine,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == strlen(message+start))
|
if (i == strlen(message+start))
|
||||||
|
{
|
||||||
start += i;
|
start += i;
|
||||||
|
if (i > max)
|
||||||
|
max = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDef.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2);
|
MessageDef.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2);
|
||||||
|
|
Loading…
Reference in a new issue