diff options
| author | eeeee <eeeee@qwe123.info> | 2018-08-18 17:33:58 -0700 |
|---|---|---|
| committer | Learath <learath2@gmail.com> | 2018-08-20 15:33:34 +0300 |
| commit | 2cd0223a8df54ae1c8715915a331dfc56a5a29d3 (patch) | |
| tree | a5c3789c4aa2c93255d824b5eb343172d3642eac | |
| parent | 3f3abebebf20e8901376b929e2afcc6bdb404a3b (diff) | |
| download | zcatch-2cd0223a8df54ae1c8715915a331dfc56a5a29d3.tar.gz zcatch-2cd0223a8df54ae1c8715915a331dfc56a5a29d3.zip | |
Port antispoof
| -rw-r--r-- | bam.lua | 25 | ||||
| -rw-r--r-- | src/base/system.c | 63 | ||||
| -rw-r--r-- | src/base/system.h | 21 | ||||
| -rw-r--r-- | src/engine/client/client.cpp | 6 | ||||
| -rw-r--r-- | src/engine/external/md5/md5.c | 381 | ||||
| -rw-r--r-- | src/engine/external/md5/md5.h | 91 | ||||
| -rw-r--r-- | src/engine/server/server.cpp | 24 | ||||
| -rw-r--r-- | src/engine/server/server.h | 1 | ||||
| -rw-r--r-- | src/engine/shared/config_variables.h | 5 | ||||
| -rw-r--r-- | src/engine/shared/network.cpp | 34 | ||||
| -rw-r--r-- | src/engine/shared/network.h | 89 | ||||
| -rw-r--r-- | src/engine/shared/network_conn.cpp | 99 | ||||
| -rw-r--r-- | src/engine/shared/network_server.cpp | 568 | ||||
| -rw-r--r-- | src/game/version.h | 4 |
14 files changed, 1286 insertions, 125 deletions
diff --git a/bam.lua b/bam.lua index cdf2453c..e68fd262 100644 --- a/bam.lua +++ b/bam.lua @@ -144,7 +144,7 @@ end function build(settings) -- apply compiler settings config.compiler:Apply(settings) - + --settings.objdir = Path("objs") settings.cc.Output = Intermediate_Output @@ -201,7 +201,7 @@ function build(settings) settings.link.libs:Add("dl") settings.link.libs:Add("rt") end - + if platform == "solaris" then settings.link.flags:Add("-lsocket") settings.link.flags:Add("-lnsl") @@ -234,6 +234,7 @@ function build(settings) if config.websockets.value then libwebsockets = Compile(settings, Collect("src/engine/external/libwebsockets/*.c")) end + md5 = Compile(settings, "src/engine/external/md5/md5.c") -- build game components engine_settings = settings:Copy() @@ -248,7 +249,7 @@ function build(settings) server_settings.link.libs:Add("dl") server_settings.link.libs:Add("rt") end - + if platform == "macosx" then client_settings.link.frameworks:Add("OpenGL") client_settings.link.frameworks:Add("AGL") @@ -314,16 +315,16 @@ function build(settings) tools = {} for i,v in ipairs(tools_src) do toolname = PathFilename(PathBase(v)) - tools[i] = Link(settings, toolname, Compile(settings, v), engine, zlib, pnglite) + tools[i] = Link(settings, toolname, Compile(settings, v), engine, zlib, pnglite, md5) end -- build client, server, version server and master server client_exe = Link(client_settings, "zCatch", game_shared, game_client, engine, client, game_editor, zlib, pnglite, wavpack, - client_link_other, client_osxlaunch, jsonparser, libwebsockets) + client_link_other, client_osxlaunch, jsonparser, libwebsockets, md5) server_exe = Link(server_settings, "zCatch-Server", engine, server, - game_shared, game_server, zlib, server_link_other, libwebsockets) + game_shared, game_server, zlib, server_link_other, libwebsockets, md5) serverlaunch = {} if platform == "macosx" then @@ -331,7 +332,7 @@ function build(settings) end versionserver_exe = Link(server_settings, "versionsrv", versionserver, - engine, zlib, libwebsockets) + engine, zlib, libwebsockets, md5) masterserver_exe = Link(server_settings, "mastersrv", masterserver, engine, zlib) @@ -396,14 +397,14 @@ if platform == "macosx" then debug_sql_settings_ppc.cc.flags:Add("-arch ppc") debug_sql_settings_ppc.link.flags:Add("-arch ppc") debug_sql_settings_ppc.cc.defines:Add("CONF_DEBUG", "CONF_SQL") - + release_settings_ppc = release_settings:Copy() release_settings_ppc.config_name = "release_ppc" release_settings_ppc.config_ext = "_ppc" release_settings_ppc.cc.flags:Add("-arch ppc") release_settings_ppc.link.flags:Add("-arch ppc") release_settings_ppc.cc.defines:Add("CONF_RELEASE") - + release_sql_settings_ppc = release_sql_settings:Copy() release_sql_settings_ppc.config_name = "sql_release_ppc" release_sql_settings_ppc.config_ext = "_sql_ppc" @@ -437,7 +438,7 @@ if platform == "macosx" then release_settings_x86.cc.flags:Add("-arch i386") release_settings_x86.link.flags:Add("-arch i386") release_settings_x86.cc.defines:Add("CONF_RELEASE") - + release_sql_settings_x86 = release_sql_settings:Copy() release_sql_settings_x86.config_name = "sql_release_x86" release_sql_settings_x86.config_ext = "_sql_x86" @@ -472,7 +473,7 @@ if platform == "macosx" then release_settings_x86_64.cc.flags:Add("-arch x86_64") release_settings_x86_64.link.flags:Add("-arch x86_64") release_settings_x86_64.cc.defines:Add("CONF_RELEASE") - + release_sql_settings_x86_64 = release_sql_settings:Copy() release_sql_settings_x86_64.config_name = "sql_release_x86_64" release_sql_settings_x86_64.config_ext = "_sql_x86_64" @@ -487,7 +488,7 @@ if platform == "macosx" then end DefaultTarget("game_debug_x86") - + if config.macosxppc.value == 1 then if arch == "ia32" then PseudoTarget("release", ppc_r, x86_r) diff --git a/src/base/system.c b/src/base/system.c index bc0c261d..970d1c6b 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -2060,6 +2060,69 @@ unsigned str_quickhash(const char *str) return hash; } +struct SECURE_RANDOM_DATA +{ + int initialized; +#if defined(CONF_FAMILY_WINDOWS) + HCRYPTPROV provider; +#else + IOHANDLE urandom; +#endif +}; + +static struct SECURE_RANDOM_DATA secure_random_data = { 0 }; + +int secure_random_init() +{ + if(secure_random_data.initialized) + { + return 0; + } +#if defined(CONF_FAMILY_WINDOWS) + if(CryptAcquireContext(&secure_random_data.provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { + secure_random_data.initialized = 1; + return 0; + } + else + { + return 1; + } +#else + secure_random_data.urandom = io_open("/dev/urandom", IOFLAG_READ); + if(secure_random_data.urandom) + { + secure_random_data.initialized = 1; + return 0; + } + else + { + return 1; + } +#endif +} + +void secure_random_fill(void *bytes, unsigned length) +{ + if(!secure_random_data.initialized) + { + dbg_msg("secure", "called secure_random_fill before secure_random_init"); + dbg_break(); + } +#if defined(CONF_FAMILY_WINDOWS) + if(!CryptGenRandom(secure_random_data.provider, length, bytes)) + { + dbg_msg("secure", "CryptGenRandom failed, last_error=%ld", GetLastError()); + dbg_break(); + } +#else + if(length != io_read(secure_random_data.urandom, bytes, length)) + { + dbg_msg("secure", "io_read returned with a short read"); + dbg_break(); + } +#endif +} #if defined(__cplusplus) } diff --git a/src/base/system.h b/src/base/system.h index ad606ceb..e23adf29 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -1297,6 +1297,27 @@ int str_utf8_encode(char *ptr, int chr); */ int str_utf8_check(const char *str); +/* + Function: secure_random_init + Initializes the secure random module. + You *MUST* check the return value of this function. + + Returns: + 0 - Initialization succeeded. + 1 - Initialization failed. +*/ +int secure_random_init(); + +/* + Function: secure_random_fill + Fills the buffer with the specified amount of random bytes. + + Parameters: + buffer - Pointer to the start of the buffer. + length - Length of the buffer. +*/ +void secure_random_fill(void *bytes, unsigned length); + #ifdef __cplusplus } #endif diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index b58b1b5a..46907946 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -2292,6 +2292,12 @@ int main(int argc, const char **argv) // ignore_convention } #endif + if(secure_random_init() != 0) + { + dbg_msg("secure", "could not initialize secure RNG"); + return -1; + } + CClient *pClient = CreateClient(); IKernel *pKernel = IKernel::Create(); pKernel->RegisterInterface(pClient); diff --git a/src/engine/external/md5/md5.c b/src/engine/external/md5/md5.c new file mode 100644 index 00000000..c35d96c5 --- /dev/null +++ b/src/engine/external/md5/md5.c @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include <string.h> + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include <stdio.h> in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/engine/external/md5/md5.h b/src/engine/external/md5/md5.h new file mode 100644 index 00000000..698c995d --- /dev/null +++ b/src/engine/external/md5/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 6bcffae3..37c6f430 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -797,6 +797,22 @@ void CServer::DoSnapshot() GameServer()->OnPostSnap(); } +int CServer::NewClientNoAuthCallback(int ClientID, void *pUser) +{ + CServer *pThis = (CServer *)pUser; + pThis->m_aClients[ClientID].m_State = CClient::STATE_CONNECTING; + pThis->m_aClients[ClientID].m_aName[0] = 0; + pThis->m_aClients[ClientID].m_aClan[0] = 0; + pThis->m_aClients[ClientID].m_Country = -1; + pThis->m_aClients[ClientID].m_Authed = AUTHED_NO; + pThis->m_aClients[ClientID].m_AuthTries = 0; + pThis->m_aClients[ClientID].m_pRconCmdToSend = 0; + pThis->m_aClients[ClientID].Reset(); + + pThis->SendMap(ClientID); + + return 0; +} int CServer::NewClientCallback(int ClientID, void *pUser) { @@ -1454,7 +1470,7 @@ int CServer::Run() return -1; } - m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this); + m_NetServer.SetCallbacks(NewClientCallback, NewClientNoAuthCallback, DelClientCallback, this); m_Econ.Init(Console(), &m_ServerBan); @@ -2261,6 +2277,12 @@ int main(int argc, const char **argv) // ignore_convention } #endif + if(secure_random_init() != 0) + { + dbg_msg("secure", "could not initialize secure RNG"); + return -1; + } + CServer *pServer = CreateServer(); IKernel *pKernel = IKernel::Create(); diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 9b3ac371..23c6ae5e 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -223,6 +223,7 @@ public: void DoSnapshot(); static int NewClientCallback(int ClientID, void *pUser); + static int NewClientNoAuthCallback(int ClientID, void *pUser); static int DelClientCallback(int ClientID, const char *pReason, void *pUser); void SendMap(int ClientID); diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index b0c3df01..3b0e2f1d 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -126,4 +126,9 @@ MACRO_CONFIG_INT(DbgGraphs, dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance gr MACRO_CONFIG_INT(DbgHitch, dbg_hitch, 0, 0, 0, CFGFLAG_SERVER, "Hitch warnings") MACRO_CONFIG_STR(DbgStressServer, dbg_stress_server, 32, "localhost", CFGFLAG_CLIENT, "Server to stress") MACRO_CONFIG_INT(DbgResizable, dbg_resizable, 0, 0, 0, CFGFLAG_CLIENT, "Enables window resizing") + +MACRO_CONFIG_INT(SvVanillaAntiSpoof, sv_vanilla_antispoof, 1, 0, 1, CFGFLAG_SERVER, "Enable vanilla Antispoof") +MACRO_CONFIG_INT(SvVanConnPerSecond, sv_van_conn_per_second, 10, 1, 1000, CFGFLAG_SERVER, "Antispoof specific ratelimit") +MACRO_CONFIG_INT(SvConnlimit, sv_connlimit, 4, 0, 100, CFGFLAG_SERVER, "Connlimit: Number of connections an IP is allowed to do in a timespan") +MACRO_CONFIG_INT(SvConnlimitTime, sv_connlimit_time, 20, 0, 1000, CFGFLAG_SERVER, "Connlimit: Time in which IP's connections are counted") #endif diff --git a/src/engine/shared/network.cpp b/src/engine/shared/network.cpp index 13d62077..eb1a51dc 100644 --- a/src/engine/shared/network.cpp +++ b/src/engine/shared/network.cpp @@ -58,10 +58,13 @@ int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk) // handle sequence stuff if(m_pConnection && (Header.m_Flags&NET_CHUNKFLAG_VITAL)) { - if(Header.m_Sequence == (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE) + // anti spoof: ignore unknown sequence + if(Header.m_Sequence == (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE || m_pConnection->m_UnknownSeq) { + m_pConnection->m_UnknownSeq = false; + // in sequence - m_pConnection->m_Ack = (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE; + m_pConnection->m_Ack = Header.m_Sequence; } else { @@ -101,7 +104,7 @@ void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void * net_udp_send(Socket, pAddr, aBuffer, 6+DataSize); } -void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket) +void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken) { unsigned char aBuffer[NET_MAX_PACKETSIZE]; int CompressedSize = -1; @@ -117,6 +120,14 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct io_flush(ms_DataLogSent); } + if (SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED) + { + // append security token + // if SecurityToken is NET_SECURITY_TOKEN_UNKNOWN we will still append it hoping to negotiate it + mem_copy(&pPacket->m_aChunkData[pPacket->m_DataSize], &SecurityToken, sizeof(SecurityToken)); + pPacket->m_DataSize += sizeof(SecurityToken); + } + // compress CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4); @@ -161,7 +172,8 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct // check the size if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE) { - dbg_msg("", "packet too small, %d", Size); + if(g_Config.m_Debug) + dbg_msg("", "packet too small, %d", Size); return -1; } @@ -185,7 +197,8 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct { if(Size < 6) { - dbg_msg("", "connection less packet too small, %d", Size); + if(g_Config.m_Debug) + dbg_msg("", "connection less packet too small, %d", Size); return -1; } @@ -198,7 +211,14 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct else { if(pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION) + { + // Don't allow compressed control packets. + if(pPacket->m_Flags&NET_PACKETFLAG_CONTROL) + { + return -1; + } pPacket->m_DataSize = ms_Huffman.Decompress(&pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData)); + } else mem_copy(pPacket->m_aChunkData, &pBuffer[3], pPacket->m_DataSize); } @@ -226,7 +246,7 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct } -void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize) +void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken) { CNetPacketConstruct Construct; Construct.m_Flags = NET_PACKETFLAG_CONTROL; @@ -237,7 +257,7 @@ void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int Con mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize); // send the control message - CNetBase::SendPacket(Socket, pAddr, &Construct); + CNetBase::SendPacket(Socket, pAddr, &Construct, SecurityToken); } diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h index cbc6e560..5924651f 100644 --- a/src/engine/shared/network.h +++ b/src/engine/shared/network.h @@ -6,6 +6,10 @@ #include "ringbuffer.h" #include "huffman.h" +#include <base/math.h> + +#include <engine/message.h> + /* CURRENT: @@ -75,12 +79,26 @@ enum NET_CONN_BUFFERSIZE=1024*32, + NET_CONNLIMIT_IPS=16, + NET_ENUM_TERMINATOR }; +typedef int SECURITY_TOKEN; + +SECURITY_TOKEN ToSecurityToken(unsigned char *pData); + +static const unsigned char SECURITY_TOKEN_MAGIC[] = {'T', 'K', 'E', 'N'}; + +enum +{ + NET_SECURITY_TOKEN_UNKNOWN = -1, + NET_SECURITY_TOKEN_UNSUPPORTED = 0, +}; typedef int (*NETFUNC_DELCLIENT)(int ClientID, const char* pReason, void *pUser); typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser); +typedef int (*NETFUNC_NEWCLIENT_NOAUTH)(int ClientID, void *pUser); struct CNetChunk { @@ -140,8 +158,10 @@ private: unsigned m_State; int m_Token; + SECURITY_TOKEN m_SecurityToken; int m_RemoteClosed; bool m_BlockCloseMsg; + bool m_UnknownSeq; TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> m_Buffer; @@ -158,7 +178,6 @@ private: NETSTATS m_Stats; // - void Reset(); void ResetStats(); void SetError(const char *pString); void AckChunks(int Ack); @@ -168,6 +187,8 @@ private: void ResendChunk(CNetChunkResend *pResend); void Resend(); + bool HasSecurityToken; + public: void Init(NETSOCKET Socket, bool BlockCloseMsg); int Connect(NETADDR *pAddr); @@ -176,7 +197,7 @@ public: int Update(); int Flush(); - int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr); + int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED); int QueueChunk(int Flags, int DataSize, const void *pData); const char *ErrorString(); @@ -192,6 +213,14 @@ public: int64 ConnectTime() const { return m_LastUpdateTime; } int AckSequence() const { return m_Ack; } + int SeqSequence() const { return m_Sequence; } + int SecurityToken() const { return m_SecurityToken; } + + // anti spoof + void DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken); + void SetUnknownSeq() { m_UnknownSeq = true; } + void SetSequence(int Sequence) { m_Sequence = Sequence; } + void Reset(); }; class CConsoleNetConnection @@ -251,6 +280,13 @@ class CNetServer CNetConnection m_Connection; }; + struct CSpamConn + { + NETADDR m_Addr; + int64 m_Time; + int m_Conns; + }; + NETSOCKET m_Socket; class CNetBan *m_pNetBan; CSlot m_aSlots[NET_MAX_CLIENTS]; @@ -258,13 +294,39 @@ class CNetServer int m_MaxClientsPerIP; NETFUNC_NEWCLIENT m_pfnNewClient; + NETFUNC_NEWCLIENT_NOAUTH m_pfnNewClientNoAuth; NETFUNC_DELCLIENT m_pfnDelClient; void *m_UserPtr; + + int m_NumConAttempts; // log flooding attacks + int64 m_TimeNumConAttempts; + unsigned char m_SecurityTokenSeed[16]; + + // vanilla connect flood detection + bool m_VConnHighLoad; + int64 m_VConnFirst; + int m_VConnNum; + + CSpamConn m_aSpamConns[NET_CONNLIMIT_IPS]; + CNetRecvUnpacker m_RecvUnpacker; + void OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet); + void OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet); + void OnConnCtrlMsg(NETADDR &Addr, int ClientID, int ControlMsg, const CNetPacketConstruct &Packet); + bool ClientExists(const NETADDR &Addr) { return GetClientSlot(Addr) != -1; }; + int GetClientSlot(const NETADDR &Addr); + void SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken); + + int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth=false); + int NumClientsWithAddr(NETADDR Addr); + bool Connlimit(NETADDR Addr); + void SendMsgs(NETADDR &Addr, const CMsgPacker *Msgs[], int num); + public: - int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); + // int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); + int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_DELCLIENT pfnDelClient, void *pUser); // bool Open(NETADDR BindAddr, class CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags); @@ -280,6 +342,7 @@ public: // status requests const NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } + bool HasSecurityToken(int ClientID) const { return m_aSlots[ClientID].m_Connection.SecurityToken() != NET_SECURITY_TOKEN_UNSUPPORTED; } NETSOCKET Socket() const { return m_Socket; } class CNetBan *NetBan() const { return m_pNetBan; } int NetType() const { return m_Socket.type; } @@ -287,6 +350,16 @@ public: // void SetMaxClientsPerIP(int Max); + bool SetTimedOut(int ClientID, int OrigID); + void SetTimeoutProtected(int ClientID); + + int ResetErrorString(int ClientID); + const char *ErrorString(int ClientID); + + // anti spoof + SECURITY_TOKEN GetToken(const NETADDR &Addr); + // vanilla token/gametick shouldn't be negative + SECURITY_TOKEN GetVanillaToken(const NETADDR &Addr) { return absolute(GetToken(Addr)); } }; class CNetConsole @@ -332,11 +405,10 @@ public: // client side class CNetClient { - NETADDR m_ServerAddr; CNetConnection m_Connection; CNetRecvUnpacker m_RecvUnpacker; - NETSOCKET m_Socket; public: + NETSOCKET m_Socket; // openness bool Open(NETADDR BindAddr, int Flags); int Close(); @@ -360,6 +432,8 @@ public: int State(); int GotProblems(); const char *ErrorString(); + + bool SecurityTokenUnknown() { return m_Connection.SecurityToken() == NET_SECURITY_TOKEN_UNKNOWN; } }; @@ -377,9 +451,10 @@ public: static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize); static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize); - static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize); + static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken); static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize); - static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket); + static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken); + static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket); // The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not diff --git a/src/engine/shared/network_conn.cpp b/src/engine/shared/network_conn.cpp index 7d76d585..d76a828b 100644 --- a/src/engine/shared/network_conn.cpp +++ b/src/engine/shared/network_conn.cpp @@ -4,9 +4,16 @@ #include "config.h" #include "network.h" +SECURITY_TOKEN ToSecurityToken(unsigned char *pData) +{ + return (int)pData[0] | (pData[1] << 8) | (pData[2] << 16) | (pData[3] << 24); +} + void CNetConnection::ResetStats() { mem_zero(&m_Stats, sizeof(m_Stats)); + mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); + m_LastUpdateTime = 0; } void CNetConnection::Reset() @@ -22,6 +29,8 @@ void CNetConnection::Reset() m_LastUpdateTime = 0; m_Token = -1; mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); + m_UnknownSeq = false; + m_SecurityToken = NET_SECURITY_TOKEN_UNKNOWN; m_Buffer.Init(); @@ -76,7 +85,7 @@ int CNetConnection::Flush() // send of the packets m_Construct.m_Ack = m_Ack; - CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct); + CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct, m_SecurityToken); // update send times m_LastSendTime = time_get(); @@ -88,10 +97,13 @@ int CNetConnection::Flush() int CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence) { + if (m_State == NET_CONNSTATE_OFFLINE || m_State == NET_CONNSTATE_ERROR) + return -1; + unsigned char *pChunkData; // check if we have space for it, if not, flush the connection - if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData)) + if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData) - (int)sizeof(SECURITY_TOKEN)) Flush(); // pack all the data @@ -126,8 +138,7 @@ int CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int } else { - // out of buffer - Disconnect("too weak connection (out of buffer)"); + // out of buffer, don't save the packet and hope nobody will ask for resend return -1; } } @@ -146,7 +157,7 @@ void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSi { // send the control message m_LastSendTime = time_get(); - CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize); + CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize, m_SecurityToken); } void CNetConnection::ResendChunk(CNetChunkResend *pResend) @@ -171,7 +182,7 @@ int CNetConnection::Connect(NETADDR *pAddr) m_PeerAddr = *pAddr; mem_zero(m_ErrorString, sizeof(m_ErrorString)); m_State = NET_CONNSTATE_CONNECT; - SendControl(NET_CTRLMSG_CONNECT, 0, 0); + SendControl(NET_CTRLMSG_CONNECT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)); return 0; } @@ -195,8 +206,39 @@ void CNetConnection::Disconnect(const char *pReason) Reset(); } -int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) +void CNetConnection::DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken) +{ + Reset(); + + m_State = NET_CONNSTATE_ONLINE; + + m_PeerAddr = Addr; + mem_zero(m_ErrorString, sizeof(m_ErrorString)); + + int64 Now = time_get(); + m_LastSendTime = Now; + m_LastRecvTime = Now; + m_LastUpdateTime = Now; + + m_SecurityToken = SecurityToken; +} + +int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken) { + if (State() != NET_CONNSTATE_OFFLINE && m_SecurityToken != NET_SECURITY_TOKEN_UNKNOWN && m_SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED) + { + // supposed to have a valid token in this packet, check it + if (pPacket->m_DataSize < (int)sizeof(m_SecurityToken)) + return 0; + pPacket->m_DataSize -= sizeof(m_SecurityToken); + if (m_SecurityToken != ToSecurityToken(&pPacket->m_aChunkData[pPacket->m_DataSize])) + { + if(g_Config.m_Debug) + dbg_msg("security", "token mismatch, expected %d got %d", m_SecurityToken, ToSecurityToken(&pPacket->m_aChunkData[pPacket->m_DataSize])); + return 0; + } + } + // check if actual ack value is valid(own sequence..latest peer ack) if(m_Sequence >= m_PeerAck) { @@ -256,6 +298,13 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) { if(CtrlMsg == NET_CTRLMSG_CONNECT) { + NETADDR nAddr; + mem_copy(&nAddr, pAddr, sizeof(nAddr)); + nAddr.port = 0; + m_PeerAddr.port = 0; + if(net_addr_comp(&m_PeerAddr, &nAddr) == 0 && time_get() - m_LastUpdateTime < time_freq() * 3) + return 0; + // send response and init connection Reset(); m_State = NET_CONNSTATE_PENDING; @@ -264,7 +313,21 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) m_LastSendTime = Now; m_LastRecvTime = Now; m_LastUpdateTime = Now; - SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); + if (m_SecurityToken == NET_SECURITY_TOKEN_UNKNOWN + && pPacket->m_DataSize >= (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(m_SecurityToken)) + && !mem_comp(&pPacket->m_aChunkData[1], SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC))) + { + m_SecurityToken = SecurityToken; + if(g_Config.m_Debug) + dbg_msg("security", "generated token %d", m_SecurityToken); + } + else + { + if(g_Config.m_Debug) + dbg_msg("security", "token not supported by client (packet size %d)", pPacket->m_DataSize); + m_SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED; + } + SendControl(NET_CTRLMSG_CONNECTACCEPT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)); if(g_Config.m_Debug) dbg_msg("connection", "got connection, sending connect+accept"); } @@ -274,6 +337,20 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) // connection made if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT) { + if (m_SecurityToken == NET_SECURITY_TOKEN_UNKNOWN + && pPacket->m_DataSize >= (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(m_SecurityToken)) + && !mem_comp(&pPacket->m_aChunkData[1], SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC))) + { + m_SecurityToken = ToSecurityToken(&pPacket->m_aChunkData[1 + sizeof(SECURITY_TOKEN_MAGIC)]); + if(g_Config.m_Debug) + dbg_msg("security", "got token %d", m_SecurityToken); + } + else + { + m_SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED; + if(g_Config.m_Debug) + dbg_msg("security", "token not supported by server"); + } m_LastRecvTime = Now; SendControl(NET_CTRLMSG_ACCEPT, 0, 0); m_State = NET_CONNSTATE_ONLINE; @@ -354,12 +431,14 @@ int CNetConnection::Update() else if(State() == NET_CONNSTATE_CONNECT) { if(time_get()-m_LastSendTime > time_freq()/2) // send a new connect every 500ms - SendControl(NET_CTRLMSG_CONNECT, 0, 0); + SendControl(NET_CTRLMSG_CONNECT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)); + // SendControl(NET_CTRLMSG_CONNECT, 0, 0); } else if(State() == NET_CONNSTATE_PENDING) { if(time_get()-m_LastSendTime > time_freq()/2) // send a new connect/accept every 500ms - SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); + SendControl(NET_CTRLMSG_CONNECTACCEPT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)); + // SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); } return 0; diff --git a/src/engine/shared/network_server.cpp b/src/engine/shared/network_server.cpp index e97a8eba..cc97981c 100644 --- a/src/engine/shared/network_server.cpp +++ b/src/engine/shared/network_server.cpp @@ -4,9 +4,48 @@ #include <engine/console.h> +#include "config.h" #include "netban.h" #include "network.h" - +#include <engine/external/md5/md5.h> +#include <engine/message.h> +#include <engine/shared/protocol.h> + +const int DummyMapCrc = 0x6c760ac4; +unsigned char g_aDummyMapData[] = { + 0x44, 0x41, 0x54, 0x41, 0x04, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, + 0x14, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0xc3, 0x52, + 0xff, 0x7f, 0x00, 0x00, 0x20, 0x5c, 0xf6, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x78, 0x9c, 0x63, 0x64, 0x60, 0x60, 0x60, 0x44, + 0xc2, 0x00, 0x00, 0x38, 0x00, 0x05 +}; + + +static SECURITY_TOKEN ToSecurityToken(const unsigned char *pData) +{ + return (int)pData[0] | (pData[1] << 8) | (pData[2] << 16) | (pData[3] << 24); +} bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags) { @@ -29,12 +68,22 @@ bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int Ma m_MaxClientsPerIP = MaxClientsPerIP; + m_NumConAttempts = 0; + m_TimeNumConAttempts = time_get(); + + m_VConnHighLoad = false; + m_VConnNum = 0; + m_VConnFirst = 0; + + secure_random_fill(m_SecurityTokenSeed, sizeof(m_SecurityTokenSeed)); + for(int i = 0; i < NET_MAX_CLIENTS; i++) m_aSlots[i].m_Connection.Init(m_Socket, true); return true; } +/* int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser) { m_pfnNewClient = pfnNewClient; @@ -42,6 +91,16 @@ int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT p m_UserPtr = pUser; return 0; } +*/ + +int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_NEWCLIENT_NOAUTH pfnNewClientNoAuth, NETFUNC_DELCLIENT pfnDelClient, void *pUser) +{ + m_pfnNewClient = pfnNewClient; + m_pfnNewClientNoAuth = pfnNewClientNoAuth; + m_pfnDelClient = pfnDelClient; + m_UserPtr = pUser; + return 0; +} int CNetServer::Close() { @@ -69,22 +128,407 @@ int CNetServer::Drop(int ClientID, const char *pReason) int CNetServer::Update() { - int64 Now = time_get(); for(int i = 0; i < MaxClients(); i++) { m_aSlots[i].m_Connection.Update(); if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) { - if(Now - m_aSlots[i].m_Connection.ConnectTime() < time_freq()/2 && NetBan()) - NetBan()->BanAddr(ClientAddr(i), 60, "Stressing network"); - else - Drop(i, m_aSlots[i].m_Connection.ErrorString()); + Drop(i, m_aSlots[i].m_Connection.ErrorString()); } } return 0; } +SECURITY_TOKEN CNetServer::GetToken(const NETADDR &Addr) +{ + md5_state_t md5; + md5_byte_t digest[16]; + SECURITY_TOKEN SecurityToken; + md5_init(&md5); + + md5_append(&md5, (unsigned char*)m_SecurityTokenSeed, sizeof(m_SecurityTokenSeed)); + md5_append(&md5, (unsigned char*)&Addr, sizeof(Addr)); + + md5_finish(&md5, digest); + SecurityToken = ToSecurityToken(digest); + + if (SecurityToken == NET_SECURITY_TOKEN_UNKNOWN || + SecurityToken == NET_SECURITY_TOKEN_UNSUPPORTED) + SecurityToken = 1; + + return SecurityToken; +} + +void CNetServer::SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken) +{ + CNetBase::SendControlMsg(m_Socket, &Addr, 0, ControlMsg, pExtra, ExtraSize, SecurityToken); +} + +int CNetServer::NumClientsWithAddr(NETADDR Addr) +{ + NETADDR ThisAddr = Addr, OtherAddr; + + int FoundAddr = 0; + ThisAddr.port = 0; + + for(int i = 0; i < MaxClients(); ++i) + { + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE || + m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) + continue; + + OtherAddr = *m_aSlots[i].m_Connection.PeerAddress(); + OtherAddr.port = 0; + if(!net_addr_comp(&ThisAddr, &OtherAddr)) + FoundAddr++; + } + + return FoundAddr; +} + +bool CNetServer::Connlimit(NETADDR Addr) +{ + int64 Now = time_get(); + int Oldest = 0; + + for(int i = 0; i < NET_CONNLIMIT_IPS; ++i) + { + if(!net_addr_comp(&m_aSpamConns[i].m_Addr, &Addr)) + { + if(m_aSpamConns[i].m_Time > Now - time_freq() * g_Config.m_SvConnlimitTime) + { + if(m_aSpamConns[i].m_Conns >= g_Config.m_SvConnlimit) + return true; + } + else + { + m_aSpamConns[i].m_Time = Now; + m_aSpamConns[i].m_Conns = 0; + } + m_aSpamConns[i].m_Conns++; + return false; + } + + if(m_aSpamConns[i].m_Time < m_aSpamConns[Oldest].m_Time) + Oldest = i; + } + + m_aSpamConns[Oldest].m_Addr = Addr; + m_aSpamConns[Oldest].m_Time = Now; + m_aSpamConns[Oldest].m_Conns = 1; + return false; +} + +int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth) +{ + if (Connlimit(Addr)) + { + const char Msg[] = "Too many connections in a short time"; + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, Msg, sizeof(Msg), SecurityToken); + return -1; // failed to add client + } + + // check for sv_max_clients_per_ip + if (NumClientsWithAddr(Addr) + 1 > m_MaxClientsPerIP) + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP); + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf) + 1, SecurityToken); + return -1; // failed to add client + } + + int Slot = -1; + for(int i = 0; i < MaxClients(); i++) + { + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) + { + Slot = i; + break; + } + } + + if (Slot == -1) + { + const char FullMsg[] = "This server is full"; + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg), SecurityToken); + + return -1; // failed to add client + } + + // init connection slot + m_aSlots[Slot].m_Connection.DirectInit(Addr, SecurityToken); + + if (VanillaAuth) + { + // client sequence is unknown if the auth was done + // connection-less + m_aSlots[Slot].m_Connection.SetUnknownSeq(); + // correct sequence + m_aSlots[Slot].m_Connection.SetSequence(6); + } + + if (g_Config.m_Debug) + { + char aAddrStr[NETADDR_MAXSTRSIZE]; + net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true); + dbg_msg("security", "client accepted %s", aAddrStr); + } + + + if (VanillaAuth) + m_pfnNewClientNoAuth(Slot, m_UserPtr); + else + m_pfnNewClient(Slot, m_UserPtr); + + return Slot; // done +} + +void CNetServer::SendMsgs(NETADDR &Addr, const CMsgPacker *Msgs[], int num) +{ + CNetPacketConstruct m_Construct; + mem_zero(&m_Construct, sizeof(m_Construct)); + unsigned char *pChunkData = &m_Construct.m_aChunkData[m_Construct.m_DataSize]; + + for (int i = 0; i < num; i++) + { + const CMsgPacker *pMsg = Msgs[i]; + CNetChunkHeader Header; + Header.m_Flags = NET_CHUNKFLAG_VITAL; + Header.m_Size = pMsg->Size(); + Header.m_Sequence = i+1; + pChunkData = Header.Pack(pChunkData); + mem_copy(pChunkData, pMsg->Data(), pMsg->Size()); + *((unsigned char*)pChunkData) <<= 1; + *((unsigned char*)pChunkData) |= 1; + pChunkData += pMsg->Size(); + m_Construct.m_NumChunks++; + } + + // + m_Construct.m_DataSize = (int)(pChunkData-m_Construct.m_aChunkData); + CNetBase::SendPacket(m_Socket, &Addr, &m_Construct, NET_SECURITY_TOKEN_UNSUPPORTED); +} + +// connection-less msg packet without token-support +void CNetServer::OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet) +{ + bool IsCtrl = Packet.m_Flags&NET_PACKETFLAG_CONTROL; + int CtrlMsg = m_RecvUnpacker.m_Data.m_aChunkData[0]; + + // log flooding + //TODO: remove + if (g_Config.m_Debug) + { + int64 Now = time_get(); + + if (Now - m_TimeNumConAttempts > time_freq()) + // reset + m_NumConAttempts = 0; + + m_NumConAttempts++; + + if (m_NumConAttempts > 100) + { + dbg_msg("security", "flooding detected"); + + m_TimeNumConAttempts = Now; + m_NumConAttempts = 0; + } + } + + + if (IsCtrl && CtrlMsg == NET_CTRLMSG_CONNECT) + { + if (g_Config.m_SvVanillaAntiSpoof && g_Config.m_Password[0] == '\0') + { + // detect flooding + int64 Now = time_get(); + if(Now <= m_VConnFirst + time_freq()) + { + m_VConnNum++; + } + else + { + m_VConnHighLoad = m_VConnNum > g_Config.m_SvVanConnPerSecond; + m_VConnNum = 1; + m_VConnFirst = Now; + } + + bool Flooding = m_VConnNum > g_Config.m_SvVanConnPerSecond || m_VConnHighLoad; + + if (g_Config.m_Debug && Flooding) + { + dbg_msg("security", "vanilla connection flooding detected"); + } + + // simulate accept + SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, NULL, 0, NET_SECURITY_TOKEN_UNSUPPORTED); + + // Begin vanilla compatible token handshake + // The idea is to pack a security token in the gametick + // parameter of NETMSG_SNAPEMPTY. The Client then will + // return the token/gametick in NETMSG_INPUT, allowing + // us to validate the token. + // https://github.com/eeeee/ddnet/commit/b8e40a244af4e242dc568aa34854c5754c75a39a + + // Before we can send NETMSG_SNAPEMPTY, the client needs + // to load a map, otherwise it might crash. The map + // should be as small as is possible and directly available + // to the client. Therefore a dummy map is sent in the same + // packet. To reduce the traffic we'll fallback to a default + // map if there are too many connection attempts at once. + + // send mapchange + map data + con_ready + 3 x empty snap (with token) + CMsgPacker MapChangeMsg(NETMSG_MAP_CHANGE); + + if (Flooding) + { + // Fallback to dm1 + MapChangeMsg.AddString("dm1", 0); + MapChangeMsg.AddInt(0xf2159e6e); + MapChangeMsg.AddInt(5805); + } + else + { + // dummy map + MapChangeMsg.AddString("dummy", 0); + MapChangeMsg.AddInt(DummyMapCrc); + MapChangeMsg.AddInt(sizeof(g_aDummyMapData)); + } + + CMsgPacker MapDataMsg(NETMSG_MAP_DATA); + + if (Flooding) + { + // send empty map data to keep 0.6.4 support + MapDataMsg.AddInt(1); // last chunk + MapDataMsg.AddInt(0); // crc + MapDataMsg.AddInt(0); // chunk index + MapDataMsg.AddInt(0); // map size + MapDataMsg.AddRaw(NULL, 0); // map data + } + else + { + // send dummy map data + MapDataMsg.AddInt(1); // last chunk + MapDataMsg.AddInt(DummyMapCrc); // crc + MapDataMsg.AddInt(0); // chunk index + MapDataMsg.AddInt(sizeof(g_aDummyMapData)); // map size + MapDataMsg.AddRaw(g_aDummyMapData, sizeof(g_aDummyMapData)); // map data + } + + CMsgPacker ConReadyMsg(NETMSG_CON_READY); + + CMsgPacker SnapEmptyMsg(NETMSG_SNAPEMPTY); + SECURITY_TOKEN SecurityToken = GetVanillaToken(Addr); + SnapEmptyMsg.AddInt((int)SecurityToken); + SnapEmptyMsg.AddInt((int)SecurityToken + 1); + + // send all chunks/msgs in one packet + const CMsgPacker *Msgs[] = {&MapChangeMsg, &MapDataMsg, &ConReadyMsg, + &SnapEmptyMsg, &SnapEmptyMsg, &SnapEmptyMsg}; + SendMsgs(Addr, Msgs, 6); + } + else + { + // accept client directly + SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, NULL, 0, NET_SECURITY_TOKEN_UNSUPPORTED); + + TryAcceptClient(Addr, NET_SECURITY_TOKEN_UNSUPPORTED); + } + } + else if(!IsCtrl && g_Config.m_SvVanillaAntiSpoof && g_Config.m_Password[0] == '\0') + { + CNetChunkHeader h; + + unsigned char *pData = Packet.m_aChunkData; + pData = h.Unpack(pData); + CUnpacker Unpacker; + Unpacker.Reset(pData, h.m_Size); + int Msg = Unpacker.GetInt() >> 1; + + if (Msg == NETMSG_INPUT) + { + SECURITY_TOKEN SecurityToken = Unpacker.GetInt(); + if (SecurityToken == GetVanillaToken(Addr)) + { + if (g_Config.m_Debug) + dbg_msg("security", "new client (vanilla handshake)"); + // try to accept client skipping auth state + TryAcceptClient(Addr, NET_SECURITY_TOKEN_UNSUPPORTED, true); + } + else if (g_Config.m_Debug) + dbg_msg("security", "invalid token (vanilla handshake)"); + + } + else + { + if (g_Config.m_Debug) + { + dbg_msg("security", "invalid preconn msg %d", Msg); + } + } + } +} + +void CNetServer::OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet) +{ + if (ClientExists(Addr)) + return; // silently ignore + + + if (ControlMsg == NET_CTRLMSG_CONNECT) + { + bool SupportsToken = Packet.m_DataSize >= + (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(SECURITY_TOKEN)) && + !mem_comp(&Packet.m_aChunkData[1], SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)); + + if (SupportsToken) + { + // response connection request with token + SECURITY_TOKEN Token = GetToken(Addr); + SendControl(Addr, NET_CTRLMSG_CONNECTACCEPT, SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC), Token); + } + } + else if (ControlMsg == NET_CTRLMSG_ACCEPT && Packet.m_DataSize == 1 + sizeof(SECURITY_TOKEN)) + { + SECURITY_TOKEN Token = ToSecurityToken(&Packet.m_aChunkData[1]); + if (Token == GetToken(Addr)) + { + // correct token + // try to accept client + if (g_Config.m_Debug) + dbg_msg("security", "new client (0.6.5 token)"); + TryAcceptClient(Addr, Token); + } + else + { + // invalid token + if (g_Config.m_Debug) + dbg_msg("security", "invalid token"); + } + } +} + +int CNetServer::GetClientSlot(const NETADDR &Addr) +{ + int Slot = -1; + + for(int i = 0; i < MaxClients(); i++) + { + if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE && + m_aSlots[i].m_Connection.State() != NET_CONNSTATE_ERROR && + net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0) + + { + Slot = i; + } + } + + return Slot; +} + /* TODO: chopp up this function into smaller working parts */ @@ -105,17 +549,17 @@ int CNetServer::Recv(CNetChunk *pChunk) if(Bytes <= 0) break; - if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) + // check if we just should drop the packet + char aBuf[128]; + if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) { - // check if we just should drop the packet - char aBuf[128]; - if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) - { - // banned, reply with a message - CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1); - continue; - } + // banned, reply with a message + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf)+1, NET_SECURITY_TOKEN_UNSUPPORTED); + continue; + } + if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) + { if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) { pChunk->m_Flags = NETSENDFLAG_CONNLESS; @@ -127,83 +571,35 @@ int CNetServer::Recv(CNetChunk *pChunk) } else { - // TODO: check size here - if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) - { - bool Found = false; + // drop invalid ctrl packets + if (m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && + m_RecvUnpacker.m_Data.m_DataSize == 0) + continue; - // check if we already got this client - for(int i = 0; i < MaxClients(); i++) - { - if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE && - net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0) - { - Found = true; // silent ignore.. we got this client already - break; - } - } + // normal packet, find matching slot + int Slot = GetClientSlot(Addr); - // client that wants to connect - if(!Found) - { + if (Slot != -1) + { + // found - // only allow a specific number of players with the same ip - NETADDR ThisAddr = Addr, OtherAddr; - int FoundAddr = 1; - ThisAddr.port = 0; - for(int i = 0; i < MaxClients(); ++i) - { - if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) - continue; - - OtherAddr = *m_aSlots[i].m_Connection.PeerAddress(); - OtherAddr.port = 0; - if(!net_addr_comp(&ThisAddr, &OtherAddr)) - { - if(FoundAddr++ >= m_MaxClientsPerIP) - { - char aBuf[128]; - str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP); - CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, sizeof(aBuf)); - return 0; - } - } - } - - for(int i = 0; i < MaxClients(); i++) - { - if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) - { - Found = true; - m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr); - if(m_pfnNewClient) - m_pfnNewClient(i, m_UserPtr); - - break; - } - } - - if(!Found) - { - const char FullMsg[] = "This server is full"; - CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg)); - } + if(m_aSlots[Slot].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) + { + if(m_RecvUnpacker.m_Data.m_DataSize) + m_RecvUnpacker.Start(&Addr, &m_aSlots[Slot].m_Connection, Slot); } } else { - // normal packet, find matching slot - for(int i = 0; i < MaxClients(); i++) - { - if(net_addr_comp(m_aSlots[i].m_Connection.PeerAddress(), &Addr) == 0) - { - if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) - { - if(m_RecvUnpacker.m_Data.m_DataSize) - m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i); - } - } - } + // not found, client that wants to connect + + if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && + m_RecvUnpacker.m_Data.m_DataSize > 1) + // got control msg with extra size (should support token) + OnTokenCtrlMsg(Addr, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data); + else + // got connection-less ctrl or sys msg + OnPreConnMsg(Addr, m_RecvUnpacker.m_Data); } } } @@ -240,7 +636,7 @@ int CNetServer::Send(CNetChunk *pChunk) } else { - Drop(pChunk->m_ClientID, "Error sending data"); + //Drop(pChunk->m_ClientID, "Error sending data"); } } return 0; diff --git a/src/game/version.h b/src/game/version.h index 76d95dd0..17010c1b 100644 --- a/src/game/version.h +++ b/src/game/version.h @@ -3,7 +3,7 @@ #ifndef GAME_VERSION_H #define GAME_VERSION_H #include "generated/nethash.cpp" -#define GAME_VERSION "0.6.2" +#define GAME_VERSION "0.6.5" #define GAME_NETVERSION "0.6 " GAME_NETVERSION_HASH -static const char GAME_RELEASE_VERSION[8] = {'0', '.', '6', '.', '2', 0}; +static const char GAME_RELEASE_VERSION[8] = {'0', '.', '6', '.', '5', 0}; #endif |