diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 1e31de517aa1fcd86f6601271d909afe9df59a62..65f3e8b79565293a63137200a932969313d0cc91 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2637,40 +2637,27 @@ static void CL_ConnectToServer(void)
 }
 
 #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
 {
 	size_t i;
-	const char *address, *mask;
-	banreason_t *reasonlist = reasonhead;
+	const char *address, *mask, *reason;
 
 	if (I_GetBanAddress)
 		CONS_Printf(M_GetText("Ban List:\n"));
 	else
 		return;
 
-	for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
+	for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++)
 	{
 		if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
 			CONS_Printf("%s: %s ", sizeu1(i+1), address);
 		else
 			CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask);
 
-		if (reasonlist && reasonlist->reason)
-			CONS_Printf("(%s)\n", reasonlist->reason);
+		if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
+			CONS_Printf("(%s)\n", reason);
 		else
 			CONS_Printf("\n");
-
-		if (reasonlist) reasonlist = reasonlist->next;
 	}
 
 	if (i == 0 && !address)
@@ -2681,13 +2668,10 @@ void D_SaveBan(void)
 {
 	FILE *f;
 	size_t i;
-	banreason_t *reasonlist = reasonhead;
-	const char *address, *mask;
+	const char *address, *mask, *reason;
+	const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt");
 
-	if (!reasonhead)
-		return;
-
-	f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "w");
+	f = fopen(path, "w");
 
 	if (!f)
 	{
@@ -2695,66 +2679,36 @@ void D_SaveBan(void)
 		return;
 	}
 
-	for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
+	for (i = 0; (address = I_GetBanAddress(i)) != NULL; i++)
 	{
 		if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
 			fprintf(f, "%s 0", address);
 		else
 			fprintf(f, "%s %s", address, mask);
 
-		if (reasonlist && reasonlist->reason)
-			fprintf(f, " %s\n", reasonlist->reason);
+		if (I_GetBanReason && (reason = I_GetBanReason(i)) != NULL)
+			fprintf(f, " %s\n", reason);
 		else
-			fprintf(f, " %s\n", "NA");
-
-		if (reasonlist) reasonlist = reasonlist->next;
+			fprintf(f, " %s\n", "No reason given");
 	}
 
 	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)
 {
-	banreason_t *temp;
-
 	if (!I_ClearBans)
 		return;
 
 	I_ClearBans();
 	D_SaveBan();
-	reasontail = NULL;
-	while (reasonhead)
-	{
-		temp = reasonhead->next;
-		Z_Free(reasonhead->reason);
-		free(reasonhead);
-		reasonhead = temp;
-	}
 }
 
 static void Ban_Load_File(boolean warning)
 {
 	FILE *f;
 	size_t i;
-	const char *address, *mask;
+	const char *address, *mask, *reason;
 	char buffer[MAX_WADPATH];
 
 	f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
@@ -2766,22 +2720,17 @@ static void Ban_Load_File(boolean warning)
 		return;
 	}
 
-	if (I_ClearBans)
-		Command_ClearBans();
-	else
-	{
-		fclose(f);
-		return;
-	}
+	I_ClearBans();
 
 	for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
 	{
 		address = strtok(buffer, " \t\r\n");
 		mask = strtok(NULL, " \t\r\n");
+		reason = strtok(NULL, "\r\n");
 
 		I_SetBanAddress(address, mask);
-
-		Ban_Add(strtok(NULL, "\r\n"));
+		if (I_SetBanReason)
+			I_SetBanReason(reason);
 	}
 
 	fclose(f);
@@ -3140,54 +3089,37 @@ static void Command_Ban(void)
 		XBOXSTATIC UINT8 buf[3 + MAX_REASONLENGTH];
 		UINT8 *p = buf;
 		const SINT8 pn = nametonum(COM_Argv(1));
-		const INT32 node = playernode[(INT32)pn];
 
 		if (pn == -1 || pn == 0)
 			return;
 
 		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("Ban failed. Invalid node?\n"));
-			WRITEUINT8(p, KICK_MSG_GO_AWAY);
+			WRITEUINT8(p, KICK_MSG_BANNED);
 			SendNetXCmd(XD_KICK, &buf, 2);
 		}
 		else
 		{
-			if (server) // only the server is allowed to do this right now
-			{
-				Ban_Add(COM_Argv(2));
-				D_SaveBan(); // save the ban list
-			}
+			size_t i, j = COM_Argc();
+			char message[MAX_REASONLENGTH];
 
-			if (COM_Argc() == 2)
+			//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++)
 			{
-				WRITEUINT8(p, KICK_MSG_BANNED);
-				SendNetXCmd(XD_KICK, &buf, 2);
+				strlcat(message, " ", sizeof message);
+				strlcat(message, COM_Argv(i), sizeof message);
 			}
-			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);
-			}
+			WRITEUINT8(p, KICK_MSG_CUSTOM_BAN);
+			WRITESTRINGN(p, message, MAX_REASONLENGTH);
+			SendNetXCmd(XD_KICK, &buf, p - buf);
 		}
 	}
 	else
 		CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
-
 }
 
 static void Command_BanIP(void)
@@ -3208,7 +3140,6 @@ static void Command_BanIP(void)
 		else
 			reason = COM_Argv(2);
 
-
 		if (I_SetBanAddress && I_SetBanAddress(address, NULL))
 		{
 			if (reason)
@@ -3216,7 +3147,8 @@ static void Command_BanIP(void)
 			else
 				CONS_Printf("Banned IP address %s\n", address);
 
-			Ban_Add(reason);
+			if (I_SetBanReason)
+				I_SetBanReason(reason);
 			D_SaveBan();
 		}
 		else
@@ -3357,16 +3289,21 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 
 	//CONS_Printf("\x82%s ", player_names[pnum]);
 
-	// If a verified admin banned someone, the server needs to know about it.
-	// If the playernum isn't zero (the server) then the server needs to record the ban.
-	if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN))
+	// 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 (I_Ban && !I_Ban(playernode[(INT32)pnum]))
-			CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
-#ifndef NONET
+		{
+			CONS_Alert(CONS_WARNING, M_GetText("Ban failed. Invalid node?\n"));
+		}
 		else
-			Ban_Add(reason);
-#endif
+		{
+			if (I_SetBanReason)
+				I_SetBanReason(reason);
+			D_SaveBan();
+		}
 	}
 
 	if (msg == KICK_MSG_PLAYER_QUIT)
diff --git a/src/d_net.c b/src/d_net.c
index d974d6cff59c4d9b31d0fb5aa7c75bb94f29d28f..b28aab7a068a29a273d78eb0f76b3dcb43c5b3ae 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -79,7 +79,9 @@ 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_GetBanReason) (size_t ban) = NULL;
 boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL;
+boolean (*I_SetBanReason) (const char *reason) = NULL;
 boolean *bannednode = NULL;
 
 
diff --git a/src/i_net.h b/src/i_net.h
index 0e17077bbcea85fcd80e9c7fc830d981745ba218..3608d81bc98b26a1ac905787d435660bea7fc0fe 100644
--- a/src/i_net.h
+++ b/src/i_net.h
@@ -145,7 +145,9 @@ 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_GetBanReason) (size_t ban);
 extern boolean (*I_SetBanAddress) (const char *address,const char *mask);
+extern boolean (*I_SetBanReason) (const char *reason);
 extern boolean *bannednode;
 
 /// \brief Called by D_SRB2Main to be defined by extern network driver
diff --git a/src/i_tcp.c b/src/i_tcp.c
index d4bf5b874f25fb9b10dbdc0e79d4ba4e3baafa94..75e3aaa6d7c0b7d93f93a352c31619bc27f648af 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -235,6 +235,7 @@ typedef struct
 {
 	mysockaddr_t address;
 	UINT8 mask;
+	char *reason;
 	// TODO: timestamp, for tempbans!
 } banned_t;
 
@@ -498,6 +499,16 @@ static const char *SOCK_GetBanMask(size_t ban)
 	return NULL;
 }
 
+static const char *SOCK_GetBanReason(size_t ban)
+{
+#ifdef NONET
+	(void)ban;
+	return NULL;
+#else
+	return banned[ban].reason;
+#endif
+}
+
 #ifndef NONET
 static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
 {
@@ -1521,6 +1532,23 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
 #endif
 }
 
+static boolean SOCK_SetBanReason(const char *reason)
+{
+#ifdef NONET
+	(void)reason;
+	return false;
+#else
+
+	if (!reason)
+	{
+		reason = "No reason given";
+	}
+
+	banned[numbans - 1].reason = Z_StrDup(reason);
+	return true;
+#endif
+}
+
 static void SOCK_ClearBans(void)
 {
 	numbans = 0;
@@ -1617,7 +1645,9 @@ boolean I_InitTcpNetwork(void)
 	I_GetNodeAddress = SOCK_GetNodeAddress;
 	I_GetBanAddress = SOCK_GetBanAddress;
 	I_GetBanMask = SOCK_GetBanMask;
+	I_GetBanReason = SOCK_GetBanReason;
 	I_SetBanAddress = SOCK_SetBanAddress;
+	I_SetBanReason = SOCK_SetBanReason;
 	bannednode = SOCK_bannednode;
 
 	return ret;