diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 858a14dcb..0bbde383f 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1347,6 +1347,8 @@ void CL_CheckForResend (void) #ifdef NQPROT if ((contype & 2) && !connectinfo.clogged) { + char *e; + int pwd; sizebuf_t sb; memset(&sb, 0, sizeof(sb)); sb.data = data; @@ -1366,11 +1368,18 @@ void CL_CheckForResend (void) which means it corrects for our public port if our nat uses different public ports for different remote ports thus all nq engines claim to be proquake */ - + if (!*password.string || !strcmp(password.string, "none")) + pwd = 0; + else + { + pwd = strtol(password.string, &e, 0); + if (*e) + pwd = CalcHashInt(&hash_md4, password.string, strlen(password.string)); + } MSG_WriteByte(&sb, 1); /*'mod'*/ MSG_WriteByte(&sb, 34); /*'mod' version*/ MSG_WriteByte(&sb, 0); /*flags*/ - MSG_WriteLong(&sb, strtoul(password.string, NULL, 0)); /*password*/ + MSG_WriteLong(&sb, pwd); /*password*/ /*FTE servers will detect this string and treat it as a qw challenge instead (if it allows qw clients), so protocol choice is deterministic*/ if (contype & 1) @@ -4370,31 +4379,36 @@ void CLNQ_ConnectionlessPacket(void) } else { - port = htons((unsigned short)MSG_ReadLong()); - //this is the port that we're meant to respond to. + port = htons((unsigned short)MSG_ReadLong()); //this is the port that we're meant to respond to... + if (msg_badread) //qe has no port specified. and that's fine when its over dtls anyway. + port = 0; - if (port && !msg_badread) - { - char buf[256]; - net_from.port = port; - Con_DPrintf("redirecting to port %s\n", NET_AdrToString(buf, sizeof(buf), &net_from)); - } cls.proquake_angles_hack = false; cls.protocol_nq = CPNQ_ID; if (MSG_ReadByte() == 1) //a proquake server adds a little extra info { int ver = MSG_ReadByte(); + int flags = MSG_ReadByte(); Con_DPrintf("ProQuake server %i.%i\n", ver/10, ver%10); // if (ver >= 34) cls.proquake_angles_hack = true; - if (MSG_ReadByte() == 1) + if (flags & 1) { //its a 'pure' server. Con_Printf("pure ProQuake server\n"); return; } + if (flags & 0x80) + port = 0; //don't force the port. + } + + if (port && port != net_from.port) + { + char buf[256]; + net_from.port = port; + Con_Printf("redirecting to port %s\n", NET_AdrToString(buf, sizeof(buf), &net_from)); } } diff --git a/engine/server/server.h b/engine/server/server.h index 2cb1d0ade..ccfae5741 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -1129,6 +1129,7 @@ const char *SV_CheckRejectConnection(netadr_t *adr, const char *uinfo, unsigned char *SV_BannedReason (netadr_t *a); void SV_EvaluatePenalties(client_t *cl); void SV_AutoAddPenalty (client_t *cl, unsigned int banflag, int duration, char *reason); +void SV_AutoBanSender (int duration, char *reason); //bans net_from //note: not all penalties are actually penalties, but they can still expire. #define BAN_BAN (1u<<0) //user is banned from the server diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index 37fefd3a6..e039dea25 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -1916,6 +1916,19 @@ void SV_AutoAddPenalty (client_t *cl, unsigned int banflag, int duration, char * SV_AddBanEntry(&proto, reason); SV_EvaluatePenalties(cl); } +void SV_AutoBanSender (int duration, char *reason) +{ + bannedips_t proto; + + proto.banflags = BAN_BAN; + proto.expiretime = SV_BanTime() + duration; + memset(&proto.adrmask.address, 0xff, sizeof(proto.adrmask.address)); + proto.adr = net_from; + proto.adr.port = 0; + proto.adrmask.type = proto.adr.type; + + SV_AddBanEntry(&proto, reason); +} static void SV_WriteIP_f (void) { diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index d84ea8900..f3efc0cbb 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -2668,15 +2668,33 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info) } else { - s = Info_ValueForKey (info->userinfo, "password"); - if (password.string[0] && - stricmp(password.string, "none") && - strcmp(password.string, s) && - !NET_IsLoopBackAddress(&info->adr)) + if (!password.string[0] || + !stricmp(password.string, "none") || + NET_IsLoopBackAddress(&info->adr)) + ; //don't care, doesn't matter. + else if (info->protocol == SCP_NETQUAKE) + { //if its a proquake client then use numeric passwords, which take a bit of processing + char *e; + int got = strtol(Info_ValueForKey (info->userinfo, "password"), NULL, 0); + int need = strtol(password.string, &e, 0); + if (*e) + need = CalcHashInt(&hash_md4, password.string, strlen(password.string)); + if (got != need) + { + Con_TPrintf ("%s:password failed\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr)); + SV_RejectMessage (info->protocol, "server requires a password\n\n"); + return; + } + } + else { - Con_TPrintf ("%s:password failed\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr)); - SV_RejectMessage (info->protocol, "server requires a password\n\n"); - return; + s = Info_ValueForKey (info->userinfo, "password"); + if (strcmp(password.string, s)) + { + Con_TPrintf ("%s:password failed\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr)); + SV_RejectMessage (info->protocol, "server requires a password\n\n"); + return; + } } spectator = false; Info_RemoveKey (info->userinfo, "password"); // remove passwd @@ -4594,12 +4612,21 @@ qboolean SVNQ_ConnectionlessPacket(void) ) { if (password.string[0] && - stricmp(password.string, "none") && - strcmp(password.string, va("%i", passwd)) ) + stricmp(password.string, "none")) { //make sure we don't get crippled because of being unable to specify the actual password with proquake's stuff. - Con_TPrintf ("%s:password failed (nq)\n", NET_AdrToString (buffer2, sizeof(buffer2), &net_from)); - SV_RejectMessage (SCP_NETQUAKE, "server requires a password\n\n"); - return true; + char *e; + intmax_t svpass = strtoll(password.string, &e, 0); + if (*e) //something ain't numeric... hash it so they have a chance of getting it right... + svpass = CalcHashInt(&hash_md4, password.string, strlen(password.string)); + if (passwd != svpass) + { + Con_TPrintf ("%s:password failed (nq)\n", NET_AdrToString (buffer2, sizeof(buffer2), &net_from)); + SV_RejectMessage (SCP_NETQUAKE, "\x01this server requires a password\n\n"); + + //and prevent them from spamming attempts. botnets might still get through fast though. + SV_AutoBanSender(15, "password cooldown"); + return true; + } } SZ_Clear(&sb);