From 5d2ff1286d86d70891dffeff9e3060327bd82e06 Mon Sep 17 00:00:00 2001 From: Spoike Date: Thu, 18 May 2017 10:24:09 +0000 Subject: [PATCH] first version with dtls support. disabled for now. schannel (ie: windows native) works as a client, not a server. gnutls provides both client+server support. servers need to load a pre-generated cert from disk. tweaked gamepads to actually work in the web target. tweak gamepads a bit. added gp_* bind aliases. xinput+sdl+web should all use the same key mappings. finally added the itemtimer glsl. tweaked software renderer to not be quite so buggy, but you probably won't realise that if you try it. disabled the ill-fated QWOVERQ3 feature. don't do the oldorigin thing in quakeworld mods. hopefully this'll fix cspree's weird stuck-in-floor issue. dpp7 is buggy serverside. disabled for now. I'm part way through rewriting its deltas. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5103 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_ents.c | 6 +- engine/client/cl_main.c | 201 +++++++---- engine/client/cl_parse.c | 5 +- engine/client/client.h | 1 + engine/client/in_generic.c | 4 +- engine/client/in_sdl.c | 68 ++-- engine/client/in_win.c | 76 +++-- engine/client/keys.c | 24 +- engine/client/keys.h | 65 ++++ engine/client/net_master.c | 5 +- engine/client/pr_clcmd.c | 45 +++ engine/client/sys_sdl.c | 7 +- engine/common/bothdefs.h | 11 + engine/common/net.h | 9 +- engine/common/net_chan.c | 34 +- engine/common/net_ice.c | 7 +- engine/common/net_ssl_gnutls.c | 514 ++++++++++++++++++++++------- engine/common/net_ssl_winsspi.c | 389 ++++++++++++++++++---- engine/common/net_wins.c | 322 ++++++++++++++---- engine/common/netinc.h | 17 +- engine/common/plugin.c | 2 +- engine/common/protocol.h | 8 +- engine/dotnet2005/ftequake.sln | 5 +- engine/gl/r_bishaders.h | 47 +++ engine/http/ftpserver.c | 2 + engine/http/httpclient.c | 2 +- engine/http/iwebiface.c | 3 + engine/qclib/qcc_pr_lex.c | 6 +- engine/qclib/qccgui.c | 64 +++- engine/server/pr_cmds.c | 2 +- engine/server/server.h | 28 +- engine/server/sv_ccmds.c | 50 ++- engine/server/sv_ents.c | 75 ++++- engine/server/sv_main.c | 227 +++++++++++-- engine/server/sv_mvd.c | 6 +- engine/server/sv_user.c | 10 +- engine/server/svq3_game.c | 53 ++- engine/shaders/generatebuiltinsl.c | 1 + engine/shaders/glsl/itemtimer.glsl | 43 +++ engine/sw/sw.h | 2 +- engine/sw/sw_backend.c | 6 + engine/sw/sw_rast.c | 22 +- engine/sw/sw_vidwin.c | 3 +- engine/web/ftejslib.h | 4 +- engine/web/ftejslib.js | 34 +- engine/web/gl_vidweb.c | 105 +++++- specs/browser.txt | 10 +- 47 files changed, 2071 insertions(+), 559 deletions(-) create mode 100644 engine/shaders/glsl/itemtimer.glsl diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index cbf4f4ef..0976cbe3 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -2904,7 +2904,7 @@ void CL_AddDecal(shader_t *shader, vec3_t origin, vec3_t up, vec3_t side, vec3_t cl_numstris--; } -void R_AddItemTimer(vec3_t shadoworg, float yaw, float radius, float percent) +void R_AddItemTimer(vec3_t shadoworg, float yaw, float radius, float percent, vec3_t rgb) { vec3_t eang; shader_t *s; @@ -2963,7 +2963,7 @@ void R_AddItemTimer(vec3_t shadoworg, float yaw, float radius, float percent) } ctx.t = t; - Vector4Set(ctx.rgbavalue, 0.1, 0.1, 0.1, percent); + Vector4Set(ctx.rgbavalue, rgb[0], rgb[1], rgb[2], percent); Mod_ClipDecal(cl.worldmodel, shadoworg, ctx.axis[0], ctx.axis[1], ctx.axis[2], radius, 0,0, CL_AddDecal_Callback, &ctx); if (!t->numidx) cl_numstris--; @@ -3781,7 +3781,7 @@ void CL_LinkPacketEntities (void) if (le->sequence != cl.lerpentssequence) continue; } - R_AddItemTimer(timer->origin, cl.time*90 + timer->origin[0] + timer->origin[1] + timer->origin[2], timer->radius, (cl.time - timer->start) / timer->duration); + R_AddItemTimer(timer->origin, cl.time*90 + timer->origin[0] + timer->origin[1] + timer->origin[2], timer->radius, (cl.time - timer->start) / timer->duration, timer->rgb); } } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 727286e0..e968e5a0 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -245,6 +245,15 @@ static struct qboolean trying; qboolean istransfer; //ignore the user's desired server (don't change connect.adr). netadr_t adr; //address that we're trying to transfer to. FIXME: support multiple resolved addresses, eg both ::1 AND 127.0.0.1 +#ifdef HAVE_DTLS + enum + { + DTLS_DISABLE, + DTLS_TRY, + DTLS_REQUIRE, + DTLS_ACTIVE + } dtlsupgrade; +#endif int mtu; unsigned int compresscrc; int protocol; //nq/qw/q2/q3. guessed based upon server replies @@ -528,8 +537,6 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, int fteprotextsupported=0; int fteprotextsupported2=0; #endif - int clients; - int c; char *a; // JACK: Fixed bug where DNS lookups would cause two connects real fast @@ -585,6 +592,7 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, NET_AdrToString(data, sizeof(data), to); Cvar_ForceSet(&cl_serveraddress, data); +// Info_SetValueForStarKey (cls.userinfo, "*ip", data, MAX_INFO_STRING); if (!NET_IsClientLegal(to)) { @@ -606,29 +614,6 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, if (connectinfo.protocol == CP_QUAKE2 && (connectinfo.subprotocol == PROTOCOL_VERSION_R1Q2 || connectinfo.subprotocol == PROTOCOL_VERSION_Q2PRO)) connectinfo.qport &= 0xff; -// Info_SetValueForStarKey (cls.userinfo, "*ip", NET_AdrToString(adr), MAX_INFO_STRING); - - clients = 1; -/* - if (cl_splitscreen.value && (fteprotextsupported & PEXT_SPLITSCREEN)) - { -// if (adr.type == NA_LOOPBACK) - clients = cl_splitscreen.value+1; -// else -// Con_Printf("Split screens are still under development\n"); - } - - if (clients < 1) - clients = 1; - if (clients > MAX_SPLITS) - clients = MAX_SPLITS; - -#ifdef Q2CLIENT - if (connectinfo.protocol == CP_QUAKE2) //q2 only supports after-connect seats - clients = 1; -#endif -*/ - #ifdef Q3CLIENT if (connectinfo.protocol == CP_QUAKE3) { //q3 requires some very strange things. @@ -639,9 +624,6 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, Q_snprintfz(data, sizeof(data), "%c%c%c%cconnect", 255, 255, 255, 255); - if (clients>1) //splitscreen 'connect' command specifies the number of userinfos sent. - Q_strncatz(data, va("%i", clients), sizeof(data)); - Q_strncatz(data, va(" %i %i %i", connectinfo.subprotocol, connectinfo.qport, connectinfo.challenge), sizeof(data)); //userinfo0 has some twiddles for extensions from other qw engines. @@ -659,8 +641,6 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, Q_strncatz(data, va("\\*z_ext\\%i", SUPPORTED_Z_EXTENSIONS), sizeof(data)); Q_strncatz(data, "\"", sizeof(data)); - for (c = 1; c < clients; c++) - Q_strncatz(data, va(" \"%s\"", cls.userinfo[c]), sizeof(data)); if (connectinfo.protocol == CP_QUAKE2 && connectinfo.subprotocol == PROTOCOL_VERSION_R1Q2) Q_strncatz(data, va(" %d %d", mtu, 1905), sizeof(data)); //mti, sub-sub-version @@ -721,11 +701,6 @@ char *CL_TryingToConnect(void) return cls.servername; } -#ifndef CLIENTONLY -int SV_NewChallenge (void); -client_t *SVC_DirectConnect(void); -#endif - /* ================= CL_CheckForResend @@ -759,6 +734,7 @@ void CL_CheckForResend (void) return; //erk? connectinfo.trying = true; connectinfo.istransfer = false; + connectinfo.adr.prot = NP_DGRAM; NET_InitClient(true); @@ -1015,6 +991,24 @@ void CL_CheckForResend (void) SCR_EndLoadingPlaque(); return; } + +#ifdef HAVE_DTLS + if (connectinfo.dtlsupgrade == DTLS_ACTIVE) + { //if we've already established a dtls connection, stick with it + if (connectinfo.adr.prot == NP_DGRAM) + connectinfo.adr.prot = NP_DTLS; + } + else if (connectinfo.adr.prot == NP_DTLS) + { //dtls connections start out with regular udp, and upgrade to dtls once its established that the server supports it. + connectinfo.dtlsupgrade = DTLS_REQUIRE; + connectinfo.adr.prot = NP_DGRAM; + } + else + { + //hostname didn't specify dtls. upgrade if we're allowed, but don't mandate it. + //connectinfo.dtlsupgrade = DTLS_TRY; + } +#endif } if (!NET_IsClientLegal(&connectinfo.adr)) { @@ -1132,6 +1126,9 @@ void CL_BeginServerConnect(const char *host, int port, qboolean noproxy) if (!port) port = cl_defaultport.value; +#ifdef HAVE_DTLS + NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr); +#endif memset(&connectinfo, 0, sizeof(connectinfo)); connectinfo.trying = true; connectinfo.defaultport = port; @@ -1148,6 +1145,10 @@ void CL_BeginServerReconnect(void) Con_TPrintf ("Connect ignored - dedicated. set a renderer first\n"); return; } +#endif +#ifdef HAVE_DTLS + NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr); + connectinfo.dtlsupgrade = 0; #endif connectinfo.trying = true; connectinfo.istransfer = false; @@ -1553,6 +1554,13 @@ void CL_ClearState (void) BZ_Free(cl.item_name[i]); #endif + while (cl.itemtimers) + { + struct itemtimer_s *t = cl.itemtimers; + cl.itemtimers = t->next; + Z_Free(t); + } + { downloadlist_t *next; while(cl.downloadlist) @@ -1792,6 +1800,8 @@ void CL_Disconnect_f (void) connectinfo.trying = false; + NET_CloseClient(); + (void)CSQC_UnconnectedInit(); } @@ -2792,6 +2802,9 @@ void CL_ConnectionlessPacket (void) static netadr_t lastadr; unsigned int curtime = Sys_Milliseconds(); unsigned long pext = 0, pext2 = 0, huffcrc=0, mtu=0; +#ifdef HAVE_DTLS + qboolean candtls = false; +#endif s = MSG_ReadString (); COM_Parse(s); @@ -2888,6 +2901,7 @@ void CL_ConnectionlessPacket (void) } #else Con_Printf("\nUnable to connect to Quake2\n"); + return; #endif s+=9; } @@ -2940,31 +2954,65 @@ void CL_ConnectionlessPacket (void) for(;;) { - c = MSG_ReadLong (); + int cmd = MSG_ReadLong (); if (msg_badread) break; - if (c == PROTOCOL_VERSION_FTE) - pext = MSG_ReadLong (); - else if (c == PROTOCOL_VERSION_FTE2) - pext2 = MSG_ReadLong (); - else if (c == PROTOCOL_VERSION_FRAGMENT) - mtu = MSG_ReadLong (); - else if (c == PROTOCOL_VERSION_VARLENGTH) + if (cmd == PROTOCOL_VERSION_VARLENGTH) { int len = MSG_ReadLong(); if (len < 0 || len > 8192) break; c = MSG_ReadLong();/*ident*/ - MSG_ReadSkip(len); /*payload*/ + switch(c) + { + default: + MSG_ReadSkip(len); /*payload*/ + break; + } } -#ifdef HUFFNETWORK - else if (c == PROTOCOL_VERSION_HUFFMAN) - huffcrc = MSG_ReadLong (); -#endif - //else if (c == PROTOCOL_VERSION_...) else - MSG_ReadLong (); + { + unsigned int l = MSG_ReadLong(); + switch(cmd) + { + case PROTOCOL_VERSION_FTE: pext = l; break; + case PROTOCOL_VERSION_FTE2: pext2 = l; break; + case PROTOCOL_VERSION_FRAGMENT: mtu = l; break; +#ifdef HAVE_DTLS + case PROTOCOL_VERSION_DTLSUPGRADE: candtls = l; break; //0:not enabled. 1:use if you want. 2:require it. +#endif +#ifdef HUFFNETWORK + case PROTOCOL_VERSION_HUFFMAN: huffcrc = l; break; +#endif + default: + break; + } + } } + +#ifdef HAVE_DTLS + if (candtls && connectinfo.adr.prot == NP_DGRAM && (connectinfo.dtlsupgrade || candtls > 1)) + { + //c2s getchallenge + //s2c c%u\0DTLS=0 + //c2s dtlsconnect %u + //s2c dtlsopened + //c2s DTLS(getchallenge) + //DTLS(etc) + + //server says it can do tls. + char *pkt = va("%c%c%c%cdtlsconnect %i", 255, 255, 255, 255, connectinfo.challenge); + NET_SendPacket (NS_CLIENT, strlen(pkt), pkt, &net_from); + return; + } + if (connectinfo.dtlsupgrade == DTLS_REQUIRE) + { + connectinfo.trying = false; + Con_Printf("Server does not support/allow dtls. not connecting.\n"); + return; + } +#endif + CL_SendConnectPacket (&net_from, mtu, pext, pext2, huffcrc/*, ...*/); return; } @@ -3094,13 +3142,37 @@ void CL_ConnectionlessPacket (void) } #endif - if (c == 'd') //note - this conflicts with qw masters, our browser uses a different socket. + if (c == 'd'/*M2C_MASTER_REPLY*/) { - Con_Printf ("d\n"); - if (cls.demoplayback != DPB_NONE) + s = MSG_ReadString (); + COM_Parse(s); + if (!strcmp(com_token, "tlsopened")) + { //server is letting us know that its now listening for a dtls handshake. +#ifdef HAVE_DTLS + Con_Printf ("dtlsopened\n"); + if (!NET_CompareAdr(&connectinfo.adr, &net_from)) + return; + + connectinfo.dtlsupgrade = DTLS_ACTIVE; + connectinfo.adr.prot = NP_DTLS; + if (!NET_DTLS_Create(cls.sockets, &net_from)) + Con_Printf ("unable to establish dtls route\n"); +#else + Con_Printf ("dtlsopened (unsupported)\n"); +#endif + } + else if (*s != '\n') + { //qw master server list response + Con_Printf ("server ip list\n"); + } + else { - Con_Printf("Disconnect\n"); - CL_Disconnect_f(); + Con_Printf ("d\n"); + if (cls.demoplayback != DPB_NONE) + { + Con_Printf("Disconnect\n"); + CL_Disconnect_f(); + } } return; } @@ -3169,7 +3241,12 @@ client_connect: //fixme: make function CL_SendClientCommand(true, "new"); cls.state = ca_connected; if (cls.netchan.remote_address.type != NA_LOOPBACK) - Con_TPrintf ("Connected.\n"); + { + if (cls.netchan.remote_address.prot == NP_DTLS || cls.netchan.remote_address.prot == NP_TLS || cls.netchan.remote_address.prot == NP_WSS) + Con_TPrintf ("Connected (encrypted).\n"); + else + Con_TPrintf ("Connected (plain-text).\n"); + } #ifdef QUAKESPYAPI allowremotecmd = false; // localid required now for remote cmds #endif @@ -3369,7 +3446,19 @@ void CL_ReadPackets (void) for(;;) { if (!CL_GetMessage()) +#ifndef HAVE_DTLS break; +#else + { + NET_DTLS_Timeouts(cls.sockets); + break; + } + + if (*(int *)net_message.data != -1) + if (NET_DTLS_Decode(cls.sockets)) + if (!net_message.cursize) + continue; +#endif #ifdef NQPROT if (cls.demoplayback == DPB_NETQUAKE) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 57f05343..9b2ea76e 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -6109,7 +6109,7 @@ static void CL_ParseItemTimer(void) atof(Cmd_Argv(2)), atof(Cmd_Argv(3))}; float radius = atof(Cmd_Argv(4)); - //unsigned int rgb = strtoul(Cmd_Argv(5), NULL, 16); + unsigned int rgb = (Cmd_Argc() > 5)?strtoul(Cmd_Argv(5), NULL, 16):0x202020; // char *timername = Cmd_Argv(6); unsigned int entnum = strtoul(Cmd_Argv(7), NULL, 0); struct itemtimer_s *timer; @@ -6139,6 +6139,9 @@ static void CL_ParseItemTimer(void) timer->entnum = entnum; timer->start = cl.time; timer->end = cl.time + timer->duration; + timer->rgb[0] = ((rgb>>16)&0xff)/255.0; + timer->rgb[1] = ((rgb>> 8)&0xff)/255.0; + timer->rgb[2] = ((rgb )&0xff)/255.0; } #ifdef PLUGINS diff --git a/engine/client/client.h b/engine/client/client.h index 5af06e14..31991f19 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -893,6 +893,7 @@ typedef struct float start; float duration; vec3_t origin; + vec3_t rgb; float radius; struct itemtimer_s *next; } *itemtimers; diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index 25c751af..08165916 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -93,8 +93,8 @@ static cvar_t joy_anglesens[3] = }; static cvar_t joy_movesens[3] = { - CVAR("joyforwardsensitivity", "1.0"), - CVAR("joysidesensitivity", "-1.0"), + CVAR("joyforwardsensitivity", "-1.0"), + CVAR("joysidesensitivity", "1.0"), CVAR("joyupsensitivity", "1.0") }; //comments on threshholds comes from microsoft's xinput docs. diff --git a/engine/client/in_sdl.c b/engine/client/in_sdl.c index 32eeba37..2cdbb109 100644 --- a/engine/client/in_sdl.c +++ b/engine/client/in_sdl.c @@ -166,8 +166,12 @@ static struct sdljoy_s *J_DevId(SDL_JoystickID jid) return NULL; } static void J_ControllerAxis(SDL_JoystickID jid, int axis, int value) -{ - int axismap[] = {0,1,3,4,2,5}; +{ //FIXME: sdlaxis 4 and 5 should trigger K_GP_LEFT_TRIGGER and K_GP_RIGHT_TRIGGER + int axismap[] = { +// SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY, SDL_CONTROLLER_AXIS_RIGHTX, + GPAXIS_LT_RIGHT, GPAXIS_LT_DOWN, GPAXIS_RT_RIGHT, +// SDL_CONTROLLER_AXIS_RIGHTY, SDL_CONTROLLER_AXIS_TRIGGERLEFT,SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + GPAXIS_RT_DOWN, GPAXIS_LT_TRIGGER, GPAXIS_RT_TRIGGER}; struct sdljoy_s *joy = J_DevId(jid); if (joy && axis < sizeof(axismap)/sizeof(axismap[0]) && joy->qdevid != DEVID_UNSET) @@ -186,41 +190,23 @@ static void J_ControllerButton(SDL_JoystickID jid, int button, qboolean pressed) { //controllers have reliable button maps. //but that doesn't meant that fte has specific k_ names for those buttons, but the mapping should be reliable, at least until they get mapped to proper k_ values. - int buttonmap[] = { -#if 0 - //NOTE: DP has specific 'X360' buttons for many of these. of course, its not an exact mapping... - K_X360_A, /*SDL_CONTROLLER_BUTTON_A*/ - K_X360_B, /*SDL_CONTROLLER_BUTTON_B*/ - K_X360_X, /*SDL_CONTROLLER_BUTTON_X*/ - K_X360_Y, /*SDL_CONTROLLER_BUTTON_Y*/ - K_X360_BACK, /*SDL_CONTROLLER_BUTTON_BACK*/ - K_AUX2, /*SDL_CONTROLLER_BUTTON_GUIDE*/ - K_X360_START, /*SDL_CONTROLLER_BUTTON_START*/ - K_X360_LEFT_THUMB, /*SDL_CONTROLLER_BUTTON_LEFTSTICK*/ - K_X360_RIGHT_THUMB, /*SDL_CONTROLLER_BUTTON_RIGHTSTICK*/ - K_X360_LEFT_SHOULDER, /*SDL_CONTROLLER_BUTTON_LEFTSHOULDER*/ - K_X360_RIGHT_SHOULDER, /*SDL_CONTROLLER_BUTTON_RIGHTSHOULDER*/ - K_X360_DPAD_UP, /*SDL_CONTROLLER_BUTTON_DPAD_UP*/ - K_X360_DPAD_DOWN, /*SDL_CONTROLLER_BUTTON_DPAD_DOWN*/ - K_X360_DPAD_LEFT, /*SDL_CONTROLLER_BUTTON_DPAD_LEFT*/ - K_X360_DPAD_RIGHT /*SDL_CONTROLLER_BUTTON_DPAD_RIGHT*/ -#else - K_JOY1, /*SDL_CONTROLLER_BUTTON_A*/ - K_JOY2, /*SDL_CONTROLLER_BUTTON_B*/ - K_JOY3, /*SDL_CONTROLLER_BUTTON_X*/ - K_JOY4, /*SDL_CONTROLLER_BUTTON_Y*/ - K_AUX1, /*SDL_CONTROLLER_BUTTON_BACK*/ - K_AUX2, /*SDL_CONTROLLER_BUTTON_GUIDE*/ - K_AUX3, /*SDL_CONTROLLER_BUTTON_START*/ - K_AUX4, /*SDL_CONTROLLER_BUTTON_LEFTSTICK*/ - K_AUX5, /*SDL_CONTROLLER_BUTTON_RIGHTSTICK*/ - K_AUX6, /*SDL_CONTROLLER_BUTTON_LEFTSHOULDER*/ - K_AUX7, /*SDL_CONTROLLER_BUTTON_RIGHTSHOULDER*/ - K_AUX8, /*SDL_CONTROLLER_BUTTON_DPAD_UP*/ - K_AUX9, /*SDL_CONTROLLER_BUTTON_DPAD_DOWN*/ - K_AUX10, /*SDL_CONTROLLER_BUTTON_DPAD_LEFT*/ - K_AUX11 /*SDL_CONTROLLER_BUTTON_DPAD_RIGHT*/ -#endif + int buttonmap[] = + { + K_GP_A, /*SDL_CONTROLLER_BUTTON_A*/ + K_GP_B, /*SDL_CONTROLLER_BUTTON_B*/ + K_GP_X, /*SDL_CONTROLLER_BUTTON_X*/ + K_GP_Y, /*SDL_CONTROLLER_BUTTON_Y*/ + K_GP_BACK, /*SDL_CONTROLLER_BUTTON_BACK*/ + K_GP_GUIDE, /*SDL_CONTROLLER_BUTTON_GUIDE*/ + K_GP_START, /*SDL_CONTROLLER_BUTTON_START*/ + K_GP_LEFT_THUMB, /*SDL_CONTROLLER_BUTTON_LEFTSTICK*/ + K_GP_RIGHT_THUMB, /*SDL_CONTROLLER_BUTTON_RIGHTSTICK*/ + K_GP_LEFT_SHOULDER, /*SDL_CONTROLLER_BUTTON_LEFTSHOULDER*/ + K_GP_RIGHT_SHOULDER, /*SDL_CONTROLLER_BUTTON_RIGHTSHOULDER*/ + K_GP_DPAD_UP, /*SDL_CONTROLLER_BUTTON_DPAD_UP*/ + K_GP_DPAD_DOWN, /*SDL_CONTROLLER_BUTTON_DPAD_DOWN*/ + K_GP_DPAD_LEFT, /*SDL_CONTROLLER_BUTTON_DPAD_LEFT*/ + K_GP_DPAD_RIGHT /*SDL_CONTROLLER_BUTTON_DPAD_RIGHT*/ }; struct sdljoy_s *joy = J_DevId(jid); @@ -721,10 +707,10 @@ static unsigned int tbl_sdltoquake[] = 0,//K_NUMLOCK, //SDLK_NUMLOCK = 300, K_CAPSLOCK, //SDLK_CAPSLOCK = 301, 0,//K_SCROLLOCK,//SDLK_SCROLLOCK= 302, - K_SHIFT, //SDLK_RSHIFT = 303, - K_SHIFT, //SDLK_LSHIFT = 304, - K_CTRL, //SDLK_RCTRL = 305, - K_CTRL, //SDLK_LCTRL = 306, + K_RSHIFT, //SDLK_RSHIFT = 303, + K_LSHIFT, //SDLK_LSHIFT = 304, + K_RCTRL, //SDLK_RCTRL = 305, + K_LCTRL, //SDLK_LCTRL = 306, K_RALT, //SDLK_RALT = 307, K_LALT, //SDLK_LALT = 308, 0, //SDLK_RMETA = 309, diff --git a/engine/client/in_win.c b/engine/client/in_win.c index 2c074c42..bf96ec4b 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -357,6 +357,7 @@ static void INS_HideMouse (void) //scan for an unused device id for joysticks (now that something was pressed). static int Joy_AllocateDevID(void) { + extern cvar_t cl_splitscreen; int id = 0, j; for (id = 0; ; id++) { @@ -366,12 +367,17 @@ static int Joy_AllocateDevID(void) break; } if (j == joy_count) + { + if (id > cl_splitscreen.ival && !*cl_splitscreen.string) + cl_splitscreen.ival = id; return id; + } } } #ifdef USINGRAWINPUT static int Mouse_AllocateDevID(void) { + extern cvar_t cl_splitscreen; int id = 0, j; for (id = 0; ; id++) { @@ -381,11 +387,16 @@ static int Mouse_AllocateDevID(void) break; } if (j == rawmicecount) + { + if (id > cl_splitscreen.ival && !*cl_splitscreen.string) + cl_splitscreen.ival = id; return id; + } } } static int Keyboard_AllocateDevID(void) { + extern cvar_t cl_splitscreen; int id = 0, j; for (id = 0; ; id++) { @@ -395,7 +406,11 @@ static int Keyboard_AllocateDevID(void) break; } if (j == rawkbdcount) + { + if (id > cl_splitscreen.ival && !*cl_splitscreen.string) + cl_splitscreen.ival = id; return id; + } } } #endif @@ -1817,26 +1832,27 @@ void INS_Commands (void) static const int xinputjbuttons[] = { - K_UPARROW, //XINPUT_GAMEPAD_DPAD_UP - K_DOWNARROW, //XINPUT_GAMEPAD_DPAD_DOWN - K_LEFTARROW, //XINPUT_GAMEPAD_DPAD_LEFT - K_RIGHTARROW, //XINPUT_GAMEPAD_DPAD_RIGHT - K_AUX5, //XINPUT_GAMEPAD_START - K_AUX6, //XINPUT_GAMEPAD_BACK - K_AUX3, //XINPUT_GAMEPAD_LEFT_THUMB - K_AUX4, //XINPUT_GAMEPAD_RIGHT_THUMB + K_GP_DPAD_UP, + K_GP_DPAD_DOWN, + K_GP_DPAD_LEFT, + K_GP_DPAD_RIGHT, + K_GP_START, + K_GP_BACK, + K_GP_LEFT_THUMB, + K_GP_RIGHT_THUMB, - K_AUX1, //XINPUT_GAMEPAD_LEFT_SHOULDER - K_AUX2, //XINPUT_GAMEPAD_RIGHT_SHOULDER - K_AUX7, //unused - K_AUX8, //unused - K_JOY2, //XINPUT_GAMEPAD_A - K_JOY4, //XINPUT_GAMEPAD_B - K_JOY1, //XINPUT_GAMEPAD_X - K_JOY3, //XINPUT_GAMEPAD_Y + K_GP_LEFT_SHOULDER, + K_GP_RIGHT_SHOULDER, + K_GP_GUIDE, //officially, this is 'reserved' + K_GP_UNKNOWN, //reserved + K_GP_A, + K_GP_B, + K_GP_X, + K_GP_Y, - K_AUX9, //left trigger - K_AUX10 //right trigger + //not part of xinput specs, but appended by us from analog triggers + K_GP_LEFT_TRIGGER, + K_GP_RIGHT_TRIGGER }; static const int mmjbuttons[32] = { @@ -2009,12 +2025,12 @@ qboolean INS_ReadJoystick (struct wjoy_s *joy) if (joy->devid != DEVID_UNSET) { - IN_JoystickAxisEvent(joy->devid, 0, xistate.Gamepad.sThumbRX / 32768.0); - IN_JoystickAxisEvent(joy->devid, 1, xistate.Gamepad.sThumbRY / 32768.0); - IN_JoystickAxisEvent(joy->devid, 2, xistate.Gamepad.bRightTrigger/255.0); - IN_JoystickAxisEvent(joy->devid, 3, xistate.Gamepad.sThumbLX / 32768.0); - IN_JoystickAxisEvent(joy->devid, 4, xistate.Gamepad.sThumbLY / 32768.0); - IN_JoystickAxisEvent(joy->devid, 5, xistate.Gamepad.bLeftTrigger/255.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_RIGHT, xistate.Gamepad.sThumbLX / 32768.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_DOWN, xistate.Gamepad.sThumbLY / 32768.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_TRIGGER, xistate.Gamepad.bLeftTrigger/255.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_RIGHT, xistate.Gamepad.sThumbRX / 32768.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_DOWN, xistate.Gamepad.sThumbRY / 32768.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_TRIGGER, xistate.Gamepad.bRightTrigger/255.0); vibrator.wLeftMotorSpeed = xinput_leftvibrator.value * 0xffff; vibrator.wRightMotorSpeed = xinput_rightvibrator.value * 0xffff; @@ -2036,12 +2052,12 @@ qboolean INS_ReadJoystick (struct wjoy_s *joy) joy->buttonstate = ji.dwButtons; if (joy->devid != DEVID_UNSET) { - IN_JoystickAxisEvent(joy->devid, 0, (ji.dwXpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 1, (ji.dwYpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 2, (ji.dwZpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 3, (ji.dwRpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 4, (ji.dwUpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 5, (ji.dwVpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_RIGHT, (ji.dwXpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_DOWN, (ji.dwYpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_AUX, (ji.dwZpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_RIGHT, (ji.dwRpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_DOWN, (ji.dwUpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_AUX, (ji.dwVpos - 32768.0) / 32768); } return true; } diff --git a/engine/client/keys.c b/engine/client/keys.c index 21a2d118..51cd46d9 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -260,6 +260,25 @@ keyname_t keynames[] = {"BACKQUOTE", '`'}, {"BACKSLASH", '\\'}, + {"GP_A", K_GP_A}, + {"GP_B", K_GP_B}, + {"GP_X", K_GP_X}, + {"GP_Y", K_GP_Y}, + {"GP_LSHOULDER", K_GP_LEFT_SHOULDER}, + {"GP_RSHOULDER", K_GP_RIGHT_SHOULDER}, + {"GP_LTRIGGER", K_GP_LEFT_TRIGGER}, + {"GP_RTRIGGER", K_GP_RIGHT_TRIGGER}, + {"GP_BACK", K_GP_BACK}, + {"GP_START", K_GP_START}, + {"GP_LTHUMB", K_GP_LEFT_THUMB}, + {"GP_RTHUMB", K_GP_RIGHT_THUMB}, + {"GP_DPAD_UP", K_GP_DPAD_UP}, + {"GP_DPAD_DOWN", K_GP_DPAD_DOWN}, + {"GP_DPAD_LEFT", K_GP_DPAD_LEFT}, + {"GP_DPAD_RIGHT", K_GP_DPAD_RIGHT}, + {"GP_GUIDE", K_GP_GUIDE}, + {"GP_UNKNOWN", K_GP_UNKNOWN}, + {NULL, 0} }; @@ -777,7 +796,10 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info) else { char cmdprefix[6]; - snprintf(cmdprefix, sizeof(cmdprefix), "%i ", i+1); + if (i == 0) + *cmdprefix = 0; + else + snprintf(cmdprefix, sizeof(cmdprefix), "%i ", i+1); //hey look! its you! diff --git a/engine/client/keys.h b/engine/client/keys.h index bee76198..cb90104c 100644 --- a/engine/client/keys.h +++ b/engine/client/keys.h @@ -21,6 +21,43 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef __CLIENT_KEYS_H__ #define __CLIENT_KEYS_H__ +enum +{ //fte's assumed gamepad axis + GPAXIS_LT_RIGHT = 0, + GPAXIS_LT_DOWN = 1, + GPAXIS_LT_AUX = 2, + + GPAXIS_RT_RIGHT = 3, + GPAXIS_RT_DOWN = 4, + GPAXIS_RT_AUX = 5, + +//gah +#define GPAXIS_LT_TRIGGER GPAXIS_LT_AUX +#define GPAXIS_RT_TRIGGER GPAXIS_RT_AUX +}; + +#if 1 +//gamepad alises, because I'm too lazy to define actual keys that are distinct from joysticks +#define K_GP_A K_JOY2 +#define K_GP_B K_JOY4 +#define K_GP_X K_JOY1 +#define K_GP_Y K_JOY3 +#define K_GP_LEFT_SHOULDER K_AUX1 +#define K_GP_RIGHT_SHOULDER K_AUX2 +#define K_GP_LEFT_TRIGGER K_AUX9 +#define K_GP_RIGHT_TRIGGER K_AUX10 +#define K_GP_BACK K_AUX6 +#define K_GP_START K_AUX5 +#define K_GP_LEFT_THUMB K_AUX3 +#define K_GP_RIGHT_THUMB K_AUX4 +#define K_GP_DPAD_UP K_UPARROW +#define K_GP_DPAD_DOWN K_DOWNARROW +#define K_GP_DPAD_LEFT K_LEFTARROW +#define K_GP_DPAD_RIGHT K_RIGHTARROW +#define K_GP_GUIDE K_AUX7 +#define K_GP_UNKNOWN K_AUX8 +#endif + // // these are the key numbers that should be passed to Key_Event // @@ -108,6 +145,22 @@ K_MOUSE10, K_MWHEELUP, K_MWHEELDOWN, // 189 +#ifndef K_GP_A +K_GP_A = 190, +K_GP_B = 191, +K_GP_X = 192, +K_GP_Y = 193, +K_GP_LEFT_SHOULDER = 194, +K_GP_RIGHT_SHOULDER = 195, +K_GP_LEFT_TRIGGER = 196, +K_GP_RIGHT_TRIGGER = 197, +K_GP_BACK = 198, +K_GP_START = 199, +K_GP_LEFT_THUMB = 200, +K_GP_RIGHT_THUMB = 201, +K_GP_GUIDE = 202, +#endif + // // joystick buttons // @@ -163,6 +216,17 @@ K_RCTRL = 246, K_RSHIFT = 247, K_PRINTSCREEN = 248, +//K_UNUSED = 249, +//K_UNUSED = 250, + +#ifndef K_GP_DPAD_UP +K_GP_DPAD_UP = 251, +K_GP_DPAD_DOWN = 252, +K_GP_DPAD_LEFT = 253, +K_GP_DPAD_RIGHT = 254, +K_GP_UNKNOWN = 255, +#endif + K_MAX = 256 }; @@ -172,6 +236,7 @@ K_MAX = 256 #define KEY_MODIFIER_ALTBINDMAP (1<<3) #define KEY_MODIFIERSTATES (1<<4) +//legacy aliases, lest we ever forget! #define K_SHIFT K_LSHIFT #define K_CTRL K_LCTRL #define K_ALT K_LALT diff --git a/engine/client/net_master.c b/engine/client/net_master.c index c54cf0d2..7b411ea4 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -64,6 +64,9 @@ extern cvar_t sv_listen_qw; extern cvar_t sv_listen_nq; extern cvar_t sv_listen_dp; extern cvar_t sv_listen_q3; +#ifdef HAVE_DTLS +extern cvar_t sv_listen_dtls; +#endif typedef struct { enum masterprotocol_e protocol; @@ -3310,7 +3313,7 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad) return; } - MSG_ReadByte (); + MSG_ReadByte (); //should be \n last = firstserver; diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 0d3e4b96..6a6d1195 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -140,6 +140,25 @@ int MP_TranslateFTEtoQCCodes(int code) case K_AUX31: return 814; case K_AUX32: return 815; +#ifndef K_GP_DPAD_UP // these are probably just aliases, so dupe cases. left for completeness. + case K_GP_DPAD_UP: return 816; + case K_GP_DPAD_DOWN: return 817; + case K_GP_DPAD_LEFT: return 818; + case K_GP_DPAD_RIGHT: return 819; + case K_GP_START: return 820; + case K_GP_BACK: return 821; + case K_GP_LEFT_THUMB: return 822; + case K_GP_RIGHT_THUMB: return 823; + case K_GP_LEFT_SHOULDER:return 824; + case K_GP_RIGHT_SHOULDER:return 825; + case K_GP_A: return 826; + case K_GP_B: return 827; + case K_GP_X: return 828; + case K_GP_Y: return 829; + case K_GP_LEFT_TRIGGER: return 830; + case K_GP_RIGHT_TRIGGER:return 831; +#endif + case K_VOLUP: return -code; case K_VOLDOWN: return -code; case K_APP: return -code; @@ -274,6 +293,32 @@ int MP_TranslateQCtoFTECodes(int code) case 813: return K_AUX30; case 814: return K_AUX31; case 815: return K_AUX32; + + //WARNING: these are currently aliases in FTE. + case 816: return K_GP_DPAD_UP; + case 817: return K_GP_DPAD_DOWN; + case 818: return K_GP_DPAD_LEFT; + case 819: return K_GP_DPAD_RIGHT; + case 820: return K_GP_START; + case 821: return K_GP_BACK; + case 822: return K_GP_LEFT_THUMB; + case 823: return K_GP_RIGHT_THUMB; + case 824: return K_GP_LEFT_SHOULDER; + case 825: return K_GP_RIGHT_SHOULDER; + case 826: return K_GP_A; + case 827: return K_GP_B; + case 828: return K_GP_X; + case 829: return K_GP_Y; + case 830: return K_GP_LEFT_TRIGGER; + case 831: return K_GP_RIGHT_TRIGGER; +// case 832: return K_GP_LEFT_THUMB_UP; +// case 833: return K_GP_LEFT_THUMB_DOWN; +// case 834: return K_GP_LEFT_THUMB_LEFT; +// case 835: return K_GP_LEFT_THUMB_RIGHT; +// case 836: return K_GP_RIGHT_THUMB_UP; +// case 837: return K_GP_RIGHT_THUMB_DOWN; +// case 838: return K_GP_RIGHT_THUMB_LEFT; +// case 839: return K_GP_RIGHT_THUMB_RIGHT; default: if (code < 0) //negative values are 'fte-native' keys, for stuff that the api lacks. return -code; diff --git a/engine/client/sys_sdl.c b/engine/client/sys_sdl.c index 19c04d0d..bc91a3da 100644 --- a/engine/client/sys_sdl.c +++ b/engine/client/sys_sdl.c @@ -135,13 +135,12 @@ qboolean Sys_rmdir (const char *path) #if WIN32 ret = _rmdir (path); #else - //user, group, others - ret = rmdir (path, 0755); //WARNING: DO NOT RUN AS ROOT! + ret = rmdir (path); #endif if (ret == 0) return true; - if (errno == ENOENT) - return true; +// if (errno == ENOENT) +// return true; return false; } diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index f0e33286..65fa4e67 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -293,6 +293,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define RTLIGHTS //realtime lighting #endif +// #define QWOVERQ3 //allows qw servers with q3 clients. requires specific cgame. + #define VM_Q1 //q1 qvm gamecode interface //#define VM_LUA //q1 lua gamecode interface @@ -531,6 +533,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #if defined(HAVE_WINSSPI) || defined(HAVE_GNUTLS) #define HAVE_SSL #endif +#if defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) + //FIXME: HAVE_WINSSPI does not work as a server. + //FIXME: advertising dtls without a valid certificate will probably bug out if a client tries to auto-upgrade. +// #define HAVE_DTLS +#endif #if defined(USE_SQLITE) || defined(USE_MYSQL) #define SQL @@ -545,6 +552,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define Q3BSPS //rbsp might as well depend upon q3bsp - its the same thing but with more lightstyles (support for which can bog down the renderer a little). #endif +#if defined(QWOVERQ3) && !defined(Q3SERVER) + #undef QWOVERQ3 +#endif + //fix things a little... #ifdef NPQTV #define NPFTE diff --git a/engine/common/net.h b/engine/common/net.h index 9baf13db..2e3865d7 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -48,7 +48,6 @@ typedef enum { NP_TLS, NP_WS, NP_WSS, - NP_IRC, NP_NATPMP } netproto_t; @@ -114,6 +113,7 @@ void NET_Init (void); void NET_Tick (void); void SVNET_RegisterCvars(void); void NET_InitClient (qboolean loopbackonly); +void NET_CloseClient(void); void NET_InitServer (void); qboolean NET_WasSpecialPacket(netsrc_t netsrc); void NET_CloseServer (void); @@ -161,6 +161,13 @@ qboolean NET_CompareAdrMasked(netadr_t *a, netadr_t *b, netadr_t *mask); qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot, qboolean islisten); +#ifdef HAVE_DTLS +qboolean NET_DTLS_Create(struct ftenet_connections_s *col, netadr_t *to); +qboolean NET_DTLS_Decode(struct ftenet_connections_s *col); +qboolean NET_DTLS_Disconnect(struct ftenet_connections_s *col, netadr_t *to); +void NET_DTLS_Timeouts(struct ftenet_connections_s *col); +#endif + //============================================================================ #define OLD_AVG 0.99 // total = oldtotal*OLD_AVG + new*(1-OLD_AVG) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 1b6d96fa..08ab6d5b 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -575,16 +575,6 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) send.maxsize = MAX_NQMSGLEN + PACKET_HEADER; send.cursize = 0; - if (NET_AddrIsReliable(&chan->remote_address) && chan->reliable_length) - { - //if over tcp, everything is assumed to be reliable. pretend it got acked. - chan->reliable_length = 0; //they got the entire message - chan->reliable_start = 0; - chan->incoming_reliable_acknowledged = chan->reliable_sequence; - chan->reliable_sequence++; - chan->nqreliable_allowed = true; - } - /*unreliables flood out, but reliables are tied to server sequences*/ if (chan->nqreliable_resendtime < realtime) chan->nqreliable_allowed = true; @@ -606,6 +596,9 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) { MSG_WriteLong(&send, 0); MSG_WriteLong(&send, LongSwap(chan->reliable_sequence)); + + //limit the payload length to nq's datagram max size. + //relax the limitation if its reliable (ie: over tcp) where its assumed to have no real limit if (i > MAX_NQDATAGRAM && !NET_AddrIsReliable(&chan->remote_address)) i = MAX_NQDATAGRAM; @@ -623,20 +616,31 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) } else *(int*)send_buf = BigLong(NETFLAG_DATA | send.cursize); - NET_SendPacket (chan->sock, send.cursize, send.data, &chan->remote_address); + chan->bytesout += send.cursize; - sentsize += send.cursize; - if (showpackets.value) Con_Printf ("out %s r s=%i %i\n" , chan->sock == NS_SERVER?"s2c":"c2s" , chan->reliable_sequence , send.cursize); - send.cursize = 0; - chan->nqreliable_allowed = false; chan->nqreliable_resendtime = realtime + 0.3; //resend reliables after 0.3 seconds. nq transports suck. + + if (NET_SendPacket (chan->sock, send.cursize, send.data, &chan->remote_address) == NETERR_SENT && NET_AddrIsReliable(&chan->remote_address)) + { //if over tcp, everything is assumed to be reliable. pretend it got acked now. + //if we get an ack later, then who cares. + chan->reliable_start += i; + if (chan->reliable_start >= chan->reliable_length) + { + chan->reliable_length = 0; //they got the entire message + chan->reliable_start = 0; + } + chan->incoming_reliable_acknowledged = chan->reliable_sequence; + chan->reliable_sequence++; + chan->nqreliable_allowed = true; + } + send.cursize = 0; } } diff --git a/engine/common/net_ice.c b/engine/common/net_ice.c index 219b24a3..867d2b98 100644 --- a/engine/common/net_ice.c +++ b/engine/common/net_ice.c @@ -615,9 +615,8 @@ qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *val #ifndef CLIENTONLY else if (con->proto == ICEP_QWSERVER) { - extern void SVC_GetChallenge(qboolean nodpresponse); net_from = con->chosenpeer; - SVC_GetChallenge(true); + SVC_GetChallenge(false); } #endif if (con->state == ICE_CONNECTED) @@ -873,12 +872,13 @@ qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *value, si if (con->proto == ICEP_QWSERVER || con->proto == ICEP_QWCLIENT) { +#ifdef HAVE_DTLS Q_strncatz(value, "m=application 9 DTLS/SCTP 5000\n", valuelen); +#endif } for (i = 0; i < countof(con->codec); i++) { - int codec = atoi(prop+5); if (!con->codec[i]) continue; @@ -917,7 +917,6 @@ qboolean QDECL ICE_GetLCandidateSDP(struct icestate_s *con, char *out, size_t ou { if (can->dirty) { - struct icecandinfo_s *c = &can->info; can->dirty = false; ICE_CandidateToSDP(can, out, outsize); diff --git a/engine/common/net_ssl_gnutls.c b/engine/common/net_ssl_gnutls.c index b243dd88..b19bfd2c 100644 --- a/engine/common/net_ssl_gnutls.c +++ b/engine/common/net_ssl_gnutls.c @@ -4,12 +4,14 @@ #include "quakedef.h" -#define GNUTLS_DYNAMIC //statically linking is bad, because that just dynamically links to a .so that probably won't exist. - //on the other hand, it does validate that the function types are correct. +#ifndef GNUTLS_STATIC + #define GNUTLS_DYNAMIC //statically linking is bad, because that just dynamically links to a .so that probably won't exist. + //on the other hand, it does validate that the function types are correct. +#endif #ifdef HAVE_GNUTLS -#if defined(_WIN32) && !defined(MINGW) +#if defined(_WIN32) && !defined(MINGW) && 0 #define GNUTLS_VERSION "2.12.23" #define GNUTLS_SOPREFIX "" @@ -92,6 +94,7 @@ typedef int (VARGS gnutls_certificate_verify_function)(gnutls_session_t session) #else #include +#include #define gnutls_connection_end_t unsigned int #if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 3) @@ -152,6 +155,7 @@ static int (VARGS *qgnutls_certificate_set_x509_system_trust)(gnutls_certificate #else static int (VARGS *qgnutls_certificate_set_x509_trust_file)(gnutls_certificate_credentials_t cred, const char * cafile, gnutls_x509_crt_fmt_t type); #endif +static int (VARGS *qgnutls_certificate_set_x509_key_file)(gnutls_certificate_credentials_t res, const char * certfile, const char * keyfile, gnutls_x509_crt_fmt_t type); #ifdef GNUTLS_HAVE_VERIFY3 static int (VARGS *qgnutls_certificate_verify_peers3)(gnutls_session_t session, const char* hostname, unsigned int * status); static int (VARGS *qgnutls_certificate_verification_status_print)(unsigned int status, gnutls_certificate_type_t type, gnutls_datum_t * out, unsigned int flags); @@ -166,6 +170,15 @@ static gnutls_certificate_type_t (VARGS *qgnutls_certificate_type_get)(gnutls_se static void (VARGS *qgnutls_free)(void * ptr); static int (VARGS *qgnutls_server_name_set)(gnutls_session_t session, gnutls_server_name_type_t type, const void * name, size_t name_length); +#ifdef HAVE_DTLS +static int (VARGS *qgnutls_key_generate)(gnutls_datum_t * key, unsigned int key_size); +static void (VARGS *qgnutls_transport_set_pull_timeout_function)(gnutls_session_t session, gnutls_pull_timeout_func func); +static int (VARGS *qgnutls_dtls_cookie_verify)(gnutls_datum_t * key, void *client_data, size_t client_data_size, void *_msg, size_t msg_size, gnutls_dtls_prestate_st * prestate); +static int (VARGS *qgnutls_dtls_cookie_send)(gnutls_datum_t * key, void *client_data, size_t client_data_size, gnutls_dtls_prestate_st * prestate, gnutls_transport_ptr_t ptr, gnutls_push_func push_func); +static void (VARGS *qgnutls_dtls_prestate_set)(gnutls_session_t session, gnutls_dtls_prestate_st * prestate); +static void (VARGS *qgnutls_dtls_set_mtu)(gnutls_session_t session, unsigned int mtu); +#endif + static qboolean Init_GNUTLS(void) { #ifdef GNUTLS_HAVE_SYSTEMTRUST @@ -186,6 +199,18 @@ static qboolean Init_GNUTLS(void) GNUTLS_FUNC(gnutls_certificate_get_peers) #endif +#ifdef HAVE_DTLS +#define GNUTLS_DTLS_STUFF \ + GNUTLS_FUNC(gnutls_key_generate) \ + GNUTLS_FUNC(gnutls_transport_set_pull_timeout_function) \ + GNUTLS_FUNC(gnutls_dtls_cookie_verify) \ + GNUTLS_FUNC(gnutls_dtls_cookie_send) \ + GNUTLS_FUNC(gnutls_dtls_prestate_set) \ + GNUTLS_FUNC(gnutls_dtls_set_mtu) +#else + #define GNUTLS_DTLS_STUFF +#endif + #define GNUTLS_FUNCS \ GNUTLS_FUNC(gnutls_bye) \ @@ -208,10 +233,12 @@ static qboolean Init_GNUTLS(void) GNUTLS_FUNC(gnutls_session_get_ptr) \ GNUTLS_FUNC(gnutls_session_set_ptr) \ GNUTLS_TRUSTFUNCS \ + GNUTLS_FUNC(gnutls_certificate_set_x509_key_file) \ GNUTLS_VERIFYFUNCS \ GNUTLS_FUNC(gnutls_certificate_type_get) \ GNUTLS_FUNC(gnutls_free) \ GNUTLS_FUNC(gnutls_server_name_set) \ + GNUTLS_DTLS_STUFF #ifdef GNUTLS_DYNAMIC dllhandle_t *hmod; @@ -247,6 +274,7 @@ static qboolean Init_GNUTLS(void) #else {(void**)&qgnutls_certificate_set_x509_trust_file, "gnutls_certificate_set_x509_trust_file"}, #endif + {(void**)&qgnutls_certificate_set_x509_key_file, "gnutls_certificate_set_x509_key_file"}, #ifdef GNUTLS_HAVE_VERIFY3 {(void**)&qgnutls_certificate_verify_peers3, "gnutls_certificate_verify_peers3"}, {(void**)&qgnutls_certificate_verification_status_print, "gnutls_certificate_verification_status_print"}, @@ -260,6 +288,16 @@ static qboolean Init_GNUTLS(void) {(void**)&qgnutls_certificate_type_get, "gnutls_certificate_type_get"}, {(void**)&qgnutls_free, "gnutls_free"}, {(void**)&qgnutls_server_name_set, "gnutls_server_name_set"}, + +#ifdef HAVE_DTLS + {(void**)&qgnutls_key_generate, "gnutls_key_generate"}, + {(void**)&qgnutls_transport_set_pull_timeout_function, "gnutls_transport_set_pull_timeout_function"}, + {(void**)&qgnutls_dtls_cookie_verify, "gnutls_dtls_cookie_verify"}, + {(void**)&qgnutls_dtls_cookie_send, "gnutls_dtls_cookie_send"}, + {(void**)&qgnutls_dtls_prestate_set, "gnutls_dtls_prestate_set"}, + {(void**)&qgnutls_dtls_set_mtu, "gnutls_dtls_set_mtu"}, +#endif + {NULL, NULL} }; @@ -282,11 +320,6 @@ static qboolean Init_GNUTLS(void) return true; } -struct sslbuf -{ - char data[8192]; - int avail; -}; typedef struct { vfsfile_t funcs; @@ -298,10 +331,13 @@ typedef struct qboolean handshaking; qboolean datagram; - struct sslbuf outplain; - struct sslbuf outcrypt; - struct sslbuf inplain; - struct sslbuf incrypt; + qboolean challenging; //not sure this is actually needed, but hey. + void *cbctx; + neterr_t(*cbpush)(void *cbctx, const qbyte *data, size_t datasize); + qbyte *readdata; + size_t readsize; + gnutls_dtls_prestate_st prestate; +// int mtu; } gnutlsfile_t; #define CAFILE "/etc/ssl/certs/ca-certificates.crt" @@ -456,7 +492,10 @@ int SSL_DoHandshake(gnutlsfile_t *file) int err; //session was previously closed = error if (!file->session) + { + Sys_Printf("null session\n"); return -1; + } err = qgnutls_handshake (file->session); if (err < 0) @@ -466,7 +505,7 @@ int SSL_DoHandshake(gnutlsfile_t *file) return 0; //certificate errors etc -// qgnutls_perror (err); + qgnutls_perror (err); SSL_Close(&file->funcs); // Con_Printf("%s: abort\n", file->certname); @@ -560,6 +599,7 @@ static qofs_t QDECL SSL_GetLen (struct vfsfile_s *file) static ssize_t SSL_Push(gnutls_transport_ptr_t p, const void *data, size_t size) { gnutlsfile_t *file = p; +// Sys_Printf("SSL_Push: %u\n", size); int done = VFS_WRITE(file->stream, data, size); if (!done) { @@ -571,32 +611,10 @@ static ssize_t SSL_Push(gnutls_transport_ptr_t p, const void *data, size_t size) return 0; return done; } -/*static ssize_t SSL_PushV(gnutls_transport_ptr_t p, giovec_t *iov, int iovcnt) -{ - int i; - ssize_t written; - ssize_t total; - gnutlsfile_t *file = p; - for (i = 0; i < iovcnt; i++) - { - written = SSL_Push(file, iov[i].iov_base, iov[i].iov_len); - if (written <= 0) - break; - total += written; - if (written < iov[i].iov_len) - break; - } - if (!total) - { - qgnutls_transport_set_errno(file->session, EAGAIN); - return -1; - } - qgnutls_transport_set_errno(file->session, 0); - return total; -}*/ static ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size) { gnutlsfile_t *file = p; +// Sys_Printf("SSL_Pull: %u\n", size); int done = VFS_READ(file->stream, data, size); if (!done) { @@ -611,62 +629,174 @@ static ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size) return done; } -vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, qboolean datagram) +static ssize_t DTLS_Push(gnutls_transport_ptr_t p, const void *data, size_t size) +{ + gnutlsfile_t *file = p; + + neterr_t ne = file->cbpush(file->cbctx, data, size); + +// Sys_Printf("DTLS_Push: %u, err=%i\n", (unsigned)size, (int)ne); + + switch(ne) + { + case NETERR_CLOGGED: + qgnutls_transport_set_errno(file->session, EAGAIN); + return -1; + case NETERR_MTU: + qgnutls_transport_set_errno(file->session, EMSGSIZE); + return -1; + case NETERR_DISCONNECTED: + qgnutls_transport_set_errno(file->session, EPERM); + return -1; + default: + qgnutls_transport_set_errno(file->session, 0); + return size; + } +} +static ssize_t DTLS_Pull(gnutls_transport_ptr_t p, void *data, size_t size) +{ + gnutlsfile_t *file = p; + +// Sys_Printf("DTLS_Pull: %u of %u\n", size, file->readsize); + + if (!file->readsize) + { //no data left +// Sys_Printf("DTLS_Pull: EAGAIN\n"); + qgnutls_transport_set_errno(file->session, EAGAIN); + return -1; + } + else if (file->readsize > size) + { //buffer passed is smaller than available data +// Sys_Printf("DTLS_Pull: EMSGSIZE\n"); + memcpy(data, file->readdata, size); + file->readsize = 0; + qgnutls_transport_set_errno(file->session, EMSGSIZE); + return -1; + } + else + { //buffer is big enough to read it all + size = file->readsize; + file->readsize = 0; +// Sys_Printf("DTLS_Pull: reading %i\n", size); + memcpy(data, file->readdata, size); + qgnutls_transport_set_errno(file->session, 0); + return size; + } +} +static int DTLS_Pull_Timeout(gnutls_transport_ptr_t p, unsigned int timeout) +{ //gnutls (pointlessly) requires this function for dtls. + gnutlsfile_t *f = p; +// Sys_Printf("DTLS_Pull_Timeout %i, %i\n", timeout, f->readsize); + return f->readsize>0?1:0; +} + +#ifdef USE_ANON +static gnutls_anon_client_credentials_t anoncred[2]; +#else +static gnutls_certificate_credentials_t xcred[2]; +#endif +static gnutls_datum_t cookie_key; + +qboolean SSL_InitGlobal(qboolean isserver) +{ + static int initstatus[2]; + if (!initstatus[isserver]) + { + if (!Init_GNUTLS()) + { + Con_Printf("GnuTLS "GNUTLS_VERSION" library not available.\n"); + return false; + } + initstatus[isserver] = true; + qgnutls_global_init (); + + if (isserver) + qgnutls_key_generate(&cookie_key, GNUTLS_COOKIE_KEY_SIZE); + + +#ifdef USE_ANON + qgnutls_anon_allocate_client_credentials (&anoncred[isserver]); +#else + + qgnutls_certificate_allocate_credentials (&xcred[isserver]); + +#ifdef GNUTLS_HAVE_SYSTEMTRUST + qgnutls_certificate_set_x509_system_trust (xcred[isserver]); +#else + qgnutls_certificate_set_x509_trust_file (xcred[isserver], CAFILE, GNUTLS_X509_FMT_PEM); +#endif + + if (isserver) + { +#define KEYFILE "c:/games/tools/ssl/key.pem" +#define CERTFILE "c:/games/tools/ssl/cert.pem" + int ret = qgnutls_certificate_set_x509_key_file(xcred[isserver], CERTFILE, KEYFILE, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + Con_Printf("No certificate or key were found\n"); + initstatus[isserver] = -1; + } + } + else + qgnutls_certificate_set_verify_function (xcred[isserver], SSL_CheckCert); +#endif + } + + if (initstatus[isserver] < 0) + return false; + return true; +} +qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboolean datagram) +{ + // Initialize TLS session + qgnutls_init (&newf->session, GNUTLS_NONBLOCK|(isserver?GNUTLS_SERVER:GNUTLS_CLIENT)|(datagram?GNUTLS_DATAGRAM:0)); + + if (!isserver) + qgnutls_server_name_set(newf->session, GNUTLS_NAME_DNS, newf->certname, strlen(newf->certname)); + + qgnutls_session_set_ptr(newf->session, newf); + +#ifdef USE_ANON + //qgnutls_kx_set_priority (newf->session, kx_prio); + qgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred[isserver]); +#else +//#if GNUTLS_VERSION_MAJOR >= 3 + //gnutls_priority_set_direct(); +//#else + //qgnutls_certificate_type_set_priority (newf->session, cert_type_priority); +//#endif + qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, xcred[isserver]); +#endif + // Use default priorities + qgnutls_set_default_priority (newf->session); + + // tell gnutls how to send/receive data + qgnutls_transport_set_ptr (newf->session, newf); + qgnutls_transport_set_push_function(newf->session, datagram?DTLS_Push:SSL_Push); + //qgnutls_transport_set_vec_push_function(newf->session, SSL_PushV); + qgnutls_transport_set_pull_function(newf->session, datagram?DTLS_Pull:SSL_Pull); + if (datagram) + qgnutls_transport_set_pull_timeout_function(newf->session, DTLS_Pull_Timeout); + +// if (isserver) //don't bother to auth any client certs +// qgnutls_certificate_server_set_request(newf->session, GNUTLS_CERT_IGNORE); + + newf->handshaking = true; + + return true; +} + +vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean isserver) { gnutlsfile_t *newf; - qboolean anon = false; - - static gnutls_anon_client_credentials_t anoncred; - static gnutls_certificate_credentials_t xcred; - -// long _false = false; -// long _true = true; - - /* Need to enable anonymous KX specifically. */ -// const int kx_prio[] = {GNUTLS_KX_ANON_DH, 0}; -// const int cert_type_priority[3] = {GNUTLS_CRT_X509, 0}; if (!source) return NULL; - if (datagram) - { - VFS_CLOSE(source); - return NULL; - } - -#ifdef GNUTLS_DATAGRAM - if (datagram) - return NULL; -#endif - - { - static qboolean needinit = true; - if (needinit) - { - if (!Init_GNUTLS()) - { - Con_Printf("GnuTLS "GNUTLS_VERSION" library not available.\n"); - VFS_CLOSE(source); - return NULL; - } - qgnutls_global_init (); - - qgnutls_anon_allocate_client_credentials (&anoncred); - qgnutls_certificate_allocate_credentials (&xcred); - -#ifdef GNUTLS_HAVE_SYSTEMTRUST - qgnutls_certificate_set_x509_system_trust (xcred); -#else - qgnutls_certificate_set_x509_trust_file (xcred, CAFILE, GNUTLS_X509_FMT_PEM); -#endif - qgnutls_certificate_set_verify_function (xcred, SSL_CheckCert); - - needinit = false; - } - } - - newf = Z_Malloc(sizeof(*newf)); + if (!SSL_InitGlobal(isserver)) + newf = NULL; + else + newf = Z_Malloc(sizeof(*newf)); if (!newf) { VFS_CLOSE(source); @@ -682,51 +812,189 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, newf->funcs.Tell = SSL_Tell; newf->funcs.seekstyle = SS_UNSEEKABLE; - Q_strncpyz(newf->certname, hostname, sizeof(newf->certname)); - - // Initialize TLS session - qgnutls_init (&newf->session, GNUTLS_CLIENT/*|(datagram?GNUTLS_DATAGRAM:0)*/); - - qgnutls_server_name_set(newf->session, GNUTLS_NAME_DNS, newf->certname, strlen(newf->certname)); - - qgnutls_session_set_ptr(newf->session, newf); - - // Use default priorities - qgnutls_set_default_priority (newf->session); - if (anon) - { - //qgnutls_kx_set_priority (newf->session, kx_prio); - qgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred); - } + if (hostname) + Q_strncpyz(newf->certname, hostname, sizeof(newf->certname)); else + Q_strncpyz(newf->certname, "", sizeof(newf->certname)); + + if (!SSL_InitConnection(newf, isserver, false)) { -//#if GNUTLS_VERSION_MAJOR >= 3 - //gnutls_priority_set_direct(); -//#else - //qgnutls_certificate_type_set_priority (newf->session, cert_type_priority); -//#endif - qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, xcred); + VFS_CLOSE(&newf->funcs); + return NULL; } - // tell gnutls how to send/receive data - qgnutls_transport_set_ptr (newf->session, newf); - qgnutls_transport_set_push_function(newf->session, SSL_Push); - //qgnutls_transport_set_vec_push_function(newf->session, SSL_PushV); - qgnutls_transport_set_pull_function(newf->session, SSL_Pull); - //qgnutls_transport_set_pull_timeout_function(newf->session, NULL); - - newf->handshaking = true; - return &newf->funcs; } -/* -vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, qboolean datagram) + + +#ifdef HAVE_DTLS + +void DTLS_DestroyContext(void *ctx) { - if (source) - VFS_CLOSE(source); - return NULL; + SSL_Close(ctx); +} +qboolean DTLS_HasServerCertificate(void) +{ + if (!SSL_InitGlobal(true)) + return false; + return true; +} +void *DTLS_CreateContext(void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver) +{ + gnutlsfile_t *newf; + + if (!SSL_InitGlobal(isserver)) + newf = NULL; + else + newf = Z_Malloc(sizeof(*newf)); + if (!newf) + return NULL; + newf->datagram = true; + newf->cbctx = cbctx; + newf->cbpush = push; + newf->challenging = isserver; + +// Sys_Printf("DTLS_CreateContext: server=%i\n", isserver); + + Q_strncpyz(newf->certname, "", sizeof(newf->certname)); + + if (!SSL_InitConnection(newf, isserver, true)) + { + SSL_Close(&newf->funcs); + return NULL; + } + + return newf; +} + +neterr_t DTLS_Transmit(void *ctx, const qbyte *data, size_t datasize) +{ + int ret; + gnutlsfile_t *f = (gnutlsfile_t *)ctx; +// Sys_Printf("DTLS_Transmit: %u\n", datasize); +// Sys_Printf("%su\n", data); + + if (f->challenging) + return NETERR_CLOGGED; + if (f->handshaking) + { + ret = SSL_DoHandshake(f); + if (!ret) + return NETERR_CLOGGED; + if (ret < 0) + return NETERR_DISCONNECTED; + } + + ret = qgnutls_record_send(f->session, data, datasize); + if (ret < 0) + { + if (ret == GNUTLS_E_LARGE_PACKET) + return NETERR_MTU; +//Sys_Error("qgnutls_record_send returned %i\n", ret); + + if (qgnutls_error_is_fatal(ret)) + return NETERR_DISCONNECTED; + return NETERR_CLOGGED; + } + return NETERR_SENT; +} + +neterr_t DTLS_Received(void *ctx, qbyte *data, size_t datasize) +{ + int cli_addr = 0xdeadbeef; + int ret; + gnutlsfile_t *f = (gnutlsfile_t *)ctx; + +//Sys_Printf("DTLS_Received: %u\n", datasize); + + if (f->challenging) + { + memset(&f->prestate, 0, sizeof(f->prestate)); + ret = qgnutls_dtls_cookie_verify(&cookie_key, + &cli_addr, sizeof(cli_addr), + data, datasize, + &f->prestate); + + if (ret < 0) + { +//Sys_Printf("Sending cookie\n"); + qgnutls_dtls_cookie_send(&cookie_key, + &cli_addr, sizeof(cli_addr), + &f->prestate, + (gnutls_transport_ptr_t)f, DTLS_Push); + return NETERR_CLOGGED; + } +//Sys_Printf("Got correct cookie\n"); + f->challenging = false; + + qgnutls_dtls_prestate_set(f->session, &f->prestate); + qgnutls_dtls_set_mtu(f->session, 1440); + +// qgnutls_transport_set_push_function(f->session, DTLS_Push); +// qgnutls_transport_set_pull_function(f->session, DTLS_Pull); + f->handshaking = true; + } + + f->readdata = data; + f->readsize = datasize; + + if (f->handshaking) + { + ret = SSL_DoHandshake(f); + if (ret <= 0) + f->readsize = 0; + if (!ret) + return NETERR_CLOGGED; + if (ret < 0) + return NETERR_DISCONNECTED; + } + + ret = qgnutls_record_recv(f->session, net_message_buffer, sizeof(net_message_buffer)); +//Sys_Printf("DTLS_Received returned %i of %i\n", ret, f->readsize); + f->readsize = 0; + if (ret <= 0) + { + if (!ret) + { +// Sys_Printf("DTLS_Received peer terminated connection\n"); + return NETERR_DISCONNECTED; + } + if (qgnutls_error_is_fatal(ret)) + { +// Sys_Printf("DTLS_Received fail error\n"); + return NETERR_DISCONNECTED; + } +// Sys_Printf("DTLS_Received temp error\n"); + return NETERR_CLOGGED; + } + net_message.cursize = ret; + data[ret] = 0; +// Sys_Printf("DTLS_Received returned %s\n", data); + return NETERR_SENT; +} + +neterr_t DTLS_Timeouts(void *ctx) +{ + gnutlsfile_t *f = (gnutlsfile_t *)ctx; + int ret; + if (f->challenging) + return NETERR_CLOGGED; + if (f->handshaking) + { + f->readsize = 0; + ret = SSL_DoHandshake(f); + f->readsize = 0; + if (!ret) + return NETERR_CLOGGED; + if (ret < 0) + return NETERR_DISCONNECTED; + +// Sys_Printf("handshaking over?\n"); + } + return NETERR_SENT; } -*/ +#endif + #endif diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index 0a0ac70b..d4353422 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -1,5 +1,12 @@ #include "quakedef.h" #if defined(HAVE_WINSSPI) + +/*regarding HAVE_DTLS +DTLS1.0 is supported from win8 onwards +Its also meant to be supported from some RDP server patch on win7, but I can't get it to work. +I've given up for now. +*/ + cvar_t *tls_ignorecertificateerrors; #include "winquake.h" @@ -32,6 +39,10 @@ cvar_t *tls_ignorecertificateerrors; #define SCH_CRED_SNI_CREDENTIAL 0x00080000 #endif +#define SEC_I_MESSAGE_FRAGMENT 0x00090364L +#define SEC_E_INVALID_PARAMETER 0x8009035DL + + //hungarian ensures we hit no macros. static struct { @@ -39,7 +50,7 @@ static struct SECURITY_STATUS (WINAPI *pDecryptMessage) (PCtxtHandle,PSecBufferDesc,ULONG,PULONG); SECURITY_STATUS (WINAPI *pEncryptMessage) (PCtxtHandle,ULONG,PSecBufferDesc,ULONG); SECURITY_STATUS (WINAPI *pAcquireCredentialsHandleA) (SEC_CHAR*,SEC_CHAR*,ULONG,PLUID,PVOID,SEC_GET_KEY_FN,PVOID,PCredHandle,PTimeStamp); - SECURITY_STATUS (WINAPI *pInitializeSecurityContextA) (PCredHandle,PCtxtHandle,SEC_CHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); +// SECURITY_STATUS (WINAPI *pInitializeSecurityContextA) (PCredHandle,PCtxtHandle,SEC_CHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); SECURITY_STATUS (WINAPI *pInitializeSecurityContextW) (PCredHandle,PCtxtHandle,SEC_WCHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); SECURITY_STATUS (WINAPI *pAcceptSecurityContext) (PCredHandle,PCtxtHandle,PSecBufferDesc,unsigned long,unsigned long,PCtxtHandle,PSecBufferDesc,unsigned long SEC_FAR *,PTimeStamp); SECURITY_STATUS (WINAPI *pCompleteAuthToken) (PCtxtHandle,PSecBufferDesc); @@ -65,7 +76,7 @@ void SSL_Init(void) {(void**)&secur.pDecryptMessage, "DecryptMessage"}, {(void**)&secur.pEncryptMessage, "EncryptMessage"}, {(void**)&secur.pAcquireCredentialsHandleA, "AcquireCredentialsHandleA"}, - {(void**)&secur.pInitializeSecurityContextA, "InitializeSecurityContextA"}, +// {(void**)&secur.pInitializeSecurityContextA, "InitializeSecurityContextA"}, {(void**)&secur.pInitializeSecurityContextW, "InitializeSecurityContextW"}, {(void**)&secur.pAcceptSecurityContext, "AcceptSecurityContext"}, {(void**)&secur.pCompleteAuthToken, "CompleteAuthToken"}, @@ -98,14 +109,13 @@ qboolean SSL_Inited(void) return !!secur.lib && !!crypt.lib; } -#define MessageAttribute (ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MANUAL_CRED_VALIDATION) +#define MessageAttribute (ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MANUAL_CRED_VALIDATION) struct sslbuf { size_t datasize; char *data; size_t avail; - size_t newd; }; typedef struct { @@ -137,6 +147,11 @@ typedef struct { SecHandle sechnd; int headersize, footersize; char headerdata[1024], footerdata[1024]; + +#ifdef HAVE_DTLS + void *cbctx; + void (*transmit)(void *cbctx, qbyte *data, size_t datasize); +#endif } sslfile_t; static int SSPI_ExpandBuffer(struct sslbuf *buf, size_t bytes) @@ -162,8 +177,15 @@ static int SSPI_CopyIntoBuffer(struct sslbuf *buf, const void *data, unsigned in static void SSPI_Error(sslfile_t *f, char *error, ...) { + va_list argptr; + char string[1024]; + va_start (argptr, error); + vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + f->handshaking = HS_ERROR; - Sys_Printf("%s", error); + if (*string) + Sys_Printf("%s", string); if (f->stream) VFS_CLOSE(f->stream); @@ -176,7 +198,17 @@ static void SSPI_TryFlushCryptOut(sslfile_t *f) { int sent; if (f->outcrypt.avail) + { +#ifdef HAVE_DTLS + if (f->transmit) + { + f->transmit(f->cbctx, f->outcrypt.data, f->outcrypt.avail); + f->outcrypt.avail = 0; + return; + } +#endif sent = VFS_WRITE(f->stream, f->outcrypt.data, f->outcrypt.avail); + } else return; @@ -215,7 +247,7 @@ static void SSPI_Decode(sslfile_t *f) return; BuffDesc.ulVersion = SECBUFFER_VERSION; - BuffDesc.cBuffers = 4; + BuffDesc.cBuffers = countof(SecBuff); BuffDesc.pBuffers = SecBuff; SecBuff[0].BufferType = SECBUFFER_DATA; @@ -238,6 +270,7 @@ static void SSPI_Decode(sslfile_t *f) } switch(ss) { + case SEC_E_DECRYPT_FAILURE: SSPI_Error(f, "DecryptMessage failed: SEC_E_DECRYPT_FAILURE\n", ss); break; case SEC_E_INVALID_HANDLE: SSPI_Error(f, "DecryptMessage failed: SEC_E_INVALID_HANDLE\n"); break; default: SSPI_Error(f, "DecryptMessage failed: %0#lx\n", ss); break; } @@ -318,6 +351,8 @@ static void SSPI_Encode(sslfile_t *f) SecBuff[2].pvBuffer = f->footerdata; SecBuff[3].BufferType = SECBUFFER_EMPTY; + SecBuff[3].cbBuffer = 0; + SecBuff[3].pvBuffer = NULL; ss = secur.pEncryptMessage(&f->sechnd, ulQop, &BuffDesc, 0); @@ -367,9 +402,19 @@ static struct }; char *narrowen(char *out, size_t outlen, wchar_t *wide); -static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, size_t datasize) +static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, size_t datasize, qboolean datagram) { int i; + if (datagram) + { + Con_Printf("FIXME: Ring of trust not yet implemented\n"); + if (status == CERT_E_UNTRUSTEDROOT) + { + Con_Printf("Allowing (probably) self-signed cert.\n"); + status = SEC_E_OK; + } + return status; + } for (i = 0; knowncerts[i].hostname; i++) { if (!wcscmp(domain, knowncerts[i].hostname)) @@ -409,7 +454,7 @@ static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, return status; } -static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServerName, DWORD dwCertFlags) +static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServerName, DWORD dwCertFlags, qboolean datagram) { HTTPSPolicyCallbackData polHttps; CERT_CHAIN_POLICY_PARA PolicyPara; @@ -465,7 +510,7 @@ static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServe } else { - Status = VerifyKnownCertificates(PolicyStatus.dwError, pwszServerName, pServerCert->pbCertEncoded, pServerCert->cbCertEncoded); + Status = VerifyKnownCertificates(PolicyStatus.dwError, pwszServerName, pServerCert->pbCertEncoded, pServerCert->cbCertEncoded, datagram); if (Status) { char fmsg[512]; @@ -598,14 +643,18 @@ static void SSPI_Handshake (sslfile_t *f) SECURITY_STATUS ss; TimeStamp Lifetime; SecBufferDesc OutBuffDesc; - SecBuffer OutSecBuff[2]; + SecBuffer OutSecBuff[8]; SecBufferDesc InBuffDesc; - SecBuffer InSecBuff[3]; + SecBuffer InSecBuff[8]; ULONG ContextAttributes; SCHANNEL_CRED SchannelCred; + int i; + qboolean retries = 5; - char buf1[128]; - char buf2[128]; +// char buf1[128]; +// char buf2[128]; + +retry: if (f->outcrypt.avail) { @@ -618,16 +667,19 @@ static void SSPI_Handshake (sslfile_t *f) //FIXME: skip this if we've had no new data since last time OutBuffDesc.ulVersion = SECBUFFER_VERSION; - OutBuffDesc.cBuffers = 2; + OutBuffDesc.cBuffers = countof(OutSecBuff); OutBuffDesc.pBuffers = OutSecBuff; - OutSecBuff[0].cbBuffer = f->outcrypt.datasize - f->outcrypt.avail; OutSecBuff[0].BufferType = SECBUFFER_TOKEN; + OutSecBuff[0].cbBuffer = f->outcrypt.datasize - f->outcrypt.avail; OutSecBuff[0].pvBuffer = f->outcrypt.data + f->outcrypt.avail; - OutSecBuff[1].BufferType = 16;//SECBUFFER_TARGET_HOST; - OutSecBuff[1].pvBuffer = buf1; - OutSecBuff[1].cbBuffer = sizeof(buf1); + for (i = 0; i < OutBuffDesc.cBuffers; i++) + { + OutSecBuff[i].BufferType = SECBUFFER_EMPTY; + OutSecBuff[i].pvBuffer = NULL; + OutSecBuff[i].cbBuffer = 0; + } if (f->handshaking == HS_ERROR) return; //gave up. @@ -653,29 +705,52 @@ static void SSPI_Handshake (sslfile_t *f) else if (f->handshaking == HS_CLIENT) { //only if we actually have data. - if (!f->incrypt.avail) + if (!f->incrypt.avail && !f->datagram) return; InBuffDesc.ulVersion = SECBUFFER_VERSION; - InBuffDesc.cBuffers = 2; + InBuffDesc.cBuffers = 4; InBuffDesc.pBuffers = InSecBuff; - InSecBuff[0].BufferType = SECBUFFER_TOKEN; - InSecBuff[0].cbBuffer = f->incrypt.avail; - InSecBuff[0].pvBuffer = f->incrypt.data; + i = 0; - InSecBuff[1].BufferType = SECBUFFER_EMPTY; - InSecBuff[1].pvBuffer = NULL; - InSecBuff[1].cbBuffer = 0; + if (f->incrypt.avail) + { + InSecBuff[i].BufferType = SECBUFFER_TOKEN; + InSecBuff[i].cbBuffer = f->incrypt.avail; + InSecBuff[i].pvBuffer = f->incrypt.data; + i++; + } - ss = secur.pInitializeSecurityContextA (&f->cred, &f->sechnd, NULL, MessageAttribute|(f->datagram?ISC_REQ_DATAGRAM:ISC_REQ_STREAM), 0, SECURITY_NATIVE_DREP, &InBuffDesc, 0, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime); + for (; i < InBuffDesc.cBuffers; i++) + { + InSecBuff[i].BufferType = SECBUFFER_EMPTY; + InSecBuff[i].pvBuffer = NULL; + InSecBuff[i].cbBuffer = 0; + } + + ss = secur.pInitializeSecurityContextW (&f->cred, &f->sechnd, NULL, MessageAttribute|(f->datagram?ISC_REQ_DATAGRAM:ISC_REQ_STREAM), 0, SECURITY_NETWORK_DREP, &InBuffDesc, 0, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime); if (ss == SEC_E_INCOMPLETE_MESSAGE) { - if (f->incrypt.avail == f->incrypt.datasize) +// Con_Printf("SEC_E_INCOMPLETE_MESSAGE\n"); + if (!f->datagram && f->incrypt.avail == f->incrypt.datasize) SSPI_ExpandBuffer(&f->incrypt, f->incrypt.datasize+1024); return; } + else if (ss == SEC_E_INVALID_TOKEN) + { +// Con_Printf("SEC_E_INVALID_TOKEN\n"); + if (f->datagram) + return; //our udp protocol may have non-dtls packets mixed in. besides, we don't want to die from spoofed packets. + } +// else if (ss == SEC_I_MESSAGE_FRAGMENT) +// Con_Printf("SEC_I_MESSAGE_FRAGMENT\n"); +// else if (ss == SEC_I_CONTINUE_NEEDED) +// Con_Printf("SEC_I_CONTINUE_NEEDED\n"); +// else +// Con_Printf("InitializeSecurityContextA %x\n", ss); + //any extra data should still remain for the next time around. this might be more handshake data or payload data. if (InSecBuff[1].BufferType == SECBUFFER_EXTRA) @@ -692,29 +767,49 @@ static void SSPI_Handshake (sslfile_t *f) return; InBuffDesc.ulVersion = SECBUFFER_VERSION; - InBuffDesc.cBuffers = 3; + InBuffDesc.cBuffers = countof(InSecBuff); InBuffDesc.pBuffers = InSecBuff; + i = 0; - InSecBuff[0].BufferType = SECBUFFER_TOKEN; - InSecBuff[0].cbBuffer = f->incrypt.avail; - InSecBuff[0].pvBuffer = f->incrypt.data; - - InSecBuff[1].BufferType = SECBUFFER_EMPTY; - InSecBuff[1].pvBuffer = NULL; - InSecBuff[1].cbBuffer = 0; - - InSecBuff[2].BufferType = 16;//SECBUFFER_TARGET_HOST; - InSecBuff[2].pvBuffer = buf2; - InSecBuff[2].cbBuffer = sizeof(buf2); - - ss = secur.pAcceptSecurityContext(&f->cred, (f->handshaking==HS_SERVER)?&f->sechnd:NULL, &InBuffDesc, ASC_REQ_ALLOCATE_MEMORY|ASC_REQ_STREAM|ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime); - - if (ss == SEC_E_INCOMPLETE_MESSAGE) + if (f->incrypt.avail) { - if (f->incrypt.avail == f->incrypt.datasize) + InSecBuff[i].BufferType = SECBUFFER_TOKEN; + InSecBuff[i].cbBuffer = f->incrypt.avail; + InSecBuff[i].pvBuffer = f->incrypt.data; + i++; + } + + for (; i < InBuffDesc.cBuffers; i++) + { + InSecBuff[i].BufferType = SECBUFFER_EMPTY; + InSecBuff[i].pvBuffer = NULL; + InSecBuff[i].cbBuffer = 0; + } + + i = 1; + OutSecBuff[i++].BufferType = SECBUFFER_EXTRA; + OutSecBuff[i++].BufferType = 17/*SECBUFFER_ALERT*/; + +#define ServerMessageAttribute (ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | ASC_REQ_CONFIDENTIALITY /*| ASC_REQ_EXTENDED_ERROR*/ | ASC_REQ_ALLOCATE_MEMORY) + + ss = secur.pAcceptSecurityContext(&f->cred, (f->handshaking==HS_SERVER)?&f->sechnd:NULL, &InBuffDesc, + ServerMessageAttribute|(f->datagram?ASC_REQ_DATAGRAM:ASC_REQ_STREAM), SECURITY_NETWORK_DREP, &f->sechnd, + &OutBuffDesc, &ContextAttributes, NULL); + if (ss == SEC_E_INVALID_TOKEN) + { +// Con_Printf("SEC_E_INVALID_TOKEN\n"); + if (f->datagram) + return; + } + else if (ss == SEC_E_INCOMPLETE_MESSAGE) + { +// Con_Printf("SEC_E_INCOMPLETE_MESSAGE\n"); + if (!f->datagram && f->incrypt.avail == f->incrypt.datasize) SSPI_ExpandBuffer(&f->incrypt, f->incrypt.datasize+1024); return; } +// else +// Con_Printf("InitializeSecurityContextA %x\n", ss); f->handshaking = HS_SERVER; //any extra data should still remain for the next time around. this might be more handshake data or payload data. @@ -743,7 +838,8 @@ static void SSPI_Handshake (sslfile_t *f) case SEC_E_INVALID_HANDLE: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_HANDLE\n"); break; case SEC_E_ILLEGAL_MESSAGE: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_ILLEGAL_MESSAGE\n"); break; case SEC_E_INVALID_TOKEN: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_TOKEN\n"); break; - default: SSPI_Error(f, "InitializeSecurityContext failed: %#lx\n", ss); break; + case SEC_E_INVALID_PARAMETER: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_PARAMETER\n"); break; + default: SSPI_Error(f, "InitializeSecurityContext failed: %lx\n", (long)ss); break; } return; } @@ -758,15 +854,6 @@ static void SSPI_Handshake (sslfile_t *f) } } - if (SSPI_CopyIntoBuffer(&f->outcrypt, OutSecBuff[0].pvBuffer, OutSecBuff[0].cbBuffer, true) < OutSecBuff[0].cbBuffer) - { - SSPI_Error(f, "crypt overflow\n"); - return; - } - - //send early, send often. - SSPI_TryFlushCryptOut(f); - //its all okay and established if we get this far. if (ss == SEC_E_OK) { @@ -787,7 +874,7 @@ static void SSPI_Handshake (sslfile_t *f) SSPI_Error(f, "unable to read server's certificate\n"); return; } - if (VerifyServerCertificate(remotecert, f->wpeername, 0)) + if (VerifyServerCertificate(remotecert, f->wpeername, 0, f->datagram)) { f->handshaking = HS_ERROR; SSPI_Error(f, "Error validating certificante\n"); @@ -799,9 +886,33 @@ static void SSPI_Handshake (sslfile_t *f) } f->handshaking = HS_ESTABLISHED; - - SSPI_Encode(f); } + + //send early, send often. +#ifdef HAVE_DTLS + if (f->transmit) + { + for (i = 0; i < OutBuffDesc.cBuffers; i++) + if (OutSecBuff[i].BufferType == SECBUFFER_TOKEN && OutSecBuff[i].cbBuffer) + f->transmit(f->cbctx, OutSecBuff[i].pvBuffer, OutSecBuff[i].cbBuffer); + } + else +#endif + { + i = 0; + if (SSPI_CopyIntoBuffer(&f->outcrypt, OutSecBuff[i].pvBuffer, OutSecBuff[i].cbBuffer, true) < OutSecBuff[i].cbBuffer) + { + SSPI_Error(f, "crypt overflow\n"); + return; + } + SSPI_TryFlushCryptOut(f); + } + + if (f->handshaking == HS_ESTABLISHED) + SSPI_Encode(f); + else if (ss == SEC_I_MESSAGE_FRAGMENT) //looks like we can connect faster if we loop when we get this result. + if (retries --> 0) + goto retry; } static int QDECL SSPI_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) @@ -886,7 +997,7 @@ static qboolean QDECL SSPI_Close (struct vfsfile_s *file) } #include -vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server, qboolean datagram) +vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server) { sslfile_t *newf; int i = 0; @@ -938,7 +1049,6 @@ vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server } newf->wpeername[i] = 0; - newf->datagram = datagram; newf->handshaking = server?HS_STARTSERVER:HS_STARTCLIENT; newf->stream = source; newf->funcs.Close = SSPI_Close; @@ -960,4 +1070,163 @@ vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server return &newf->funcs; } + + +#if 0 +struct nulldtls_s +{ + void *cbctx; + neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize); +}; +void *DTLS_CreateContext(void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver) +{ + struct nulldtls_s *ctx = Z_Malloc(sizeof(*ctx)); + ctx->cbctx = cbctx; + ctx->push = push; + return ctx; +} +qboolean DTLS_HasServerCertificate(void) +{ + //FIXME: at this point, schannel is still returning errors when I try acting as a server. + //so just block any attempt to use this as a server. + //clients don't need certs! + return false; +} +neterr_t DTLS_Transmit(void *vctx, const qbyte *data, size_t datasize) +{ + struct nulldtls_s *ctx = vctx; + neterr_t r; + *(int*)data ^= 0xdeadbeef; + r = ctx->push(ctx->cbctx, data, datasize); + *(int*)data ^= 0xdeadbeef; + return r; +} +neterr_t DTLS_Received(void *ctx, qbyte *data, size_t datasize) +{ + *(int*)data ^= 0xdeadbeef; + return NETERR_SENT; +} +#elif defined(HAVE_DTLS) +void *DTLS_CreateContext(char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver) +{ + int i = 0; + sslfile_t *ctx; + if (!SSL_Inited()) + return NULL; + + ctx = Z_Malloc(sizeof(*ctx)); + ctx->datagram = true; + ctx->handshaking = isserver?HS_STARTSERVER:HS_STARTCLIENT; + ctx->cbctx = cbctx; + ctx->transmit = push; + + while(*remotehost) + { + int err; + int c = utf8_decode(&err, remotehost, (void*)&remotehost); + if (c > WCHAR_MAX) + err = true; //no 16bit surrogates. they're evil. + else if (i == sizeof(ctx->wpeername)/sizeof(ctx->wpeername[0]) - 1) + err = true; //no space to store it + else + ctx->wpeername[i++] = c; + if (err) + { + Z_Free(ctx); + return NULL; + } + } + ctx->wpeername[i] = 0; + + SSPI_ExpandBuffer(&ctx->outraw, 8192); + SSPI_ExpandBuffer(&ctx->outcrypt, 65536); + SSPI_ExpandBuffer(&ctx->inraw, 8192); + SSPI_ExpandBuffer(&ctx->incrypt, 65536); + + if (isserver) + SSPI_GenServerCredentials(ctx); + else + SSPI_Handshake(ctx); //begin the initial handshake now + return ctx; +} + +void DTLS_DestroyContext(void *vctx) +{ + SSPI_Close(vctx); +} + + +neterr_t DTLS_Transmit(void *ctx, const qbyte *data, size_t datasize) +{ + int ret; + sslfile_t *f = (sslfile_t *)ctx; + +//Con_Printf("DTLS_Transmit: %i\n", datasize); + + //sspi likes writing over the source data. make sure nothing is hurt by copying it out first. + f->outraw.avail = 0; + SSPI_CopyIntoBuffer(&f->outraw, data, datasize, true); + + if (f->handshaking) + { + SSPI_Handshake(f); + + if (f->handshaking == HS_ERROR) + ret = NETERR_DISCONNECTED; + ret = NETERR_CLOGGED; //not ready yet + } + else + { + SSPI_Encode(f); + ret = NETERR_SENT; + } + + return ret; +} + +neterr_t DTLS_Received(void *ctx, qbyte *data, size_t datasize) +{ + int ret; + sslfile_t *f = (sslfile_t *)ctx; + +//Con_Printf("DTLS_Received: %i\n", datasize); + + f->incrypt.data = data; + f->incrypt.avail = f->incrypt.datasize = datasize; + + if (f->handshaking) + { + SSPI_Handshake(f); + ret = NETERR_CLOGGED; //not ready yet + + if (f->handshaking == HS_ERROR) + ret = NETERR_DISCONNECTED; + } + else + { + SSPI_Decode(f); + ret = NETERR_SENT; + + memcpy(net_message_buffer, f->inraw.data, f->inraw.avail); + net_message.cursize = f->inraw.avail; + f->inraw.avail = 0; + + net_message_buffer[net_message.cursize] = 0; +// Con_Printf("returning %i bytes: %s\n", net_message.cursize, net_message_buffer); + } + f->incrypt.data = NULL; + return ret; +} +neterr_t DTLS_Timeouts(void *ctx) +{ + sslfile_t *f = (sslfile_t *)ctx; + if (f->handshaking) + { +// SSPI_Handshake(f); + return NETERR_CLOGGED; + } + return NETERR_SENT; +} +#endif + #endif diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index f779112a..02656b1f 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -97,13 +97,16 @@ cvar_t net_enabled = CVARD("net_enabled", "1", "If 0, disables all network #if defined(TCPCONNECT) && !defined(CLIENTONLY) cvar_t net_enable_qizmo = CVARD("net_enable_qizmo", "1", "Enables compatibility with qizmo's tcp connections serverside. Frankly, using sv_port_tcp without this is a bit pointless."); cvar_t net_enable_qtv = CVARD("net_enable_qtv", "1", "Listens for qtv proxies, or clients using the qtvplay command."); -cvar_t net_enable_tls = CVARD("net_enable_tls", "1", "If enabled, binary data sent to a tcp connection will be interpretted as a tls handshake (enabling https or wss over the same tcp port."); +cvar_t net_enable_tls = CVARD("net_enable_tls", "1", "If enabled, binary data sent to a non-tls tcp port will be interpretted as a tls handshake (enabling https or wss over the same tcp port."); cvar_t net_enable_http = CVARD("net_enable_http", "1", "If enabled, tcp ports will accept http clients, potentially serving large files which could distrupt gameplay."); cvar_t net_enable_websockets = CVARD("net_enable_websockets", "1", "If enabled, tcp ports will accept websocket game clients."); cvar_t net_enable_webrtcbroker = CVARD("net_enable_webrtcbroker", "1", "If 1, tcp ports will accept websocket connections from clients trying to broker direct webrtc connections. This should be low traffic, but might involve a lot of mostly-idle connections."); #endif -extern cvar_t sv_public, sv_listen_qw, sv_listen_nq, sv_listen_dp, sv_listen_q3; +extern cvar_t sv_public, sv_listen_qw, sv_listen_nq, sv_listen_dp; +#ifdef QWOVERQ3 +extern cvar_t sv_listen_q3; +#endif #define MAX_LOOPBACK 64 typedef struct @@ -121,6 +124,14 @@ typedef struct } loopback_t; loopback_t loopbacks[2]; + + +#ifdef HAVE_DTLS +static neterr_t FTENET_DTLS_SendPacket(ftenet_connections_t *col, int length, const void *data, netadr_t *to); +#endif + + + //============================================================================= int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) @@ -234,7 +245,6 @@ qboolean NET_AddrIsReliable(netadr_t *adr) //hints that the protocol is reliable case NP_TLS: case NP_WS: case NP_WSS: - case NP_IRC: return true; } } @@ -495,7 +505,6 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) case NP_TLS: prot = "tls://"; break; case NP_WS: prot = "ws://"; break; case NP_WSS: prot = "wss://"; break; - case NP_IRC: prot = "irc://"; break; case NP_NATPMP: prot = "natpmp://"; break; } @@ -679,7 +688,6 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) case NP_TLS: prot = "tls://"; break; case NP_WS: prot = "ws://"; break; case NP_WSS: prot = "wss://"; break; - case NP_IRC: prot = "irc://"; break; case NP_NATPMP: prot = "natpmp://"; break; } @@ -1141,11 +1149,11 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num a->prot = NP_STREAM; return true; } - if (!strncmp (s, "ws://", 7)) + if (!strncmp (s, "ws://", 5)) { //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+6, defaultport, &sadr[0], NULL, NULL)) + if (!NET_StringToSockaddr (s+5, defaultport, &sadr[0], NULL, NULL)) { a->type = NA_INVALID; return false; @@ -1155,7 +1163,7 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num a->prot = NP_WS; return true; } - if (!strncmp (s, "wss://", 7)) + if (!strncmp (s, "wss://", 6)) { //make sure that the rest of the address is a valid ip address (4 or 6) @@ -1171,9 +1179,10 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num } if (!strncmp (s, "dtls://", 7)) { +#ifdef HAVE_DTLS //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+6, defaultport, &sadr[0], NULL, NULL)) + if (!NET_StringToSockaddr (s+7, defaultport, &sadr[0], NULL, NULL)) { a->type = NA_INVALID; return false; @@ -1182,6 +1191,9 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num SockadrToNetadr (&sadr[0], a); a->prot = NP_DTLS; return true; +#else + return false; +#endif } if (!strncmp (s, "tls://", 6)) { @@ -2217,6 +2229,144 @@ ftenet_generic_connection_t *FTENET_NATPMP_EstablishConnection(qboolean isserver } #endif +#ifdef HAVE_DTLS +struct dtlspeer_s +{ + ftenet_connections_t *col; + void *dtlsstate; + netadr_t addr; + float timeout; + + struct dtlspeer_s *next; + struct dtlspeer_s **link; +}; + +void NET_DTLS_Timeouts(ftenet_connections_t *col) +{ + struct dtlspeer_s *peer; + if (!col) + return; + for (peer = col->dtls; peer; peer = peer->next) + { + DTLS_Timeouts(peer->dtlsstate); + } +} + +static neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length, const void *data, netadr_t *to); +static neterr_t FTENET_DTLS_DoSendPacket(void *cbctx, const qbyte *data, size_t length) +{ //callback that does the actual sending + struct dtlspeer_s *peer = cbctx; + return NET_SendPacketCol(peer->col, length, data, &peer->addr); +} +qboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to) +{ + struct dtlspeer_s *peer; + if (to->prot != NP_DGRAM) + return false; + for (peer = col->dtls; peer; peer = peer->next) + { + if (NET_CompareAdr(&peer->addr, to)) + break; + } + if (!peer) + { + char hostname[256]; + peer = Z_Malloc(sizeof(*peer)); + peer->addr = *to; + peer->col = col; + + peer->dtlsstate = DTLS_CreateContext(NET_BaseAdrToString(hostname, sizeof(hostname), to), peer, FTENET_DTLS_DoSendPacket, col->islisten); + Sys_Printf("Created %p\n", peer->dtlsstate); + + if (peer->next) + peer->next->link = &peer->next; + peer->link = &col->dtls; + peer->next = col->dtls; + col->dtls = peer; + } + return true; +} +static void NET_DTLS_DisconnectPeer(ftenet_connections_t *col, struct dtlspeer_s *peer) +{ +// Sys_Printf("Destroy %p\n", peer->dtlsstate); + + if (peer->next) + peer->next->link = peer->link; + *peer->link = peer->next; + + DTLS_DestroyContext(peer->dtlsstate); + Z_Free(peer); +} +qboolean NET_DTLS_Disconnect(ftenet_connections_t *col, netadr_t *to) +{ + struct dtlspeer_s *peer; + netadr_t n = *to; + if (!col || (to->prot != NP_DGRAM && to->prot != NP_DTLS)) + return false; + n.prot = NP_DGRAM; + for (peer = col->dtls; peer; peer = peer->next) + { + if (NET_CompareAdr(&peer->addr, &n)) + { + NET_DTLS_DisconnectPeer(col, peer); + break; + } + } + return peer?true:false; +} +static neterr_t FTENET_DTLS_SendPacket(ftenet_connections_t *col, int length, const void *data, netadr_t *to) +{ + struct dtlspeer_s *peer; + to->prot = NP_DGRAM; + for (peer = col->dtls; peer; peer = peer->next) + { + if (NET_CompareAdr(&peer->addr, to)) + break; + } + to->prot = NP_DTLS; + if (peer) + return DTLS_Transmit(peer->dtlsstate, data, length); + else + return NETERR_NOROUTE; +} + +qboolean NET_DTLS_Decode(ftenet_connections_t *col) +{ + struct dtlspeer_s *peer; + for (peer = col->dtls; peer; peer = peer->next) + { + if (NET_CompareAdr(&peer->addr, &net_from)) + { + switch(DTLS_Received(peer->dtlsstate, net_message.data, net_message.cursize)) + { + case NETERR_DISCONNECTED: + Sys_Printf("disconnected %p\n", peer->dtlsstate); + NET_DTLS_DisconnectPeer(col, peer); + break; + case NETERR_NOROUTE: + return false; //not a valid dtls packet. + default: + case NETERR_CLOGGED: + //ate it + net_message.cursize = 0; + break; + case NETERR_SENT: + //we decoded it properly + break; + } + net_from.prot = NP_DTLS; + return true; + } + } + return false; +} +#endif + + + + + + static qboolean FTENET_AddToCollection_Ptr(ftenet_connections_t *col, const char *name, ftenet_generic_connection_t *(*establish)(qboolean isserver, const char *address, netadr_t adr), qboolean islisten, const char *address, netadr_t *adr) { int count = 0; @@ -2300,10 +2450,10 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con if (adr[i].prot == NP_DGRAM && adr[i].type == NA_LOOPBACK) establish[i] = FTENET_Loop_EstablishConnection; else #endif #ifdef HAVE_IPV4 - if (adr[i].prot == NP_DGRAM && adr[i].type == NA_IP) establish[i] = FTENET_Datagram_EstablishConnection; else + if ((adr[i].prot == NP_DGRAM) && adr[i].type == NA_IP) establish[i] = FTENET_Datagram_EstablishConnection; else #endif #ifdef HAVE_IPV6 - if (adr[i].prot == NP_DGRAM && adr[i].type == NA_IPV6) establish[i] = FTENET_Datagram_EstablishConnection; else + if ((adr[i].prot == NP_DGRAM) && adr[i].type == NA_IPV6) establish[i] = FTENET_Datagram_EstablishConnection; else #endif #ifdef USEIPX if (adr[i].prot == NP_DGRAM && adr[i].type == NA_IPX) establish[i] = FTENET_Datagram_EstablishConnection; else @@ -4021,7 +4171,8 @@ static int QDECL TLSPromoteRead (struct vfsfile_s *file, void *buffer, int bytes if (bytestoread > net_message.cursize) bytestoread = net_message.cursize; memcpy(buffer, net_message_buffer, bytestoread); - net_message.cursize = 0; + net_message.cursize -= bytestoread; + memmove(net_message_buffer, net_message_buffer+bytestoread, net_message.cursize); return bytestoread; } #endif @@ -4120,7 +4271,8 @@ qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) { Con_Printf ("tcp peer %s closed connection\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); closesvstream: - VFS_CLOSE(st->clientstream); + if (st->clientstream) + VFS_CLOSE(st->clientstream); st->clientstream = NULL; continue; } @@ -4143,13 +4295,16 @@ closesvstream: memcpy(net_message_buffer, st->inbuffer, st->inlen); net_message.cursize = st->inlen; //wrap the stream now - st->clientstream = FS_OpenSSL(NULL, st->clientstream, true, false); + st->clientstream = FS_OpenSSL(NULL, st->clientstream, true); st->remoteaddr.prot = NP_TLS; - //try and reclaim it all - st->inlen = VFS_READ(st->clientstream, st->inbuffer, sizeof(st->inbuffer)-1); - //make sure we actually read from the proper stream again - stream->ReadBytes = realread; - if (net_message.cursize) + if (st->clientstream) + { + //try and reclaim it all + st->inlen = VFS_READ(st->clientstream, st->inbuffer, sizeof(st->inbuffer)-1); + //make sure we actually read from the proper stream again + stream->ReadBytes = realread; + } + if (!st->clientstream || net_message.cursize) goto closesvstream; //something cocked up. we didn't give the tls stream all the data. net_message.cursize = 0; continue; @@ -4564,7 +4719,7 @@ closesvstream: #ifdef HAVE_SSL if (con->tls && st->clientstream) //if we're meant to be using tls, wrap the stream in a tls connection { - st->clientstream = FS_OpenSSL(NULL, st->clientstream, true, false); + st->clientstream = FS_OpenSSL(NULL, st->clientstream, true); /*sockadr doesn't contain transport info, so fix that up here*/ st->remoteaddr.prot = NP_TLS; } @@ -4890,7 +5045,7 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(qboolean isse #ifdef HAVE_SSL if (newcon->tls) //if we're meant to be using tls, wrap the stream in a tls connection - newcon->tcpstreams->clientstream = FS_OpenSSL(address, newcon->tcpstreams->clientstream, false, false); + newcon->tcpstreams->clientstream = FS_OpenSSL(address, newcon->tcpstreams->clientstream, false); #endif //send the qizmo greeting. @@ -6202,21 +6357,20 @@ int NET_GetPacket (netsrc_t netsrc, int firstsock) while (firstsock < MAX_CONNECTIONS) { - if (!collection->conn[firstsock]) - break; - if (collection->conn[firstsock]->GetPacket(collection->conn[firstsock])) - { - if (net_fakeloss.value) + if (collection->conn[firstsock]) + if (collection->conn[firstsock]->GetPacket(collection->conn[firstsock])) { - if (frandom () < net_fakeloss.value) - continue; - } + if (net_fakeloss.value) + { + if (frandom () < net_fakeloss.value) + continue; + } - collection->bytesin += net_message.cursize; - collection->packetsin += 1; - net_from.connum = firstsock+1; - return firstsock; - } + collection->bytesin += net_message.cursize; + collection->packetsin += 1; + net_from.connum = firstsock+1; + return firstsock; + } firstsock += 1; } @@ -6254,32 +6408,11 @@ int NET_LocalAddressForRemote(ftenet_connections_t *collection, netadr_t *remote return collection->conn[remote->connum-1]->GetLocalAddresses(collection->conn[remote->connum-1], &adrflags, local, 1); } -neterr_t NET_SendPacket (netsrc_t netsrc, int length, const void *data, netadr_t *to) +static neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length, const void *data, netadr_t *to) { neterr_t err; -// char buffer[64]; - ftenet_connections_t *collection; int i; - if (netsrc == NS_SERVER) - { -#ifdef CLIENTONLY - Sys_Error("NET_GetPacket: Bad netsrc"); - return NETERR_NOROUTE; -#else - collection = svs.sockets; -#endif - } - else - { -#ifdef SERVERONLY - Sys_Error("NET_GetPacket: Bad netsrc"); - return NETERR_NOROUTE; -#else - collection = cls.sockets; -#endif - } - if (!collection) return NETERR_NOROUTE; @@ -6338,6 +6471,35 @@ neterr_t NET_SendPacket (netsrc_t netsrc, int length, const void *data, netadr_t return NETERR_NOROUTE; } +neterr_t NET_SendPacket (netsrc_t netsrc, int length, const void *data, netadr_t *to) +{ + ftenet_connections_t *collection; + + if (netsrc == NS_SERVER) + { +#ifdef CLIENTONLY + Sys_Error("NET_GetPacket: Bad netsrc"); + return NETERR_NOROUTE; +#else + collection = svs.sockets; +#endif + } + else + { +#ifdef SERVERONLY + Sys_Error("NET_GetPacket: Bad netsrc"); + return NETERR_NOROUTE; +#else + collection = cls.sockets; +#endif + } +#ifdef HAVE_DTLS + if (to->prot == NP_DTLS) + return FTENET_DTLS_SendPacket(collection, length, data, to); +#endif + return NET_SendPacketCol (collection, length, data, to); +} + qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char *host, qboolean islisten) { netadr_t adr; @@ -6347,11 +6509,15 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char switch(adr.prot) { case NP_DTLS: +#ifdef HAVE_DTLS +// if (FTENET_DTLS_Create(collection, &adr) == NETERR_NOROUTE) +/// return false; + break; +#endif case NP_WS: case NP_WSS: case NP_TLS: case NP_STREAM: - case NP_IRC: if (!FTENET_AddToCollection(collection, routename, host, adr.type, adr.prot, islisten)) return false; Con_Printf("Establishing connection to %s\n", host); @@ -6496,6 +6662,15 @@ void NET_PrintConnectionsStatus(ftenet_connections_t *collection) if (collection->conn[i]->PrintStatus) collection->conn[i]->PrintStatus(collection->conn[i]); } + +#ifdef HAVE_DTLS + { + struct dtlspeer_s *dtls; + char adr[64]; + for (dtls = collection->dtls; dtls; dtls = dtls->next) + Con_Printf("dtls: %s\n", NET_AdrToString(adr, sizeof(adr), &dtls->addr)); + } +#endif } //============================================================================= @@ -6977,7 +7152,7 @@ void NET_ClientPort_f(void) qboolean NET_WasSpecialPacket(netsrc_t netsrc) { -#ifdef HAVE_NATPMP +#if defined(HAVE_NATPMP) ftenet_connections_t *collection = NULL; if (netsrc == NS_SERVER) { @@ -6991,16 +7166,18 @@ qboolean NET_WasSpecialPacket(netsrc_t netsrc) collection = cls.sockets; #endif } + +#ifdef HAVE_NATPMP + if (NET_Was_NATPMP(collection)) + return true; +#endif #endif #ifdef SUPPORT_ICE if (ICE_WasStun(netsrc)) return true; #endif -#ifdef HAVE_NATPMP - if (NET_Was_NATPMP(collection)) - return true; -#endif + return false; } @@ -7065,6 +7242,11 @@ void NET_Init (void) Net_Master_Init(); } #ifndef SERVERONLY +void NET_CloseClient(void) +{ //called by disconnect console command + FTENET_CloseCollection(cls.sockets); + cls.sockets = NULL; +} void NET_InitClient(qboolean loopbackonly) { const char *port; @@ -7201,6 +7383,16 @@ void SVNET_RegisterCvars(void) Cvar_Register (&sv_port_natpmp, "networking"); sv_port_natpmp.restriction = RESTRICT_MAX; #endif + + +#if defined(TCPCONNECT) && !defined(CLIENTONLY) + Cvar_Register (&net_enable_qizmo, "networking"); + Cvar_Register (&net_enable_qtv, "networking"); + Cvar_Register (&net_enable_tls, "networking"); + Cvar_Register (&net_enable_http, "networking"); + Cvar_Register (&net_enable_websockets, "networking"); + Cvar_Register (&net_enable_webrtcbroker, "networking"); +#endif } void NET_CloseServer(void) @@ -7211,7 +7403,11 @@ void NET_CloseServer(void) void NET_InitServer(void) { - if (sv_listen_nq.value || sv_listen_dp.value || sv_listen_qw.value || sv_listen_q3.ival) + if (sv_listen_nq.value || sv_listen_dp.value || sv_listen_qw.value +#ifdef QWOVERQ3 + || sv_listen_q3.ival +#endif + ) { if (!svs.sockets) { diff --git a/engine/common/netinc.h b/engine/common/netinc.h index 150692ff..b0bd10f7 100644 --- a/engine/common/netinc.h +++ b/engine/common/netinc.h @@ -292,6 +292,17 @@ typedef struct ftenet_generic_connection_s { #endif } ftenet_generic_connection_t; +#ifdef HAVE_DTLS +void *DTLS_CreateContext(char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver); //if remotehost is null then their certificate will not be validated. +void DTLS_DestroyContext(void *ctx); +neterr_t DTLS_Transmit(void *ctx, const qbyte *data, size_t datasize); +neterr_t DTLS_Received(void *ctx, qbyte *data, size_t datasize); +neterr_t DTLS_Timeouts(void *ctx); +qboolean DTLS_HasServerCertificate(void); +#endif + + + #define MAX_CONNECTIONS 8 typedef struct ftenet_connections_s { @@ -306,6 +317,10 @@ typedef struct ftenet_connections_s float bytesinrate; float bytesoutrate; ftenet_generic_connection_t *conn[MAX_CONNECTIONS]; + +#ifdef HAVE_DTLS + struct dtlspeer_s *dtls; //linked list. linked lists are shit, but at least it keeps pointers valid when things are resized. +#endif } ftenet_connections_t; void ICE_Tick(void); @@ -318,7 +333,7 @@ void FTENET_CloseCollection(ftenet_connections_t *col); qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot, qboolean islisten); int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, unsigned int *adrflags, netadr_t *addresses, int maxaddresses); -vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, qboolean datagram); +vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server); #ifdef HAVE_PACKET vfsfile_t *FS_OpenTCPSocket(SOCKET socket, qboolean conpending, const char *peername); //conpending allows us to reject any writes until the connection has succeeded #endif diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 1852b522..b0ae2fc9 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -1085,7 +1085,7 @@ qintptr_t VARGS Plug_Net_SetTLSClient(void *offset, quintptr_t mask, const qintp return -2; } - stream->vfs = FS_OpenSSL(VM_POINTER(arg[1]), stream->vfs, false, false); + stream->vfs = FS_OpenSSL(VM_POINTER(arg[1]), stream->vfs, false); if (!stream->vfs) { Plug_Net_Close_Internal(handle); diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 4761cd6b..90620163 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -98,11 +98,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif +#define PROTOCOL_VERSION_VARLENGTH (('v'<<0) + ('l'<<8) + ('e'<<16) + ('n' << 24)) //variable length handshake + #define PROTOCOL_VERSION_FTE (('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24)) //fte extensions. #define PROTOCOL_VERSION_FTE2 (('F'<<0) + ('T'<<8) + ('E'<<16) + ('2' << 24)) //fte extensions. #define PROTOCOL_VERSION_HUFFMAN (('H'<<0) + ('U'<<8) + ('F'<<16) + ('F' << 24)) //packet compression -#define PROTOCOL_VERSION_VARLENGTH (('v'<<0) + ('l'<<8) + ('e'<<16) + ('n' << 24)) //variable length handshake #define PROTOCOL_VERSION_FRAGMENT (('F'<<0) + ('R'<<8) + ('A'<<16) + ('G' << 24)) //supports fragmentation/packets larger than 1450 +#ifdef HAVE_DTLS +#define PROTOCOL_VERSION_DTLSUPGRADE (('D'<<0) + ('T'<<8) + ('L'<<16) + ('S' << 24)) //server supports dtls. clients should dtlsconnect THEN continue connecting (also allows dtls rcon!). +#endif #define PROTOCOL_INFO_GUID (('G'<<0) + ('U'<<8) + ('I'<<16) + ('D' << 24)) //globally 'unique' client id info. @@ -1733,3 +1737,5 @@ typedef struct q1usercmd_s #define E5_EXTEND4 (1<<31) #define E5_ALLUNUSED (E5_UNUSED27|E5_UNUSED28|E5_UNUSED29|E5_UNUSED30) +#define E5_SERVERPRIVATE (E5_EXTEND1|E5_EXTEND2|E5_EXTEND3|E5_EXTEND4) +#define E5_SERVERREMOVE E5_EXTEND1 diff --git a/engine/dotnet2005/ftequake.sln b/engine/dotnet2005/ftequake.sln index 69339861..e81f513e 100644 --- a/engine/dotnet2005/ftequake.sln +++ b/engine/dotnet2005/ftequake.sln @@ -556,7 +556,8 @@ Global {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Debug|Win32.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Debug|x64.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Debug|x64.Build.0 = Debug Dedicated Server|x64 - {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLDebug|Win32.ActiveCfg = Debug Dedicated Server|x64 + {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLDebug|Win32.ActiveCfg = Debug Dedicated Server|Win32 + {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLDebug|Win32.Build.0 = Debug Dedicated Server|Win32 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLDebug|x64.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLRelease|Win32.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLRelease|x64.ActiveCfg = Debug Dedicated Server|x64 @@ -580,7 +581,7 @@ Global {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Release|Win32.ActiveCfg = Release Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Release|x64.ActiveCfg = Release Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Release|x64.Build.0 = Release Dedicated Server|x64 - {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkDebug|Win32.ActiveCfg = Debug Dedicated Server|x64 + {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkDebug|Win32.ActiveCfg = Debug Dedicated Server|Win32 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkDebug|x64.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkRelease|Win32.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkRelease|x64.ActiveCfg = Debug Dedicated Server|x64 diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index 37c6e6bc..7ec4a5da 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -7250,6 +7250,53 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "}\n" "#endif\n" +}, +#endif +#ifdef GLQUAKE +{QR_OPENGL, 110, "itemtimer", +"!!permu FOG\n" + +"#include \"sys/defs.h\"\n" +"#include \"sys/fog.h\"\n" + +"varying vec2 tc;\n" +"varying vec4 vc;\n" + +"#ifdef VERTEX_SHADER\n" +"void main ()\n" +"{\n" +"tc = v_texcoord;\n" +"vc = v_colour;\n" +"gl_Position = ftetransform();\n" +"}\n" +"#endif\n" + + +"#ifdef FRAGMENT_SHADER\n" +"void main ()\n" +"{\n" +"gl_FragColor = vec4(0.5,0.5,0.5,1);//texture2D(s_diffuse, tc.xy);\n" + +"vec2 st = (tc-floor(tc)) - 0.5;\n" +"st *= 2.0;\n" +"float dist = sqrt(dot(st,st));\n" + +"float ring = 1.0 + smoothstep(0.9, 1.0, dist)\n" +"- smoothstep(0.8, 0.9, dist);\n" + +//fade out the rim +"if ((atan(st.t, st.s)+3.14)/6.28 > vc.a)\n" +"gl_FragColor.a *= 0.25;\n" +"gl_FragColor.rgb *= mix(vc.rgb, vec3(0.0), ring);\n" +//gl_FragColor.a; + +//and finally hide it all if we're fogged. +"#ifdef FOG\n" +"gl_FragColor = fog4additive(gl_FragColor);\n" +"#endif\n" +"}\n" +"#endif\n" + }, #endif #ifdef GLQUAKE diff --git a/engine/http/ftpserver.c b/engine/http/ftpserver.c index 39bbf8e9..0987bb88 100644 --- a/engine/http/ftpserver.c +++ b/engine/http/ftpserver.c @@ -1485,8 +1485,10 @@ unsigned long _true = true; cl->controlaf = 1; else if (((struct sockaddr *)&from)->sa_family == AF_INET6) cl->controlaf = 2; +#ifdef USEIPX else if (((struct sockaddr *)&from)->sa_family == AF_IPX) cl->controlaf = 11; +#endif else cl->controlaf = 0; diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 6e03f4f0..c18e78fb 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -1061,7 +1061,7 @@ void HTTPDL_Establish(struct dl_download *dl) if (https) { //https has an extra ssl/tls layer between tcp and http. - con->stream = FS_OpenSSL(con->server, con->stream, false, false); + con->stream = FS_OpenSSL(con->server, con->stream, false); con->secure = true; } #endif diff --git a/engine/http/iwebiface.c b/engine/http/iwebiface.c index 396507f6..d4875de4 100644 --- a/engine/http/iwebiface.c +++ b/engine/http/iwebiface.c @@ -601,6 +601,9 @@ void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority { return NULL; } +void Sys_WaitOnThread(void *thread) +{ +} qboolean FS_Remove(const char *fname, enum fs_relative relativeto) { return false; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 09a47c43..dbf63ed0 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -2948,6 +2948,8 @@ static void QCC_PR_ExpandStrCat(char **buffer, size_t *bufferlen, size_t *buffer static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t retbufsize) { + if (constname[0] != '_' || constname[1] != '_') + return NULL; if (!strcmp(constname, "__TIME__")) { time_t long_time; @@ -2969,7 +2971,7 @@ static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t } if (!strcmp(constname, "__QCCVER__")) { - return "FTEQCC "__DATE__","__TIME__""; + return "\"FTEQCC "__DATE__","__TIME__"\""; } if (!strcmp(constname, "__FILE__")) { @@ -2986,7 +2988,7 @@ static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t QC_snprintfz(retbuf, retbufsize, "\"%i\"", pr_source_line); return retbuf; } - if (!strcmp(constname, "__FUNC__")) + if (!strcmp(constname, "__FUNC__") || !strcmp(constname, "__func__")) { QC_snprintfz(retbuf, retbufsize, "\"%s\"",pr_scope?pr_scope->name:""); return retbuf; diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index 467059cc..ae01f41b 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -56,6 +56,11 @@ void AddSourceFile(const char *parentsrc, const char *filename); #define SCI_GETANCHOR 2009 #define SCI_SETSAVEPOINT 2014 #define SCI_GETCURLINE 2027 +#define SCI_CONVERTEOLS 2029 +#define SC_EOL_CRLF 0 +#define SC_EOL_CR 1 +#define SC_EOL_LF 2 +#define SCI_SETEOLMODE 2031 #define SCI_SETCODEPAGE 2037 #define SCI_MARKERDEFINE 2040 #define SCI_MARKERSETFORE 2041 @@ -114,6 +119,7 @@ void AddSourceFile(const char *parentsrc, const char *filename); #define SCI_BRACEHIGHLIGHT 2351 #define SCI_BRACEBADLIGHT 2352 #define SCI_BRACEMATCH 2353 +#define SCI_SETVIEWEOL 2356 #define SCI_ANNOTATIONSETTEXT 2540 #define SCI_ANNOTATIONGETTEXT 2541 #define SCI_ANNOTATIONSETSTYLE 2542 @@ -1295,6 +1301,8 @@ enum { IDM_DEBUG_TOGGLEBREAK, IDM_ENCODING_PRIVATEUSE, IDM_ENCODING_DEPRIVATEUSE, + IDM_ENCODING_UNIX, + IDM_ENCODING_WINDOWS, IDM_CREATEINSTALLER_WINDOWS, IDM_CREATEINSTALLER_ANDROID, @@ -1816,6 +1824,15 @@ void EditorMenu(editor_t *editor, WPARAM wParam) GUI_Recode(editor, UTF_ANSI); break; + case IDM_ENCODING_UNIX: + SendMessage(editor->editpane, SCI_CONVERTEOLS, SC_EOL_LF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + case IDM_ENCODING_WINDOWS: + SendMessage(editor->editpane, SCI_CONVERTEOLS, SC_EOL_CRLF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + default: GenericMenu(wParam); break; @@ -2416,7 +2433,6 @@ static void EditorReload(editor_t *editor) char *file; unsigned int flen; pbool dofree; - rawfile = QCC_ReadFile(editor->filename, NULL, 0, &flensz); flen = flensz; @@ -2427,6 +2443,50 @@ static void EditorReload(editor_t *editor) if (editor->scintilla) { + int endings = 0; + char *e, *stop; + for (e = file, stop=file+flen; e < stop; ) + { + if (*e == '\r') + { + e++; + if (*e == '\n') + { + e++; + endings |= 4; + } + else + endings |= 2; + } + else if (*e == '\n') + { + e++; + endings |= 1; + } + else + e++; + } + switch(endings) + { + case 0: //new file with no endings, default to windows on windows. + case 4: //windows + SendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_CRLF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + case 1: //unix + SendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_LF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + case 2: //mac. traditionally qccs have never supported this. one of the mission packs has a \r in the middle of some single-line comment. + SendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_CR, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + default: //panic! everyone panic! + SendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_LF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, true, 0); + break; + } + // SendMessage(editor->editpane, SCI_SETTEXT, 0, (LPARAM)file); // SendMessage(editor->editpane, SCI_SETUNDOCOLLECTION, 0, 0); SendMessage(editor->editpane, SCI_SETTEXT, 0, (LPARAM)file); @@ -5301,6 +5361,8 @@ static LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message, AppendMenu(m, MF_SEPARATOR, 0, NULL); AppendMenu(m, 0, IDM_ENCODING_PRIVATEUSE, "Convert to UTF-8"); AppendMenu(m, 0, IDM_ENCODING_DEPRIVATEUSE, "Convert to Quake encoding"); + AppendMenu(m, 0, IDM_ENCODING_UNIX, "Convert to Unix Endings"); + AppendMenu(m, 0, IDM_ENCODING_WINDOWS, "Convert to Dos Endings"); AppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = windowmenu = CreateMenu()), "&Window"); AppendMenu(m, 0, IDM_CASCADE, "Cascade"); diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 40859e0c..954f8021 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -9345,7 +9345,7 @@ static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars pmove.cmd.upmove = bound(-32767, (pr_global_struct->input_movevalues)[2], 32767); pmove.cmd.buttons = pr_global_struct->input_buttons; - pmove.safeorigin_known = true; + pmove.safeorigin_known = progstype != PROG_QW; VectorCopy(ent->v->oldorigin, pmove.safeorigin); VectorCopy(ent->v->origin, pmove.origin); VectorCopy(ent->v->velocity, pmove.velocity); diff --git a/engine/server/server.h b/engine/server/server.h index 606165ff..e2171047 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -299,6 +299,20 @@ typedef struct } server_t; void SV_WipeServerState(void); +/* +#define CS_EMPTY 0 +#define CS_ZOMBIE (1u<<0) //just stops the slot from being reused for a bit. +#define CS_CLUSTER (1u<<1) //is managed by the cluster host (and will appear on scoreboards). +#define CS_SPAWNED (1u<<2) //has an active entity. +#define CS_ACTIVE (1u<<3) //has a connection + +#define cs_free (CS_EMPTY) +#define cs_zombie (CS_ZOMBIE) +#define cs_loadzombie (CS_SPAWNED) +#define cs_connected (CS_ACTIVE) +#define cs_spawned (CS_ACTIVE|CS_SPAWNED) +*/ + typedef enum { cs_free, // can be reused for a new connection @@ -413,6 +427,7 @@ enum #define STUFFCMD_IGNOREINDEMO ( 1<<0) // do not put in mvd demo #define STUFFCMD_DEMOONLY ( 1<<1) // put in mvd demo only +#define STUFFCMD_BROADCAST ( 1<<2) // everyone sees it. typedef struct client_s { @@ -545,14 +560,7 @@ typedef struct client_s //true/false/persist unsigned int penalties; -/* qbyte ismuted; - qbyte iscuffed; - qbyte iscrippled; - qbyte isdeaf; - qbyte islagged; - qbyte isvip; -*/ - qbyte istobeloaded; //loadgame creates place holders for clients to connect to. Effectivly loading a game reconnects all clients, but has precreated ents. + qbyte istobeloaded; //loadgame creates place holders for clients to connect to. Effectivly loading a game reconnects all clients, but has precreated ents. double floodprotmessage; double lastspoke; @@ -1092,6 +1100,10 @@ void SV_FullClientUpdate (client_t *client, client_t *to); void SV_GeneratePublicUserInfo(int pext, client_t *cl, char *info, int infolength); char *SV_PlayerPublicAddress(client_t *cl); +qboolean SVC_GetChallenge (qboolean respond_dp); +int SV_NewChallenge (void); +client_t *SVC_DirectConnect(void); + int SV_ModelIndex (const char *name); void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index df652e5e..c1bbce82 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -1776,7 +1776,13 @@ static void SV_Status_f (void) float pi, po, bi, bo; int columns = 80; - extern cvar_t sv_listen_qw, sv_listen_nq, sv_listen_dp, sv_listen_q3; + extern cvar_t sv_listen_qw, sv_listen_nq, sv_listen_dp; +#ifdef QWOVERQ3 + extern cvar_t sv_listen_q3; +#endif +#ifdef HAVE_DTLS + extern cvar_t sv_listen_dtls; +#endif #ifndef SERVERONLY if (!sv.state && cls.state >= ca_connected && !cls.demoplayback && cls.protocol == CP_NETQUAKE) @@ -1815,7 +1821,47 @@ static void SV_Status_f (void) Con_Printf("packets,bytes/sec: in: %g %g out: %g %g\n", pi, bi, po, bo); //not relevent as a limit. Con_Printf("server uptime : %s\n", ShowTime(realtime)); Con_Printf("public : %s\n", sv_public.value?"yes":"no"); - Con_Printf("client types :%s%s%s%s\n", sv_listen_qw.ival?" QW":"", sv_listen_nq.ival?" NQ":"", sv_listen_dp.ival?" DP":"", sv_listen_q3.ival?" Q3":""); + switch(svs.gametype) + { +#ifdef Q3SERVER + case GT_QUAKE3: + Con_Printf("client types :%s\n", sv_listen_qw.ival?" Q3":""); + break; +#endif +#ifdef Q2SERVER + case GT_QUAKE2: + Con_Printf("client types :%s\n", sv_listen_qw.ival?" Q2":""); + break; +#endif + default: + Con_Printf("client types :%s", sv_listen_qw.ival?" QW":""); +#ifdef NQPROT + Con_Printf("%s%s", (sv_listen_nq.ival==2)?" -NQ":(sv_listen_nq.ival?" NQ":""), sv_listen_dp.ival?" DP":""); +#endif +#ifdef QWOVERQ3 + if (sv_listen_q3.ival) Con_Printf(" Q3"); +#endif +#ifdef HAVE_DTLS + if (sv_listen_dtls.ival >= 2) + Con_Printf(" +DTLS"); + else if (sv_listen_dtls.ival) + Con_Printf(" DTLS"); +#endif + /*if (net_enable_tls.ival) + Con_Printf(" TLS"); + if (net_enable_http.ival) + Con_Printf(" HTTP"); + if (net_enable_webrtcbroker.ival) + Con_Printf(" WebRTC"); + if (net_enable_websockets.ival) + Con_Printf(" WS"); + if (net_enable_qizmo.ival) + Con_Printf(" QZ"); + if (net_enable_qtv.ival) + Con_Printf(" QTV");*/ + Con_Printf("\n"); + break; + } #ifdef SUBSERVERS if (sv.state == ss_clustermode) { diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 2e319fb8..8cc1029c 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -1786,17 +1786,12 @@ unsigned int SVDP_CalcDelta(entity_state_t *from, qbyte *frombonedatabase, entit return bits; } -void SVDP_EmitEntityDelta(entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean isnew, qbyte *bonedatabase) +void SVDP_EmitEntityDelta(unsigned int bits, entity_state_t *to, sizebuf_t *msg, qbyte *bonedatabase) { - int bits; + bits &= ~E5_SERVERPRIVATE; - bits = 0; - if (isnew) - bits |= E5_FULLUPDATE; - - //FIXME: this stuff should be outside of this function - //with the whole nack stuff - bits = SVDP_CalcDelta(from, NULL, to, bonedatabase); + if (!bits) + return; if (bits >= 256) bits |= E5_EXTEND1; @@ -1805,9 +1800,6 @@ void SVDP_EmitEntityDelta(entity_state_t *from, entity_state_t *to, sizebuf_t *m if (bits >= 16777216) bits |= E5_EXTEND3; - if (!bits) - return; - MSG_WriteShort(msg, to->number); MSG_WriteByte(msg, bits & 0xFF); if (bits & E5_EXTEND1) @@ -1930,6 +1922,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t int oldindex, newindex; int oldnum, newnum; int oldmax; + int j; // this is the frame that we are going to delta update from if (!client->netchan.incoming_sequence) @@ -1939,11 +1932,59 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t } else { - client_frame_t *fromframe = &client->frameunion.frames[(client->netchan.incoming_sequence-1) & UPDATE_MASK]; - from = &fromframe->entities; + from = &client->sentents; oldmax = from->num_entities; } + if (to->num_entities) + { + j = to->entities[to->num_entities-1].number+1; + if (j > from->max_entities) + { + from->entities = BZ_Realloc(from->entities, sizeof(*from->entities) * j); + memset(&from->entities[from->max_entities], 0, sizeof(from->entities[0]) * (j - from->max_entities)); + from->max_entities = j; + } + while(j > client->sentents.num_entities) + { + from->entities[from->num_entities].number = 0; + from->num_entities++; + } + } + + //diff the from+to states, flagging any changed state (which is combined with any state from previous packet loss + newindex = 0; + oldindex = 0; + while (newindex < to->num_entities || oldindex < oldmax) + { + newnum = newindex >= to->num_entities ? 0x7fff : to->entities[newindex].number; + oldnum = oldindex >= oldmax ? 0x7fff : from->entities[oldindex].number; + + if (newnum < oldnum) + { // this is a new entity, send it from the baseline... as far as dp understands it... + client->pendingdeltabits[newnum] |= E5_FULLUPDATE | SVDP_CalcDelta(&nullentitystate, NULL, &to->entities[oldindex], to->bonedata); + newindex++; + } + else if (newnum > oldnum) + { // the old entity isn't present in the new message + client->pendingdeltabits[oldnum] = E5_SERVERREMOVE; + oldindex++; + } + else + { // delta update from old position + client->pendingdeltabits[newnum] |= SVDP_CalcDelta(&from->entities[oldindex], NULL/*from->bonedata*/, &to->entities[oldindex], to->bonedata); + if (client->pendingdeltabits[newnum] & E5_SERVERREMOVE) + { //if it got flagged for removal, but its actually a valid entity, then assume that its an outdated remove and just flag it for a full update in case stuff got lost. + client->pendingdeltabits[newnum] &= ~E5_SERVERREMOVE; + client->pendingdeltabits[newnum] |= E5_FULLUPDATE; + } + oldindex++; + newindex++; + } + } + + //loop through all ents and send them as required + // Con_Printf ("frame %i\n", client->netchan.incoming_sequence); MSG_WriteByte(msg, svcdp_entities); @@ -1955,7 +1996,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t //add in the bitmasks of dropped packets. - newindex = 0; +/* newindex = 0; oldindex = 0; //Con_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK // , client->netchan.outgoing_sequence & UPDATE_MASK); @@ -1989,7 +2030,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t continue; } } - +*/ MSG_WriteShort(msg, 0x8000); } #endif @@ -3862,6 +3903,7 @@ void SV_Snapshot_Clear(packet_entities_t *pack) numnails = 0; } +#ifdef QWOVERQ3 /* ============= SVQ3Q1_BuildEntityPacket @@ -3876,6 +3918,7 @@ void SVQ3Q1_BuildEntityPacket(client_t *client, packet_entities_t *pack) SV_Snapshot_SetupPVS(client, &cameras); SV_Snapshot_BuildQ1(client, pack, &cameras, client->edict); } +#endif /* ============= diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 9eaf254f..2fa159d1 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -41,6 +41,14 @@ void SV_Tcpport6_Callback(struct cvar_s *var, char *oldvalue); void SV_Port_Callback(struct cvar_s *var, char *oldvalue); void SV_PortIPv6_Callback(struct cvar_s *var, char *oldvalue); void SV_PortIPX_Callback(struct cvar_s *var, char *oldvalue); +#ifdef HAVE_DTLS +void SV_Listen_Dtls_Changed(struct cvar_s *var, char *oldvalue) +{ + if (var->ival) + if (!DTLS_HasCertificate()) + var->ival = 0; //disable the cvar (internally) if we don't have a usable certificate. this allows us to default the cvar to enabled without it breaking otherwise. +} +#endif client_t *host_client; // current client @@ -97,10 +105,15 @@ extern cvar_t sv_allow_splitscreen; cvar_t sv_serverip = CVARD("sv_serverip", "", "Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional."); cvar_t sv_public = CVAR("sv_public", "0"); -cvar_t sv_listen_qw = CVARAF("sv_listen_qw", "1", "sv_listen", 0); +cvar_t sv_listen_qw = CVARAFD("sv_listen_qw", "1", "sv_listen", 0, "Specifies whether normal clients are allowed to connect."); cvar_t sv_listen_nq = CVARD("sv_listen_nq", "2", "Allow new (net)quake clients to connect to the server.\n0 = don't let them in.\n1 = allow them in (WARNING: this allows 'qsmurf' DOS attacks).\n2 = accept (net)quake clients by emulating a challenge (as secure as QW/Q2 but does not fully conform to the NQ protocol)."); cvar_t sv_listen_dp = CVARD("sv_listen_dp", "0", "Allows the server to respond with the DP-specific handshake protocol.\nWarning: this can potentially get confused with quake2, and results in race conditions with both vanilla netquake and quakeworld protocols.\nOn the plus side, DP clients can usually be identified correctly, enabling a model+sound limit boost."); +#ifdef QWOVERQ3 cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0"); +#endif +#ifdef HAVE_DTLS +cvar_t sv_listen_dtls = CVARCD("net_enable_dtls", "", SV_Listen_Dtls_Changed, "Controls serverside dtls support.\n0: dtls blocked, not advertised.\n1: available in desired.\n2: used where possible.\n3: disallow non-dtls clients (sv_port_tcp should be eg tls://[::]:27500 to also disallow unencrypted tcp connections)."); +#endif cvar_t sv_reportheartbeats = CVAR("sv_reportheartbeats", "1"); cvar_t sv_highchars = CVAR("sv_highchars", "1"); cvar_t sv_maxrate = CVAR("sv_maxrate", "30000"); @@ -679,6 +692,10 @@ void SV_DropClient (client_t *drop) //send twice, to cover packetloss a little. Netchan_Transmit (&drop->netchan, termmsg.cursize, termmsg.data, 10000); Netchan_Transmit (&drop->netchan, termmsg.cursize, termmsg.data, 10000); + +#ifdef HAVE_DTLS + NET_DTLS_Disconnect(svs.sockets, &drop->netchan.remote_address); +#endif } if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) //gamecode should do it all for us. @@ -1071,7 +1088,7 @@ Responds with all the info that qplug or qspy can see This message can be up to around 5k with worst case string lengths. ================ */ -void SVC_Status (void) +static void SVC_Status (void) { int displayflags; int i; @@ -1150,7 +1167,7 @@ void SVC_Status (void) } #ifdef NQPROT -void SVC_GetInfo (char *challenge, int fullstatus) +static void SVC_GetInfo (char *challenge, int fullstatus) { //dpmaster support char response[MAX_UDP_PACKET]; @@ -1267,7 +1284,7 @@ void SVC_GetInfo (char *challenge, int fullstatus) #endif #ifdef Q2SERVER -void SVC_InfoQ2 (void) +static void SVC_InfoQ2 (void) { char string[64]; int i, count; @@ -1301,7 +1318,7 @@ SV_CheckLog =================== */ #define LOG_FLUSH 10*60 -void SV_CheckLog (void) +static void SV_CheckLog (void) { sizebuf_t *sz; @@ -1332,7 +1349,7 @@ the same as the current sequence, an A2A_NACK will be returned instead of the data. ================ */ -void SVC_Log (void) +static void SVC_Log (void) { unsigned int seq; char data[MAX_DATAGRAM+64]; @@ -1439,7 +1456,7 @@ flood the server with invalid connection IPs. With a challenge, they must give a valid IP address. ================= */ -void SVC_GetChallenge (qboolean nodpresponse) +qboolean SVC_GetChallenge (qboolean respond_dp) { #ifdef HUFFNETWORK int compressioncrc; @@ -1449,24 +1466,71 @@ void SVC_GetChallenge (qboolean nodpresponse) int lng; char *over; - if (!sv_listen_qw.value && !sv_listen_dp.value && !sv_listen_q3.ival) - return; + qboolean respond_std = true; +#ifdef QWOVERQ3 + qboolean respond_qwoverq3 = true; + respond_qwoverq3 &= !!sv_listen_q3.value; +#else + const qboolean respond_qwoverq3 = false; +#endif + respond_std &= !!sv_listen_qw.value; + respond_dp &= !!sv_listen_dp.value; + + if (progstype == PROG_H2) + respond_dp = false; //don't bother. dp doesn't support the maps anyway. + //dp's connections result in race conditions or are ambiguous in certain regards + //race: dp vs nq. + // the dp request will generally arrive first. we check if there was a recent challenge requested, and inhibit the nq response, ensuring that dp clients connect with a known protocol + //race: dp vs qw. + // DP clients will just bindly respond to both with a connection request. sending the dp one usually means the server will see the dp connection request first + // FTE clients explicitly ignore dp challenges with the specific 'FTE' prefix so you get qw connections there. + //conflict: dp vs q2. dp challenge responses USUALLY contain letters. vanilla q2 is always a 32bit int. FTE clients will check that before sending an appropriate response. + //so: + // vanilla nq doesn't send getchallenge, its nq connect is not inhibited, and connects directly (we optionally hack a challenge over stuffcmds, as well as protocol extensions). + // dp gets a dp+qw challenge, its nq request is ignored due to packet ordering and a small timeout, the server sees the dp connection request first and ignores the qw connect. + // fte's nq request is treated as a getchallenge. fte clients ignore the dp challenge response (if qw protocols are still enabled). ends up with a qw/fte connection + if (!(sv_listen_nq.value || sv_bigcoords.value || !respond_std)) + respond_dp = false; + +#ifdef QWOVERQ3 + if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM) + respond_qwoverq3 = false; //should probably just nuke this feature. +#endif + + if (!respond_std && !respond_dp && !respond_qwoverq3) + return false; + + if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM) + { //if we're running q2 or q3, just ignore the whole DP thing. its irrelevent in those game modes. + respond_std |= true; +#ifdef QWOVERQ3 + respond_qwoverq3 = false; +#endif + respond_dp = false; + } + if (respond_dp) + respond_std = false; challenge = SV_NewChallenge(); - // send it back + //different game modes require different types of responses + switch(svs.gametype) + { #ifdef Q3SERVER - if (svs.gametype == GT_QUAKE3) //q3 servers + case GT_QUAKE3: //q3 servers buf = va("challengeResponse %i", challenge); - else + break; #endif #ifdef Q2SERVER - if (svs.gametype == GT_QUAKE2) + case GT_QUAKE2: buf = va("challenge %i", challenge); //quake 2 servers give a different challenge response - else + break; #endif + default: buf = va("%c%i", S2C_CHALLENGE, challenge); //quakeworld's response is a bit poo. + break; + } over = buf + strlen(buf) + 1; @@ -1527,12 +1591,25 @@ void SVC_GetChallenge (qboolean nodpresponse) over+=sizeof(lng); } #endif + +#ifdef HAVE_DTLS + if (sv_listen_dtls.ival/* || !*sv_listen_dtls.string*/) + { + lng = LittleLong(PROTOCOL_VERSION_DTLSUPGRADE); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); + + if (sv_listen_dtls.ival >= 2) + lng = LittleLong(2); //required + else + lng = LittleLong(1); //supported + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); + } +#endif } - if (progstype == PROG_H2) - nodpresponse = true; - - if (!nodpresponse && sv_listen_dp.value && (sv_listen_nq.value || sv_bigcoords.value || !sv_listen_qw.value)) + if (respond_dp) { char *dp; if (sv_listen_qw.value) @@ -1542,22 +1619,23 @@ void SVC_GetChallenge (qboolean nodpresponse) Netchan_OutOfBand(NS_SERVER, &net_from, strlen(dp)+1, dp); } - if (sv_listen_qw.value || (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)) + if (respond_std) Netchan_OutOfBand(NS_SERVER, &net_from, over-buf, buf); -#ifdef Q3SERVER +#ifdef QWOVERQ3 if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) { - if (sv_listen_q3.ival) + if (respond_qwoverq3) { buf = va("challengeResponse %i", challenge); Netchan_OutOfBand(NS_SERVER, &net_from, strlen(buf), buf); } } #endif + return true; } -void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...) +static void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...) { va_list argptr; char string[8192]; @@ -1579,7 +1657,7 @@ void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...) Netchan_OutOfBand (NS_SERVER, adr, strlen(string), (qbyte *)string); } -void VARGS SV_OutOfBandTPrintf (int q2, netadr_t *adr, int language, translation_t text, ...) +static void VARGS SV_OutOfBandTPrintf (int q2, netadr_t *adr, int language, translation_t text, ...) { va_list argptr; char string[8192]; @@ -1621,7 +1699,7 @@ qboolean SV_ChallengePasses(int challenge) //this means that DP clients tend to connect as generic NQ clients. //and because DP _REQUIRES_ sv_bigcoords, they tend to end up being given fitz/rmq protocols //thus we don't respond to the connect if sv_listen_dp is 1, and we had a recent getchallenge request. recent is 2 secs. -qboolean SV_ChallengeRecent(void) +static qboolean SV_ChallengeRecent(void) { int curtime = realtime; //yeah, evil. sue me. consitent with challenges. int i; @@ -1872,7 +1950,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) if (client->frameunion.frames) Z_Free(client->frameunion.frames); - if ((client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))// || ISDPCLIENT(&temp)) + if ((client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || ISDPCLIENT(client)) { char *ptr; int maxents = maxpacketentities*4; /*this is the max number of ents updated per frame. we can't track more, so...*/ @@ -2144,11 +2222,15 @@ client_t *SVC_DirectConnect(void) if (*Cmd_Argv(1) == '\\') { //connect "\key\val" - +#ifndef QWOVERQ3 + SV_RejectMessage (SCP_QUAKE3, "This is not a q3 server: %s\n", version_string()); + Con_TPrintf ("* rejected connect from q3 client\n"); + return NULL; +#else //this is used by q3 (note, we already decrypted the huffman connection packet in a hack) if (!sv_listen_q3.ival) { - SV_RejectMessage (SCP_QUAKE3, "Server is not accepting quake3 clients at this time.\n", version_string()); + SV_RejectMessage (SCP_QUAKE3, "Server is not accepting quake3 clients at this time: %s\n", version_string()); Con_TPrintf ("* rejected connect from q3 client\n"); return NULL; } @@ -2181,6 +2263,7 @@ client_t *SVC_DirectConnect(void) #ifdef HUFFNETWORK huffcrc = HUFFCRC_QUAKE3; +#endif #endif } else if (*(Cmd_Argv(0)+7) == '\\') @@ -2237,7 +2320,7 @@ client_t *SVC_DirectConnect(void) {"NEHAHRABJP", 0}, {"NEHAHRABJP2", 0}, {"NEHAHRABJP3", 1u< 2 && (net_from.prot == NP_DGRAM || net_from.prot == NP_STREAM || net_from.prot == NP_WS)) + { + SV_RejectMessage (protocol, "This server requires the use of DTLS/TLS/WSS.\n"); + return NULL; + } +#endif + { char *banreason = SV_BannedReason(&net_from); if (banreason) @@ -2509,7 +2600,7 @@ client_t *SVC_DirectConnect(void) else if (!strcmp(sv_protocol_nq.string, "dp6")) protocol = SCP_DARKPLACES6; else if (!strcmp(sv_protocol_nq.string, "dp7")) - protocol = SCP_DARKPLACES6; + protocol = SCP_DARKPLACES7; else if (!strcmp(sv_protocol_nq.string, "id") || !strcmp(sv_protocol_nq.string, "vanilla")) protocol = SCP_NETQUAKE; else switch(sv_protocol_nq.ival) @@ -3451,6 +3542,11 @@ qboolean SV_ConnectionlessPacket (void) #endif else if (!strncmp(c,"connect", 7)) { +#ifdef HAVE_DTLS + if (net_from.prot == NP_DGRAM) + NET_DTLS_Disconnect(svs.sockets, &net_from); +#endif + #ifdef Q3SERVER if (svs.gametype == GT_QUAKE3) { @@ -3458,6 +3554,7 @@ qboolean SV_ConnectionlessPacket (void) return true; } +#ifdef QWOVERQ3 if (sv_listen_q3.ival) { if (!strstr(s, "\\name\\")) @@ -3473,7 +3570,9 @@ qboolean SV_ConnectionlessPacket (void) } } #endif - if (secure.value) //FIXME: possible problem for nq clients when enabled +#endif + + if (secure.value) //FIXME: possible problem for nq clients when enabled { Netchan_OutOfBandTPrintf (NS_SERVER, &net_from, svs.language, "%c\nThis server requires client validation.\nPlease use the "FULLENGINENAME" validation program\n", A2C_PRINT); } @@ -3483,13 +3582,44 @@ qboolean SV_ConnectionlessPacket (void) return true; } } - else if (!strcmp(c,"\xad\xad\xad\xad""getchallenge")) + else if (!strcmp(c,"dtlsconnect")) { - SVC_GetChallenge (true); +#ifdef HAVE_DTLS + if (net_from.prot == NP_DGRAM && (sv_listen_dtls.ival /*|| !*sv_listen_dtls.ival*/)) + { + if (SV_ChallengePasses(atoi(Cmd_Argv(1)))) + { + char *banreason = SV_BannedReason(&net_from); + if (banreason) + { + if (*banreason) + SV_RejectMessage (SCP_QUAKEWORLD, "You were banned.\nReason: %s\n", banreason); + else + SV_RejectMessage (SCP_QUAKEWORLD, "You were banned.\n"); + } + else + { + //NET_DTLS_Disconnect(svs.sockets, &net_from); + if (NET_DTLS_Create(svs.sockets, &net_from)) + Netchan_OutOfBandPrint(NS_SERVER, &net_from, "dtlsopened"); + } + } + else + SV_RejectMessage (SCP_QUAKEWORLD, "Bad challenge.\n"); + } + return true; +#endif } - else if (!strcmp(c,"getchallenge")) + /*else if (!strcmp(c,"\xad\xad\xad\xad""getchallenge")) { SVC_GetChallenge (false); + }*/ + else if (!strcmp(c,"getchallenge")) + { + //qw+q2 always sends "\xff\xff\xff\xffgetchallenge\n" + //dp+q3 always sends "\xff\xff\xff\xffgetchallenge" + //its a subtle difference, but means we can avoid wasteful spam for real qw clients. + SVC_GetChallenge ((net_message.cursize==16)?true:false); } #ifdef NQPROT /*for DP*/ @@ -3537,6 +3667,7 @@ qboolean SVNQ_ConnectionlessPacket(void) char buffer[256], buffer2[256]; netadr_t localaddr; char *banreason; + if (net_from.type == NA_LOOPBACK) return false; @@ -3689,7 +3820,7 @@ qboolean SVNQ_ConnectionlessPacket(void) else if (!strncmp(MSG_ReadString(), "getchallenge", 12) && (sv_listen_qw.ival || sv_listen_dp.ival)) { /*dual-stack client, supporting either DP or QW protocols*/ - SVC_GetChallenge (true); + SVC_GetChallenge (false); } else { @@ -4024,6 +4155,21 @@ qboolean SV_ReadPackets (float *delay) SV_ConnectionlessPacket(); continue; } +#ifdef HAVE_DTLS + else + { + if (NET_DTLS_Decode(svs.sockets)) + { + if (!net_message.cursize) + continue; + if (*(unsigned int *)net_message.data == ~0) + { + SV_ConnectionlessPacket(); + continue; + } + } + } +#endif #ifdef Q3SERVER if (svs.gametype == GT_QUAKE3) @@ -4139,7 +4285,7 @@ dominping: if (i != svs.allocated_client_slots) continue; -#ifdef Q3SERVER +#ifdef QWOVERQ3 if (sv_listen_q3.ival && SVQ3_HandleClient()) { received++; @@ -4162,6 +4308,10 @@ dominping: Con_Printf ("%s:sequenced packet without connection\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); //hack: com_token cos we need some random temp buffer. } +#ifdef HAVE_DTLS + NET_DTLS_Timeouts(svs.sockets); +#endif + return received; } @@ -4848,8 +4998,13 @@ void SV_InitLocal (void) Cvar_Register (&sv_listen_qw, cvargroup_servercontrol); Cvar_Register (&sv_listen_nq, cvargroup_servercontrol); Cvar_Register (&sv_listen_dp, cvargroup_servercontrol); +#ifdef QWOVERQ3 Cvar_Register (&sv_listen_q3, cvargroup_servercontrol); - sv_listen_qw.restriction = RESTRICT_MAX; +#endif +#ifdef HAVE_DTLS + Cvar_Register (&sv_listen_dtls, cvargroup_servercontrol); +#endif + sv_listen_qw.restriction = RESTRICT_MAX; //no disabling this over rcon. Cvar_Register (&fraglog_public, cvargroup_servercontrol); SVNET_RegisterCvars(); diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index ec09a69e..10f31b9b 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -2389,7 +2389,11 @@ void SV_UserCmdMVDList_HTML (vfsfile_t *pipe) VFS_PRINTF(pipe, "*%d: %s %dk
\n", i, list->name, d->totalsize/1024); } if (!d) - VFS_PRINTF(pipe, "%d: %s %dk play
\n", i, list->name, list->name, list->size/1024, list->name); + { + char datetime[64]; + strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", localtime(&list->mtime)); + VFS_PRINTF(pipe, "%d: %s %dk play %s
\n", i, list->name, list->name, list->size/1024, list->name, datetime); + } } for (d = demo.dest; d; d = d->nextdest) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index b2170602..31b151e5 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -528,8 +528,8 @@ void SVNQ_New_f (void) qboolean big; //used as a filter to exclude protocols that don't match our coord+angles mode } preferedprot[] = { - {SCP_DARKPLACES7, true}, - {SCP_DARKPLACES6, true}, +// {SCP_DARKPLACES7, true}, +// {SCP_DARKPLACES6, true}, {SCP_FITZ666, true}, //actually 999... shh... {SCP_FITZ666, false}, {SCP_BJP3, false} @@ -604,7 +604,7 @@ void SVNQ_New_f (void) protoname = "NQ"; } break; - case SCP_DARKPLACES6: + /*case SCP_DARKPLACES6: SV_LogPlayer(host_client, "new (DP6)"); protmain = PROTOCOL_VERSION_DP6; protext1 &= ~PEXT_FLOATCOORDS; //always enabled, try not to break things @@ -615,7 +615,7 @@ void SVNQ_New_f (void) protmain = PROTOCOL_VERSION_DP7; protext1 &= ~PEXT_FLOATCOORDS; //always enabled, try not to break things protoname = "DPP7"; - break; + break;*/ default: host_client->drop = true; protoname = "?""?""?"; @@ -6869,7 +6869,7 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse) VectorCopy(sv_player->v->origin, pmove.origin); VectorCopy(sv_player->v->oldorigin, pmove.safeorigin); - pmove.safeorigin_known = true; + pmove.safeorigin_known = progstype != PROG_QW; VectorCopy (sv_player->v->velocity, pmove.velocity); VectorCopy (sv_player->v->v_angle, pmove.angles); diff --git a/engine/server/svq3_game.c b/engine/server/svq3_game.c index 483d0f1f..4387e730 100644 --- a/engine/server/svq3_game.c +++ b/engine/server/svq3_game.c @@ -78,7 +78,9 @@ static qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_ void SVQ3_CreateBaseline(void); void SVQ3_ClientThink(client_t *cl); -void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3); +#ifdef QWOVERQ3 +static void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3); +#endif const char *mapentspointer; @@ -1991,9 +1993,8 @@ void SVQ3_EmitPacketEntities(client_t *client, q3client_frame_t *from, q3client_ if(newnum < oldnum) { // this is a new entity, send it from the baseline - if (svs.gametype == GT_QUAKE3) - MSGQ3_WriteDeltaEntity( msg, &q3_baselines[newnum], newent, true ); - else +#ifdef QWOVERQ3 + if (svs.gametype != GT_QUAKE3) { q3entityState_t q3base; edict_t *e; @@ -2001,6 +2002,9 @@ void SVQ3_EmitPacketEntities(client_t *client, q3client_frame_t *from, q3client_ SVQ3Q1_ConvertEntStateQ1ToQ3(&e->baseline, &q3base); MSGQ3_WriteDeltaEntity( msg, &q3base, newent, true ); } + else +#endif + MSGQ3_WriteDeltaEntity( msg, &q3_baselines[newnum], newent, true ); newindex++; continue; } @@ -2230,7 +2234,7 @@ static qboolean SVQ3_EntityIsVisible(q3client_frame_t *snap, q3sharedEntity_t *e return true; } -q3playerState_t *SVQ3Q1_BuildPlayerState(client_t *client) +static q3playerState_t *SVQ3Q1_BuildPlayerState(client_t *client) { static q3playerState_t state; extern cvar_t sv_gravity; @@ -2411,6 +2415,7 @@ void SVQ3_BuildClientSnapshot( client_t *client ) } } } +#ifdef QWOVERQ3 else { //our q1->q3 converter packet_entities_t pack; @@ -2426,6 +2431,7 @@ void SVQ3_BuildClientSnapshot( client_t *client ) entityStates[snap->num_entities++] = &q3packentities[i]; } } +#endif if( q3_next_snapshot_entities + snap->num_entities >= 0x7FFFFFFE ) { @@ -2450,10 +2456,10 @@ void SVQ3_BuildClientSnapshot( client_t *client ) { //fix areabits, q2->q3 style.. snap->areabits[i]^=255; } - } -void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3) +#ifdef QWOVERQ3 +static void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3) { #ifdef warningmsg #pragma warningmsg("qwoverq3: This _WILL_ need extending") @@ -2511,31 +2517,9 @@ void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3) q3->angles2[2] = 0; q3->constantLight = q1->abslight; q3->frame = q1->frame; - -#if 0 - -//these are the things I've not packed in to the above structure yet. -#if defined(Q2CLIENT) || defined(Q2SERVER) - int renderfx; //q2 - qbyte modelindex3; //q2 - qbyte modelindex4; //q2 -#endif - qbyte glowsize; - qbyte glowcolour; - qbyte scale; - char fatness; - qbyte hexen2flags; - qbyte dpflags; - qbyte colormod[3];//multiply this by 8 to read as 0 to 1... - qbyte lightstyle; - qbyte lightpflags; - unsigned short light[4]; - unsigned short tagentity; - unsigned short tagindex; -#endif } -void SVQ3Q1_SendGamestateConfigstrings(sizebuf_t *msg) +static void SVQ3Q1_SendGamestateConfigstrings(sizebuf_t *msg) { const int cs_models = 32; const int cs_sounds = cs_models + 256; @@ -2614,6 +2598,7 @@ void SVQ3Q1_SendGamestateConfigstrings(sizebuf_t *msg) MSG_WriteBits(msg, 0, 8); } } +#endif //writes initial gamestate void SVQ3_SendGameState(client_t *client) @@ -2664,6 +2649,7 @@ void SVQ3_SendGameState(client_t *client) MSGQ3_WriteDeltaEntity( &msg, NULL, &q3_baselines[i], true ); } break; +#ifdef QWOVERQ3 case GT_PROGS: case GT_Q1QVM: SVQ3Q1_SendGamestateConfigstrings(&msg); @@ -2680,10 +2666,9 @@ void SVQ3_SendGameState(client_t *client) } } break; - // warning: enumeration value GT_? not handled in switch - case GT_HALFLIFE: - case GT_QUAKE2: - case GT_MAX: +#endif + default: + client->drop = true; break; } diff --git a/engine/shaders/generatebuiltinsl.c b/engine/shaders/generatebuiltinsl.c index 92701828..227aab2b 100644 --- a/engine/shaders/generatebuiltinsl.c +++ b/engine/shaders/generatebuiltinsl.c @@ -25,6 +25,7 @@ char shaders[][64] = "defaultgammacb", "drawflat_wall", "wireframe", + "itemtimer", "lpp_depthnorm", "lpp_light", "lpp_wall", diff --git a/engine/shaders/glsl/itemtimer.glsl b/engine/shaders/glsl/itemtimer.glsl new file mode 100644 index 00000000..56c0e777 --- /dev/null +++ b/engine/shaders/glsl/itemtimer.glsl @@ -0,0 +1,43 @@ +!!permu FOG + +#include "sys/defs.h" +#include "sys/fog.h" + +varying vec2 tc; +varying vec4 vc; + +#ifdef VERTEX_SHADER +void main () +{ + tc = v_texcoord; + vc = v_colour; + gl_Position = ftetransform(); +} +#endif + + +#ifdef FRAGMENT_SHADER +void main () +{ + gl_FragColor = vec4(0.5,0.5,0.5,1);//texture2D(s_diffuse, tc.xy); + + vec2 st = (tc-floor(tc)) - 0.5; + st *= 2.0; + float dist = sqrt(dot(st,st)); + + float ring = 1.0 + smoothstep(0.9, 1.0, dist) + - smoothstep(0.8, 0.9, dist); + + //fade out the rim + if ((atan(st.t, st.s)+3.14)/6.28 > vc.a) + gl_FragColor.a *= 0.25; + gl_FragColor.rgb *= mix(vc.rgb, vec3(0.0), ring); +//gl_FragColor.a; + +//and finally hide it all if we're fogged. +#ifdef FOG + gl_FragColor = fog4additive(gl_FragColor); +#endif +} +#endif + diff --git a/engine/sw/sw.h b/engine/sw/sw.h index 9724fb1c..09308450 100644 --- a/engine/sw/sw.h +++ b/engine/sw/sw.h @@ -149,7 +149,7 @@ void SWRast_Sync(struct workqueue_s *wq); qboolean SW_VID_Init(rendererstate_t *info, unsigned char *palette); void SW_VID_DeInit(void); qboolean SW_VID_ApplyGammaRamps (unsigned int rampcount, unsigned short *ramps); -char *SW_VID_GetRGBInfo(int *truevidwidth, int *truevidheight, enum uploadfmt *fmt); +char *SW_VID_GetRGBInfo(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt); void SW_VID_SetWindowCaption(const char *msg); void SW_VID_SwapBuffers(void); void SW_VID_UpdateViewport(wqcom_t *com); diff --git a/engine/sw/sw_backend.c b/engine/sw/sw_backend.c index 7656e4a2..89b5ee11 100644 --- a/engine/sw/sw_backend.c +++ b/engine/sw/sw_backend.c @@ -575,6 +575,12 @@ void SWBE_Set2D(void) float ang, rad, w, h; float tmp[16]; float tmp2[16]; + + vid.fbvwidth = vid.width; + vid.fbvheight = vid.height; + vid.fbpwidth = vid.pixelwidth; + vid.fbpheight = vid.pixelheight; + ang = (gl_screenangle.value>0?(gl_screenangle.value+45):(gl_screenangle.value-45))/90; ang = (int)ang * 90; if (ang) diff --git a/engine/sw/sw_rast.c b/engine/sw/sw_rast.c index df2f649d..4c02c2b1 100644 --- a/engine/sw/sw_rast.c +++ b/engine/sw/sw_rast.c @@ -906,7 +906,26 @@ void SW_R_RenderView(void) if (!cl.worldmodel || (!cl.worldmodel->nodes && cl.worldmodel->type != mod_heightmap)) r_refdef.flags |= RDF_NOWORLDMODEL; -// R_SetupGL (); + //no fbos here + vid.fbvwidth = vid.width; + vid.fbvheight = vid.height; + vid.fbpwidth = vid.pixelwidth; + vid.fbpheight = vid.pixelheight; + + { + //figure out the viewport that we should be using. + int x = floor(r_refdef.vrect.x * (float)vid.fbpwidth/(float)vid.width); + int x2 = ceil((r_refdef.vrect.x + r_refdef.vrect.width) * (float)vid.fbpwidth/(float)vid.width); + int y = floor(r_refdef.vrect.y * (float)vid.fbpheight/(float)vid.height); + int y2 = ceil((r_refdef.vrect.y + r_refdef.vrect.height) * (float)vid.fbpheight/(float)vid.height); + int w = x2 - x; + int h = y2 - y; + r_refdef.pxrect.x = x; + r_refdef.pxrect.y = y; + r_refdef.pxrect.width = w; + r_refdef.pxrect.height = h; + r_refdef.pxrect.maxheight = vid.fbpheight; + } AngleVectors (r_refdef.viewangles, vpn, vright, vup); VectorCopy (r_refdef.vieworg, r_origin); @@ -982,7 +1001,6 @@ qboolean SW_SCR_UpdateScreen(void) if (!CSQC_DrawView()) V_RenderView (); - R2D_PolyBlend (); R2D_BrightenScreen(); } diff --git a/engine/sw/sw_vidwin.c b/engine/sw/sw_vidwin.c index 0803ab3f..4e356193 100644 --- a/engine/sw/sw_vidwin.c +++ b/engine/sw/sw_vidwin.c @@ -774,7 +774,7 @@ qboolean SW_VID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ra { return false; } -char *SW_VID_GetRGBInfo(int *truevidwidth, int *truevidheight, enum uploadfmt *fmt) +char *SW_VID_GetRGBInfo(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt) { char *buf = NULL; char *src, *dst; @@ -790,6 +790,7 @@ char *SW_VID_GetRGBInfo(int *truevidwidth, int *truevidheight, enum uploadfmt *f dst[2] = src[0]; } } + *bytestride = vid.pixelwidth*3; *truevidwidth = vid.pixelwidth; *truevidheight = vid.pixelheight; *fmt = TF_BGR24; diff --git a/engine/web/ftejslib.h b/engine/web/ftejslib.h index 84ef49a6..e4918ff6 100644 --- a/engine/web/ftejslib.h +++ b/engine/web/ftejslib.h @@ -53,8 +53,8 @@ int emscriptenfte_setupcanvas( void(*Button)(unsigned int devid, int down, int mbutton), int(*Keyboard)(unsigned int devid, int down, int keycode, int unicode), void(*LoadFile)(char *url, char *mime, int filehandle), - void(*buttonevent)(unsigned int joydev, int button, int ispressed), - void(*axisevent)(unsigned int joydev, int axis, float value), + void(*buttonevent)(unsigned int joydev, int button, int ispressed, int isstandard), + void(*axisevent)(unsigned int joydev, int axis, float value, int isstandard), int (*ShouldSwitchToFullscreen)(void) ); diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js index 69ec16e5..a09747f2 100644 --- a/engine/web/ftejslib.js +++ b/engine/web/ftejslib.js @@ -266,10 +266,10 @@ mergeInto(LibraryManager.library, delete FTEH.gamepads[gp.index]; if (FTEC.evcb.jaxis) //try and clear out the axis when released. for (var j = 0; j < 6; j+=1) - Runtime.dynCall('viid', FTEC.evcb.jaxis, [gp.index, j, 0]); + Runtime.dynCall('viidi', FTEC.evcb.jaxis, [gp.index, j, 0, true]); if (FTEC.evcb.jbutton) //try and clear out the axis when released. for (var j = 0; j < 32+4; j+=1) - Runtime.dynCall('viid', FTEC.evcb.jbutton, [gp.index, j, 0]); + Runtime.dynCall('viiii', FTEC.evcb.jbutton, [gp.index, j, 0, true]); console.log("Gamepad disconnected from index %d: %s", gp.index, gp.id); break; case 'pointerlockchange': @@ -362,9 +362,9 @@ mergeInto(LibraryManager.library, //with events, we can do unplug stuff properly. //otherwise hot unplug might be buggy. var gamepads; - if (FTEH.gamepads !== undefined) - gamepads = FTEH.gamepads; - else +// if (FTEH.gamepads !== undefined) +// gamepads = FTEH.gamepads; +// else gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []); if (gamepads !== undefined) @@ -380,23 +380,18 @@ mergeInto(LibraryManager.library, var b = gp.buttons[j]; var p; if (typeof(b) == "object") - { - p = b.pressed; - if (b.lastframe != p) - { //cache it to avoid spam - b.lastframe = p; - Runtime.dynCall('viii', FTEC.evcb.jbutton, [gp.index, j, p]); - } - } + p = b.pressed; //.value is a fractional thing. oh well. else - {//old chrome bug - p = b==1.0; - //warning: no cache. this is going to be spammy. - Runtime.dynCall('viii', FTEC.evcb.jbutton, [gp.index, j, p]); + p = b > 0.5; //old chrome bug + + if (b.lastframe != p) + { //cache it to avoid spam + b.lastframe = p; + Runtime.dynCall('viiii', FTEC.evcb.jbutton, [gp.index, j, p, gp.mapping=="standard"]); } } for (var j = 0; j < gp.axes.length; j+=1) - Runtime.dynCall('viid', FTEC.evcb.jaxis, [gp.index, j, gp.axes[j]]); + Runtime.dynCall('viidi', FTEC.evcb.jaxis, [gp.index, j, gp.axes[j], gp.mapping=="standard"]); } }, emscriptenfte_setupcanvas__deps: ['$FTEC', '$Browser', 'emscriptenfte_buf_createfromarraybuf'], @@ -437,7 +432,6 @@ mergeInto(LibraryManager.library, 'keypress', 'keydown', 'keyup', 'touchstart', 'touchend', 'touchcancel', 'touchleave', 'touchmove', 'dragenter', 'dragover', 'drop', - 'gamepadconnected', 'gamepaddisconnected', 'message', 'pointerlockchange', 'mozpointerlockchange', 'webkitpointerlockchange', 'focus', 'blur']; //try to fix alt-tab @@ -453,7 +447,7 @@ mergeInto(LibraryManager.library, document.addEventListener(event, FTEC.handleevent, true); }); - var windowevents = ['message','vrdisplaypresentchange','vrdisplayactivate','vrdisplaydeactivate']; + var windowevents = ['message','vrdisplaypresentchange','vrdisplayactivate','vrdisplaydeactivate','gamepadconnected', 'gamepaddisconnected']; windowevents.forEach(function(event) { window.addEventListener(event, FTEC.handleevent, true); diff --git a/engine/web/gl_vidweb.c b/engine/web/gl_vidweb.c index ffd9b53f..f1d33fc5 100644 --- a/engine/web/gl_vidweb.c +++ b/engine/web/gl_vidweb.c @@ -11,17 +11,79 @@ extern qboolean vid_isfullscreen; qboolean mouseactive; extern qboolean mouseusedforgui; +static int gamepaddeviceids[] = {DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET}; +static int keyboardid[] = {0}; +static int mouseid[] = {0}; static void *GLVID_getsdlglfunction(char *functionname) { return NULL; } -static void IN_JoystickButtonEvent(unsigned int joydevid, int button, int ispressed) +static void IN_GamePadButtonEvent(unsigned int joydevid, int button, int ispressed, int isstandardmapping) { - if (button >= 32+4) - return; - IN_KeyEvent(joydevid, ispressed, K_JOY1+button, 0); + int standardmapping[] = + { //the order of these keys is different from that of xinput + //however, the quake button codes should be the same. I really ought to define some K_ aliases for them. + K_GP_A, + K_GP_B, + K_GP_X, + K_GP_Y, + K_GP_LEFT_SHOULDER, + K_GP_RIGHT_SHOULDER, + K_GP_LEFT_TRIGGER, + K_GP_RIGHT_TRIGGER, + K_GP_BACK, + K_GP_START, + K_GP_LEFT_THUMB, + K_GP_RIGHT_THUMB, + K_GP_DPAD_UP, + K_GP_DPAD_DOWN, + K_GP_DPAD_LEFT, + K_GP_DPAD_RIGHT, + K_GP_GUIDE, + //K_GP_UNKNOWN + }; + + if (joydevid < countof(gamepaddeviceids)) + { + if (joydevid == gamepaddeviceids[joydevid]) + { + if (!ispressed) + return; //don't send axis events until its enabled. + gamepaddeviceids[joydevid] = joydevid; + } + joydevid = gamepaddeviceids[joydevid]; + } + + if (isstandardmapping) + { + if (button < countof(standardmapping)) + IN_KeyEvent(joydevid, ispressed, standardmapping[button], 0); + } + else + { + if (button < 32+4) + IN_KeyEvent(joydevid, ispressed, K_JOY1+button, 0); + } +} + +static void IN_GamePadAxisEvent(unsigned int joydevid, int axis, float value, int isstandardmapping) +{ + if (joydevid < countof(gamepaddeviceids)) + { + joydevid = gamepaddeviceids[joydevid]; + if (joydevid == DEVID_UNSET) + return; //don't send axis events until its enabled. + } + if (isstandardmapping) + { + int axismap[] = {GPAXIS_LT_RIGHT,GPAXIS_LT_DOWN,GPAXIS_RT_RIGHT,GPAXIS_RT_DOWN}; + if (axis < countof(axismap)) + IN_JoystickAxisEvent(joydevid, axismap[axis], value); + } + else + IN_JoystickAxisEvent(joydevid, axis, value); } static void VID_Resized(int width, int height) @@ -119,7 +181,7 @@ static int DOM_KeyEvent(unsigned int devid, int down, int scan, int uni) scan = domkeytoquake(scan); uni = (scan >= 32 && scan <= 127)?scan:0; } - IN_KeyEvent(devid, down, scan, uni); + IN_KeyEvent(keyboardid[devid], down, scan, uni); //Chars which don't map to some printable ascii value get preventDefaulted. //This is to stop fucking annoying fucking things like backspace randomly destroying the page and thus game. //And it has to be conditional, or we don't get any unicode chars at all. @@ -137,12 +199,12 @@ static void DOM_ButtonEvent(unsigned int devid, int down, int button) //fixme: the event is a float. we ignore that. while(button < 0) { - IN_KeyEvent(devid, true, K_MWHEELUP, 0); + IN_KeyEvent(mouseid[devid], true, K_MWHEELUP, 0); button += 1; } while(button > 0) { - IN_KeyEvent(devid, true, K_MWHEELDOWN, 0); + IN_KeyEvent(mouseid[devid], true, K_MWHEELDOWN, 0); button -= 1; } } @@ -154,9 +216,13 @@ static void DOM_ButtonEvent(unsigned int devid, int down, int button) else if (button == 1) button = 2; - IN_KeyEvent(devid, down, K_MOUSE1+button, 0); + IN_KeyEvent(mouseid[devid], down, K_MOUSE1+button, 0); } } +void DOM_MouseMove(unsigned int devid, int abs, float x, float y, float z, float size) +{ + IN_MouseMove(mouseid[devid], abs, x, y, z, size); +} void DOM_LoadFile(char *loc, char *mime, int handle) { @@ -206,12 +272,12 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) info->width, info->height, VID_Resized, - IN_MouseMove, + DOM_MouseMove, DOM_ButtonEvent, DOM_KeyEvent, DOM_LoadFile, - IN_JoystickButtonEvent, - IN_JoystickAxisEvent, + IN_GamePadButtonEvent, + IN_GamePadAxisEvent, VID_ShouldSwitchToFullscreen )) { @@ -307,5 +373,22 @@ void INS_Commands (void) } void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid)) { + size_t i; + char foobar[64]; + for (i = 0; i < countof(gamepaddeviceids); i++) + { + Q_snprintfz(foobar, sizeof(foobar), "gp%i", i); + callback(ctx, "gamepad", foobar, &gamepaddeviceids[i]); + } + for (i = 0; i < countof(mouseid); i++) + { + Q_snprintfz(foobar, sizeof(foobar), "m%i", i); + callback(ctx, "mouse", foobar, &mouseid[i]); + } + for (i = 0; i < countof(keyboardid); i++) + { + Q_snprintfz(foobar, sizeof(foobar), "kb%i", i); + callback(ctx, "keyboard", foobar, &keyboardid[i]); + } } diff --git a/specs/browser.txt b/specs/browser.txt index 6a1e5ecb..cf772644 100644 --- a/specs/browser.txt +++ b/specs/browser.txt @@ -1,6 +1,3 @@ -FIXME: verify+clarify these docs... - - There are multiple ways to embed a program into a browser. The 'web'/emscripten port, the nacl port, the npapi port, and the activex port. Quick start with browser-servers: @@ -31,7 +28,7 @@ sv_port_rtc - This says the broker+resource used to register a webrtc server. cfg_save - This command saves your config to your browser's local storage. In combination with seta, you can save most settings this way. Beware that browsers might still wipe it all eventually. -Hosting: +Hosting Quake: To get fte running on a web page, you will need: ftewebgl.html - An html file that embeds the javascript. You can probably modify fte's default if you want to integrate it better with your site, it doesn't change much. ftewebgl.js - This is the meat of the engine. All in a single file. pre-gzip it if you can, to keep sizes down. @@ -48,11 +45,14 @@ The browser port is set up to ignore most args when linked to from another site. Built-in http server: The http server provided by sv_port_tcp will provide a page (either directly or with a redirect) to a version of the webgl client. Thanks to allow_download_* cvars, only certain things may be downloaded. +Unlike over game connections, the server will transparently also provide http content compression only where a .gz file has also been provided (and is more recent and in the same gamedir). + Additionally, there are also some files generated by the server. index.html (and no resource) - attempts to provide a link to the webgl version of fte. -default.fmf - provides a redirect to the manifest's update url. all other files match what you would be able to download over udp. + + Manifest files: These are FTE's way of reconfiguring FTE for standalone mods. They offer basic rebranding features as well as content updates. They contain a number of attributes, and frankly its easier to start with an example. Check http://triptohell.info/moodles/web/ for a few.