From 55be74396fdfd1deb163e0c0e6b48c9c33f64e0f Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 14 Jun 2022 17:25:40 +0100 Subject: [PATCH] Ban improvements - Save a note of the username, not just the reason. - Allow setting a mask with the `banip` command. - Make ban.txt's formatting a lot more sane. Username and reason are stored in quotes. The mask uses the same formatting as actual CDIR. - Keep track of if we tried to load ban.txt. If it wasn't, then don't save over it with a blank file. - Disallow quotes in player names, as it makes player name detection in console more annoying, and saving username in files scary. # Conflicts: # src/d_clisrv.c # src/d_netcmd.c # src/i_tcp.c --- src/d_clisrv.c | 122 +++++++++++++++++++++++++++++++++++++++---------- src/d_net.c | 2 + src/d_net.h | 1 + src/d_netcmd.c | 23 +++++++++- src/i_net.h | 2 + src/i_tcp.c | 50 +++++++++++++++++++- 6 files changed, 172 insertions(+), 28 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ec2ce776..90f8cfa1 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2673,15 +2673,25 @@ static void Command_ShowBan(void) //Print out ban list CONS_Printf(M_GetText("(empty)\n")); } +static boolean bansLoaded = false; + void D_SaveBan(void) { FILE *f; size_t i; - const char *address, *mask, *reason; + 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 (bansLoaded != true) + { + // You didn't even get to ATTEMPT to load bans.txt. + // Don't immediately save nothing over it. + return; + } + f = fopen(path, "w"); if (!f) @@ -2696,24 +2706,37 @@ void D_SaveBan(void) { unbanTime = I_GetUnbanTime(i); } + else + { + unbanTime = NO_BAN_TIME; + } if (unbanTime != NO_BAN_TIME && curTime >= unbanTime) { - // Don't need to save this one anymore. + // 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) - fprintf(f, "%s 0", address); + fprintf(f, "%s/0", address); else - fprintf(f, "%s %s", address, mask); + fprintf(f, "%s/%s", address, mask); fprintf(f, " %ld", (long)unbanTime); - if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL) - fprintf(f, " %s\n", reason); + username = NULL; + if (I_GetBanUsername && (username = I_GetBanUsername(i)) != NULL) + fprintf(f, " \"%s\"", username); else - fprintf(f, " %s\n", "No reason given"); + fprintf(f, " \"%s\"", "Direct IP ban"); + + 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); @@ -2728,14 +2751,21 @@ static void Command_ClearBans(void) D_SaveBan(); } -static void Ban_Load_File(boolean warning) +void D_LoadBan(boolean warning) { FILE *f; size_t i; - const char *address, *mask, *reason; + const char *address, *mask; + const char *username, *reason; time_t unbanTime = NO_BAN_TIME; char buffer[MAX_WADPATH]; + if (!I_ClearBans) + return; + + // We at least attempted loading bans.txt + bansLoaded = true; + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); if (!f) @@ -2747,16 +2777,26 @@ static void Ban_Load_File(boolean warning) I_ClearBans(); - 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"); - unbanTime = atoi(strtok(NULL, " \t\r\n")); - reason = strtok(NULL, "\r\n"); + + unbanTime = atoi(strtok(NULL, " \"\t\r\n")); + + username = strtok(NULL, "\"\t\r\n"); // go until next " + + strtok(NULL, "\"\t\r\n"); // remove first " + reason = strtok(NULL, "\"\r\n"); // go until next " I_SetBanAddress(address, mask); + if (I_SetUnbanTime) I_SetUnbanTime(unbanTime); + + if (I_SetBanUsername) + I_SetBanUsername(username); + if (I_SetBanReason) I_SetBanReason(reason); } @@ -2766,7 +2806,7 @@ static void Ban_Load_File(boolean warning) static void Command_ReloadBan(void) //recheck ban.txt { - Ban_Load_File(true); + D_LoadBan(true); } static void Command_connect(void) @@ -3152,31 +3192,60 @@ static void Command_Ban(void) static void Command_BanIP(void) { - if (COM_Argc() < 2) + size_t ac = COM_Argc(); + + if (ac < 2) { - CONS_Printf(M_GetText("banip : ban an ip address\n")); + CONS_Printf(M_GetText("banip []: ban an ip address\n")); return; } if (server) // Only the server can use this, otherwise does nothing. { - const char *address = (COM_Argv(1)); - const char *reason; + char *addressInput = Z_StrDup(COM_Argv(1)); - if (COM_Argc() == 2) - reason = NULL; - else + const char *address = NULL; + const char *mask = NULL; + + const char *reason = NULL; + + address = strtok(addressInput, "/"); + mask = strtok(NULL, ""); + + if (ac > 2) + { reason = COM_Argv(2); + } - if (I_SetBanAddress && I_SetBanAddress(address, NULL)) + if (I_SetBanAddress && I_SetBanAddress(address, mask)) { 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 - 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); + D_SaveBan(); } else @@ -3337,6 +3406,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) } else { + if (I_SetBanUsername) + I_SetBanUsername(player_names[pnum]); + if (I_SetBanReason) I_SetBanReason(reason); @@ -3645,7 +3717,7 @@ void D_ClientServerInit(void) #ifdef DUMPCONSISTENCY CV_RegisterVar(&cv_dumpconsistency); #endif - Ban_Load_File(false); + D_LoadBan(false); #endif gametic = 0; diff --git a/src/d_net.c b/src/d_net.c index 70086ef2..264e7c8b 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -79,9 +79,11 @@ void (*I_ClearBans)(void) = NULL; 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_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_SetBanUsername) (const char *username) = NULL; boolean (*I_SetBanReason) (const char *reason) = NULL; boolean (*I_SetUnbanTime) (time_t timestamp) = NULL; boolean *bannednode = NULL; diff --git a/src/d_net.h b/src/d_net.h index 607f3e90..e267f526 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -55,6 +55,7 @@ boolean HGetPacket(void); void D_SetDoomcom(void); #ifndef NONET void D_SaveBan(void); +void D_LoadBan(boolean warning); #endif boolean D_CheckNetGame(void); void D_CloseConnection(void); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a192feda..aadd48aa 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1035,7 +1035,16 @@ void D_RegisterClientCommands(void) * \sa CleanupPlayerName, SetPlayerName, Got_NameAndColor * \author Graue */ -static boolean IsNameGood(char *name, INT32 playernum) + +static boolean AllowedPlayerNameChar(char ch) +{ + if (!isprint(ch) || ch == ';' || ch == '"' || (UINT8)(ch) >= 0x80) + return false; + + return true; +} + +boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) { INT32 ix; @@ -1056,7 +1065,7 @@ static boolean IsNameGood(char *name, INT32 playernum) // Also, anything over 0x80 is disallowed too, since compilers love to // differ on whether they're printable characters or not. for (ix = 0; name[ix] != '\0'; ix++) - if (!isprint(name[ix]) || name[ix] == ';' || (UINT8)(name[ix]) >= 0x80) + if (!AllowedPlayerNameChar(name[ix])) return false; // Check if a player is currently using the name, case-insensitively. @@ -1142,6 +1151,16 @@ static void CleanupPlayerName(INT32 playernum, const char *newname) tmpname = p; + do + { + if (!AllowedPlayerNameChar(*p)) + break; + } + while (*++p) ; + + if (*p)/* bad char found */ + break; + // Remove trailing spaces. p = &tmpname[strlen(tmpname)-1]; // last character while (*p == ' ' && p >= tmpname) diff --git a/src/i_net.h b/src/i_net.h index 9a2e11f4..155935f5 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -147,9 +147,11 @@ extern void (*I_ClearBans)(void); 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_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_SetBanUsername) (const char *username); extern boolean (*I_SetBanReason) (const char *reason); extern boolean (*I_SetUnbanTime) (time_t timestamp); extern boolean *bannednode; diff --git a/src/i_tcp.c b/src/i_tcp.c index f425bbce..25720b8e 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -235,6 +235,7 @@ typedef struct { mysockaddr_t address; UINT8 mask; + char *username; char *reason; time_t timestamp; } banned_t; @@ -500,6 +501,18 @@ static const char *SOCK_GetBanMask(size_t ban) 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 @@ -1564,6 +1577,11 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) banned[ban].mask = 128; #endif + // Set defaults, in case anything funny happens. + SOCK_SetBanUsername(NULL); + SOCK_SetBanReason(NULL); + SOCK_SetUnbanTime(NO_BAN_TIME); + runp = runp->ai_next; } @@ -1573,17 +1591,45 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) #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) + 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 @@ -1696,9 +1742,11 @@ boolean I_InitTcpNetwork(void) I_GetNodeAddress = SOCK_GetNodeAddress; I_GetBanAddress = SOCK_GetBanAddress; I_GetBanMask = SOCK_GetBanMask; + I_GetBanUsername = SOCK_GetBanUsername; I_GetBanReason = SOCK_GetBanReason; I_GetUnbanTime = SOCK_GetUnbanTime; I_SetBanAddress = SOCK_SetBanAddress; + I_SetBanUsername = SOCK_SetBanUsername; I_SetBanReason = SOCK_SetBanReason; I_SetUnbanTime = SOCK_SetUnbanTime; bannednode = SOCK_bannednode;