diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c
index e2ef97551..03b31d72f 100644
--- a/engine/common/net_wins.c
+++ b/engine/common/net_wins.c
@@ -580,7 +580,7 @@ qboolean	NET_StringToAdr (char *s, netadr_t *a)
 // (bits < 0 will always fill all bits)
 void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)
 {
-	int i;
+	unsigned int i;
 	qbyte *n;
 
 	memset (amask, 0, sizeof(*amask));
@@ -608,7 +608,8 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)
 		// fill last bit
 		if (i)
 		{
-			i = (~((1 << i) - 1)) & 0xFF;
+			i = 8 - i;
+			i = 255 - ((1 << i) - 1);
 			*n = i;
 		}
 		break;
@@ -628,7 +629,8 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)
 		// fill last bit
 		if (i)
 		{
-			i = (~((1 << i) - 1)) & 0xFF;
+			i = 8 - i;
+			i = 255 - ((1 << i) - 1);
 			*n = i;
 		}
 		break;
@@ -647,13 +649,62 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)
 		// fill last bit
 		if (i)
 		{
-			i = (~((1 << i) - 1)) & 0xFF;
+			i = 8 - i;
+			i = 255 - ((1 << i) - 1);
 			*n = i;
 		}
 		break;
 	}
 }
 
+// ParsePartialIPv4: check string to see if it is a partial IPv4 address and
+// return bits to mask and set netadr_t or 0 if not an address
+int ParsePartialIPv4(char *s, netadr_t *a)
+{
+	char *colon = NULL;
+	char *address = a->address.ip;
+	int bits = 8;
+
+	if (!*s)
+		return 0;
+
+	memset (a, 0, sizeof(*a));
+	while (*s)
+	{
+		if (*s == ':')
+		{
+			if (colon) // only 1 colon
+				return 0;
+			colon = s + 1;
+		}
+		else if (*s == '.')
+		{
+			if (colon) // no colons before periods (probably invalid anyway)
+				return 0;
+			else if (bits >= 32) // only 32 bits in ipv4
+				return 0; 
+			else if (*(s+1) == '.') 
+				return 0;
+			else if (*(s+1) == '\0')
+				break; // don't add more bits to the mask for x.x., etc
+			bits += 8;
+			address++;
+		}
+		else if (*s >= '0' && *s <= '9')
+			*address = ((*address)*10) + (*s-'0');
+		else
+			return 0; // invalid character
+
+		s++;
+	}
+
+	a->type = NA_IP;
+	if (colon)
+		a->port = atoi(colon);
+
+	return bits;
+}
+
 // NET_StringToAdrMasked: extension to NET_StringToAdr to handle IP addresses
 // with masks or integers representing the bit masks
 qboolean NET_StringToAdrMasked (char *s, netadr_t *a, netadr_t *amask)
@@ -669,12 +720,12 @@ qboolean NET_StringToAdrMasked (char *s, netadr_t *a, netadr_t *amask)
 		// we have a slash in the address so split and resolve separately
 		char *c;
 
-		i = spoint - s;
-		if (i + 1 > sizeof(t))
+		i = (int)(spoint - s) + 1;
+		if (i > sizeof(t))
 			i = sizeof(t);
 
 		Q_strncpyz(t, s, i);
-		if (!NET_StringToAdr(t, a))
+		if (!ParsePartialIPv4(t, a) && !NET_StringToAdr(t, a))
 			return false;
 		spoint++;
 		
@@ -693,7 +744,7 @@ qboolean NET_StringToAdrMasked (char *s, netadr_t *a, netadr_t *amask)
 		}
 
 		if (c == NULL) // we have an address so resolve it and return
-			return NET_StringToAdr(spoint, amask);
+			return ParsePartialIPv4(spoint, amask) || NET_StringToAdr(spoint, amask);
 
 		// otherwise generate mask for given bits
 		i = atoi(spoint);
@@ -702,13 +753,17 @@ qboolean NET_StringToAdrMasked (char *s, netadr_t *a, netadr_t *amask)
 	else
 	{
 		// we don't have a slash, resolve and fill with a full mask
-		if (!NET_StringToAdr(s, a))
+		i = ParsePartialIPv4(s, a);
+		if (!i && !NET_StringToAdr(s, a))
 			return false;
 
 		memset (amask, 0, sizeof(*amask));
 		amask->type = a->type;
 
-		NET_IntegerToMask(a, amask, -1);
+		if (i)
+			NET_IntegerToMask(a, amask, i);
+		else
+			NET_IntegerToMask(a, amask, -1);
 	}
 
 	return true;
@@ -774,7 +829,7 @@ int UniformMaskedBits(netadr_t mask)
 {
 	int bits;
 	int b;
-	int bs;
+	unsigned int bs;
 	qboolean bitenc = false;
 
 	switch (mask.type)
@@ -788,7 +843,7 @@ int UniformMaskedBits(netadr_t mask)
 				bitenc = true;
 			else if (mask.address.ip[b])
 			{
-				bs = ~mask.address.ip[b];
+				bs = (~mask.address.ip[b]) & 0xFF;
 				while (bs)
 				{
 					if (bs & 1)
@@ -818,7 +873,7 @@ int UniformMaskedBits(netadr_t mask)
 				bitenc = true;
 			else if (mask.address.ip6[b])
 			{
-				bs = ~mask.address.ip6[b];
+				bs = (~mask.address.ip6[b]) & 0xFF;
 				while (bs)
 				{
 					if (bs & 1)
@@ -849,7 +904,7 @@ int UniformMaskedBits(netadr_t mask)
 				bitenc = true;
 			else if (mask.address.ipx[b])
 			{
-				bs = ~mask.address.ipx[b];
+				bs = (~mask.address.ipx[b]) & 0xFF;
 				while (bs)
 				{
 					if (bs & 1)
@@ -887,7 +942,13 @@ char	*NET_AdrToStringMasked (netadr_t a, netadr_t amask)
 	if (i >= 0)
 		sprintf(s, "%s/%i", NET_AdrToString(a), i);
 	else
-		sprintf(s, "%s/%s", NET_AdrToString(a), NET_AdrToString(amask));
+	{
+		// has to be done this way due to NET_AdrToString returning a
+		// static address
+		Q_strncatz(s, NET_AdrToString(a), sizeof(s));
+		Q_strncatz(s, "/", sizeof(s));
+		Q_strncatz(s, NET_AdrToString(amask), sizeof(s));
+	}
 
 	return s;
 }
diff --git a/engine/server/server.h b/engine/server/server.h
index 81be511d3..40ce2e77a 100644
--- a/engine/server/server.h
+++ b/engine/server/server.h
@@ -651,8 +651,15 @@ typedef struct bannedips_s {
 	struct bannedips_s *next;
 	netadr_t	adr;
 	netadr_t	adrmask;
+	char reason[1];
 } bannedips_t;
 
+typedef struct filteredip_s {
+	struct filteredip_s *next;
+	netadr_t	adr;
+	netadr_t	adrmask;
+} filteredips_t;
+
 typedef enum {
 	GT_PROGS,	//q1, qw, h2 are similar enough that we consider it only one game mode. (We don't support the h2 protocol)
 	GT_QUAKE2,	//q2 servers run from a q2 game dll
@@ -711,6 +718,7 @@ typedef struct
 	challenge_t	challenges[MAX_CHALLENGES];	// to prevent invalid IPs from connecting
 
 	bannedips_t *bannedips;
+	filteredips_t *filteredips;
 
 	char progsnames[MAX_PROGS][32];
 	progsnum_t progsnum[MAX_PROGS];
diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c
index ac89d381e..3d9dcafbc 100644
--- a/engine/server/sv_ccmds.c
+++ b/engine/server/sv_ccmds.c
@@ -638,13 +638,21 @@ void SV_BanName_f (void)
 {
 	client_t	*cl;
 	int clnum=-1;
+	char *reason = NULL;
+	int reasonsize = 0;
 
 	if (Cmd_Argc() < 2)
 	{
-		Con_Printf("%s userid|nick\n", Cmd_Argv(0));
+		Con_Printf("%s userid|nick [reason]\n", Cmd_Argv(0));
 		return;
 	}
 
+	if (Cmd_Argc() > 2)
+	{
+		reason = Cmd_Argv(2);
+		reasonsize = strlen(reason);
+	}
+
 	while((cl = SV_GetClientForString(Cmd_Argv(1), &clnum)))
 	if (cl)
 	{
@@ -656,13 +664,15 @@ void SV_BanName_f (void)
 			continue;
 		}
 
-		nb = Z_Malloc(sizeof(bannedips_t));
+		nb = Z_Malloc(sizeof(bannedips_t)+reasonsize);
 		nb->next = svs.bannedips;
 		nb->adr = cl->netchan.remote_address;
 		NET_IntegerToMask(&nb->adr, &nb->adrmask, -1); // fill mask
 		if (*Cmd_Argv(2))	//explicit blocking of all ports of a client ip
 			nb->adr.port = 0;
 		svs.bannedips = nb;
+		if (reasonsize)
+			Q_strcpy(nb->reason, reason);
 
 		SV_BroadcastTPrintf (PRINT_HIGH, STL_CLIENTWASBANNED, cl->name);
 		// print directly, because the dropped client won't get the
@@ -682,10 +692,12 @@ void SV_BanIP_f (void)
 	int i;
 	client_t	*cl;
 	bannedips_t *nb;
+	char *reason = NULL;
+	int reasonsize = 0;
 
 	if (Cmd_Argc() < 2)
 	{
-		Con_Printf("%s address/mask|adress/maskbits\n", Cmd_Argv(0));
+		Con_Printf("%s address/mask|adress/maskbits [reason]\n", Cmd_Argv(0));
 		return;
 	}
 
@@ -701,6 +713,12 @@ void SV_BanIP_f (void)
 		return;
 	}
 
+	if (Cmd_Argc() > 2)
+	{
+		reason = Cmd_Argv(2);
+		reasonsize = strlen(reason);
+	}
+
 	// loop through clients and kick the ones that match
 	for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)
 	{
@@ -719,11 +737,58 @@ void SV_BanIP_f (void)
 	}
 
 	// add IP and mask to ban list
-	nb = Z_Malloc(sizeof(bannedips_t));
+	nb = Z_Malloc(sizeof(bannedips_t)+reasonsize);
 	nb->next = svs.bannedips;
 	nb->adr = banadr;
 	nb->adrmask = banmask;
 	svs.bannedips = nb;
+	if (reasonsize)
+		Q_strcpy(nb->reason, reason);
+}
+
+void SV_FilterIP_f (void)
+{
+	netadr_t banadr;
+	netadr_t banmask;
+	int i;
+	client_t	*cl;
+	filteredips_t *nb;
+	extern cvar_t filterban;
+
+	if (Cmd_Argc() < 2)
+	{
+		Con_Printf("%s address/mask|adress/maskbits\n", Cmd_Argv(0));
+		return;
+	}
+
+	if (!NET_StringToAdrMasked(Cmd_Argv(1), &banadr, &banmask))
+	{
+		Con_Printf("invalid address or mask\n");
+		return;
+	}
+
+	if (NET_IsLoopBackAddress(banadr))
+	{
+		Con_Printf("You're not allowed to filter loopback!\n");
+		return;
+	}
+
+	// loop through clients and kick the ones that match
+	for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)
+	{
+		if (cl->state<=cs_zombie)
+			continue;
+
+		if (filterban.value && NET_CompareAdrMasked(cl->netchan.remote_address, banadr, banmask))
+			SV_DropClient (cl);
+	}
+
+	// add IP and mask to filter list
+	nb = Z_Malloc(sizeof(filteredips_t));
+	nb->next = svs.filteredips;
+	nb->adr = banadr;
+	nb->adrmask = banmask;
+	svs.filteredips = nb;
 }
 
 void SV_BanList_f (void)
@@ -733,7 +798,10 @@ void SV_BanList_f (void)
 
 	while (nb)
 	{
-		Con_Printf("%s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
+		if (nb->reason[0])
+			Con_Printf("%s, %s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask), nb->reason);
+		else
+			Con_Printf("%s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
 		bancount++;
 		nb = nb->next;
 	}
@@ -741,6 +809,21 @@ void SV_BanList_f (void)
 	Con_Printf("%i total entries in ban list\n", bancount);
 }
 
+void SV_FilterList_f (void)
+{
+	int filtercount = 0;
+	filteredips_t *nb = svs.filteredips;
+
+	while (nb)
+	{
+		Con_Printf("%s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
+		filtercount++;
+		nb = nb->next;
+	}
+
+	Con_Printf("%i total entries in filter list\n", filtercount);
+}
+
 void SV_Unban_f (void)
 {
 	qboolean all = false;
@@ -766,7 +849,7 @@ void SV_Unban_f (void)
 	while (nb)
 	{
 		nbnext = nb->next;
-		if (NET_CompareAdrMasked(nb->adr, unbanadr, unbanmask))
+		if (all || NET_CompareAdrMasked(nb->adr, unbanadr, unbanmask))
 		{
 			if (!all)
 				Con_Printf("unbanned %s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
@@ -779,6 +862,44 @@ void SV_Unban_f (void)
 	}
 }
 
+void SV_Unfilter_f (void)
+{
+	qboolean all = false;
+	filteredips_t *nb = svs.filteredips;
+	filteredips_t *nbnext;
+	netadr_t unbanadr = {0};
+	netadr_t unbanmask = {0};
+
+	if (Cmd_Argc() < 2)
+	{
+		Con_Printf("%s address/mask|address/maskbits|all\n", Cmd_Argv(0));
+		return;
+	}
+
+	if (!Q_strcasecmp(Cmd_Argv(1), "all"))
+		all = true;
+	else if (!NET_StringToAdrMasked(Cmd_Argv(1), &unbanadr, &unbanmask))
+	{
+		Con_Printf("invalid address or mask\n");
+		return;
+	}
+
+	while (nb)
+	{
+		nbnext = nb->next;
+		if (all || NET_CompareAdrMasked(nb->adr, unbanadr, unbanmask))
+		{
+			if (!all)
+				Con_Printf("unfiltered %s\n", NET_AdrToStringMasked(nb->adr, nb->adrmask));
+			if (svs.filteredips == nb)
+				svs.filteredips = nbnext;
+			Z_Free(nb);
+		}
+
+		nb = nbnext;
+	}
+}
+
 void SV_ForceName_f (void)
 {
 	client_t	*cl;
@@ -1807,6 +1928,16 @@ void SV_InitOperatorCommands (void)
 //	Cmd_AddCommand ("ban", SV_BanName_f);
 	Cmd_AddCommand ("status", SV_Status_f);
 
+	Cmd_AddCommand ("addip", SV_FilterIP_f);
+	Cmd_AddCommand ("removeip", SV_Unfilter_f);
+	Cmd_AddCommand ("listip", SV_FilterList_f);
+
+//	Cmd_AddCommand ("filterip", SV_FilterIP_f);
+//	Cmd_AddCommand ("unfilter", SV_Unfilter_f);
+//	Cmd_AddCommand ("filterlist", SV_FilterList_f);
+
+//	Cmd_AddCommand ("writeip", SV_WriteIP_f);
+
 	Cmd_AddCommand ("sv", SV_SendGameCommand_f);
 
 	Cmd_AddCommand ("killserver", SV_KillServer_f);
diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c
index 4ad807f85..6b4a08e4b 100644
--- a/engine/server/sv_main.c
+++ b/engine/server/sv_main.c
@@ -196,6 +196,7 @@ void SV_FixupName(char *in, char *out);
 void SV_AcceptClient (netadr_t adr, int userid, char *userinfo);
 void Master_Shutdown (void);
 void PR_SetPlayerClass(client_t *cl, int classnum, qboolean fromqc);
+bannedips_t *SV_BannedAddress (netadr_t *a);
 
 //============================================================================
 
@@ -1757,18 +1758,16 @@ client_t *SVC_DirectConnect(void)
 		}
 	}
 
-
 	{
-		bannedips_t *banip;
-		for (banip = svs.bannedips; banip; banip=banip->next)
+		bannedips_t *banip = SV_BannedAddress(&adr);
+		if (banip)
 		{
-			if (NET_CompareAdrMasked(adr, banip->adr, banip->adrmask))
-			{
-				SV_RejectMessage (protocol, "You were banned.\nContact the administrator to complain.\n");
-				return NULL;
-			}
+			if (banip->reason[0])
+				SV_RejectMessage (protocol, "You were banned.\nReason: %s\n", banip->reason);
+			else
+				SV_RejectMessage (protocol, "You were banned.\n");
+			return NULL;
 		}
-		//yay, a legit client who we havn't banned yet.
 	}
 
 	edictnum = (newcl-svs.clients)+1;
@@ -2091,6 +2090,15 @@ void SVC_RemoteCommand (void)
 	int		i;
 	char	remaining[1024];
 
+	{
+		bannedips_t *banip = SV_BannedAddress(&net_from);
+		if (banip)
+		{
+			Con_Printf ("Rcon from banned ip %s\n", NET_AdrToString (net_from));
+			return;
+		}
+	}
+
 	if (!Rcon_Validate ())
 	{
 #ifdef SVRANKING
@@ -2362,136 +2370,8 @@ If 0, then only addresses matching the list will be allowed.  This lets you easi
 ==============================================================================
 */
 
-
-typedef struct
-{
-	unsigned	mask;
-	unsigned	compare;
-} ipfilter_t;
-
-#define	MAX_IPFILTERS	1024
-
-ipfilter_t	ipfilters[MAX_IPFILTERS];
-int			numipfilters;
-
 cvar_t	filterban = SCVAR("filterban", "1");
 
-/*
-=================
-StringToFilter
-=================
-*/
-qboolean StringToFilter (char *s, ipfilter_t *f)
-{
-	char	num[128];
-	int		i, j;
-	qbyte	b[4];
-	qbyte	m[4];
-
-	for (i=0 ; i<4 ; i++)
-	{
-		b[i] = 0;
-		m[i] = 0;
-	}
-
-	for (i=0 ; i<4 ; i++)
-	{
-		if (*s < '0' || *s > '9')
-		{
-			Con_Printf ("Bad filter address: %s\n", s);
-			return false;
-		}
-
-		j = 0;
-		while (*s >= '0' && *s <= '9')
-		{
-			num[j++] = *s++;
-		}
-		num[j] = 0;
-		b[i] = atoi(num);
-		if (b[i] != 0)
-			m[i] = 255;
-
-		if (!*s)
-			break;
-		s++;
-	}
-
-	f->mask = *(unsigned *)m;
-	f->compare = *(unsigned *)b;
-
-	return true;
-}
-
-/*
-=================
-SV_AddIP_f
-=================
-*/
-void SV_AddIP_f (void)
-{
-	int		i;
-
-	for (i=0 ; i<numipfilters ; i++)
-		if (ipfilters[i].compare == 0xffffffff)
-			break;		// free spot
-	if (i == numipfilters)
-	{
-		if (numipfilters == MAX_IPFILTERS)
-		{
-			Con_Printf ("IP filter list is full\n");
-			return;
-		}
-		numipfilters++;
-	}
-
-	if (!StringToFilter (Cmd_Argv(1), &ipfilters[i]))
-		ipfilters[i].compare = 0xffffffff;
-}
-
-/*
-=================
-SV_RemoveIP_f
-=================
-*/
-void SV_RemoveIP_f (void)
-{
-	ipfilter_t	f;
-	int			i, j;
-
-	if (!StringToFilter (Cmd_Argv(1), &f))
-		return;
-	for (i=0 ; i<numipfilters ; i++)
-		if (ipfilters[i].mask == f.mask
-		&& ipfilters[i].compare == f.compare)
-		{
-			for (j=i+1 ; j<numipfilters ; j++)
-				ipfilters[j-1] = ipfilters[j];
-			numipfilters--;
-			Con_Printf ("Removed.\n");
-			return;
-		}
-	Con_Printf ("Didn't find %s.\n", Cmd_Argv(1));
-}
-
-/*
-=================
-SV_ListIP_f
-=================
-*/
-void SV_ListIP_f (void)
-{
-	int		i;
-	qbyte	b[4];
-
-	Con_Printf ("Filter list:\n");
-	for (i=0 ; i<numipfilters ; i++)
-	{
-		*(unsigned *)b = ipfilters[i].compare;
-		Con_Printf ("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
-	}
-}
-
 /*
 =================
 SV_WriteIP_f
@@ -2499,6 +2379,8 @@ SV_WriteIP_f
 */
 void SV_WriteIP_f (void)
 {
+// TODO: function needs to be rewritten to handle new banning and filtering logic
+/*
 	vfsfile_t	*f;
 	char	name[MAX_OSPATH];
 	qbyte	b[4];
@@ -2524,23 +2406,7 @@ void SV_WriteIP_f (void)
 	}
 
 	VFS_CLOSE (f);
-}
-
-/*
-=================
-SV_SendBan
-=================
 */
-void SV_SendBan (void)
-{
-	char		data[128];
-
-	data[0] = data[1] = data[2] = data[3] = 0xff;
-	data[4] = A2C_PRINT;
-	data[5] = 0;
-	strcat (data, "\nbanned.\n");
-
-	NET_SendPacket (NS_SERVER, strlen(data), data, net_from);
 }
 
 /*
@@ -2548,20 +2414,34 @@ void SV_SendBan (void)
 SV_FilterPacket
 =================
 */
-qboolean SV_FilterPacket (void)
+qboolean SV_FilterPacket (netadr_t *a)
 {
-	int		i;
-	unsigned	in;
+	filteredips_t *banip;
 
-	in = *(unsigned *)net_from.address.ip;
+	if (NET_IsLoopBackAddress(*a))
+		return 0; // never filter loopback
 
-	for (i=0 ; i<numipfilters ; i++)
-		if ( (in & ipfilters[i].mask) == ipfilters[i].compare)
+	for (banip = svs.filteredips; banip; banip=banip->next)
+	{
+		if (NET_CompareAdrMasked(*a, banip->adr, banip->adrmask))
 			return filterban.value;
-
+	}
 	return !filterban.value;
 }
 
+// SV_BannedAdress, run through ban address list and return corresponding bannedips_t
+// pointer, otherwise return NULL if not in the list
+bannedips_t *SV_BannedAddress (netadr_t *a)
+{
+	bannedips_t *banip;
+	for (banip = svs.bannedips; banip; banip=banip->next)
+	{
+		if (NET_CompareAdrMasked(*a, banip->adr, banip->adrmask))
+			return banip;
+	}
+	return NULL;
+}
+
 //send a network packet to a new non-connected client.
 //this is to combat tunneling
 void SV_OpenRoute_f(void)
@@ -2598,11 +2478,8 @@ void SV_ReadPackets (void)
 	good = false;
 	while (SV_GetPacket ())
 	{
-		if (SV_FilterPacket ())
-		{
-			SV_SendBan ();	// tell them we aren't listening...
+		if (SV_FilterPacket (&net_from))
 			continue;
-		}
 
 		// check for connectionless packet (0xffffffff) first
 		if (*(int *)net_message.data == -1)
@@ -3235,11 +3112,6 @@ void SV_InitLocal (void)
 
 	Cvar_Register (&sv_nailhack, cvargroup_servercontrol);
 
-	Cmd_AddCommand ("addip", SV_AddIP_f);
-	Cmd_AddCommand ("removeip", SV_RemoveIP_f);
-	Cmd_AddCommand ("listip", SV_ListIP_f);
-	Cmd_AddCommand ("writeip", SV_WriteIP_f);
-
 	Cmd_AddCommand ("sv_impulse", SV_Impulse_f);
 
 	Cmd_AddCommand ("openroute", SV_OpenRoute_f);
diff --git a/engine/server/svq3_game.c b/engine/server/svq3_game.c
index 0f0829df7..ac7a0f6d7 100644
--- a/engine/server/svq3_game.c
+++ b/engine/server/svq3_game.c
@@ -2911,6 +2911,7 @@ void SVQ3_HandleClient(void)
 	SVQ3_ParseClientMessage(&svs.clients[i]);
 }
 
+bannedips_t *SV_BannedAddress (netadr_t *a);
 void SVQ3_DirectConnect(void)	//Actually connect the client, use up a slot, and let the gamecode know of it.
 {
 	char *reason;
@@ -2919,6 +2920,7 @@ void SVQ3_DirectConnect(void)	//Actually connect the client, use up a slot, and
 	int ret;
 	int challenge;
 	int qport;
+	bannedips_t *banip;
 
 	if (net_message.cursize < 13)
 		return;
@@ -2934,7 +2936,17 @@ void SVQ3_DirectConnect(void)	//Actually connect the client, use up a slot, and
 	if (!cl)
 		cl = SVQ3_FindEmptyPlayerSlot();
 
-	if (!cl)
+	banip = SV_BannedAddress(&net_from);
+
+	if (banip)
+	{
+		if (banip->reason[0])
+			reason = banip->reason;
+		else
+			reason = "Banned.";
+		userinfo = NULL;
+	}
+	else if (!cl)
 	{
 		reason = "Server is full.";
 		userinfo = NULL;