mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-28 22:52:02 +00:00
work around eztv newline bug.
Websocket support. Better IPv6 support (at least as a server). git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4019 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
0e77fa7386
commit
6385756e75
10 changed files with 958 additions and 316 deletions
|
@ -509,6 +509,8 @@ int main(int argc, char **argv)
|
||||||
cluster->qwdsocket[1] = INVALID_SOCKET;
|
cluster->qwdsocket[1] = INVALID_SOCKET;
|
||||||
cluster->tcpsocket[0] = INVALID_SOCKET;
|
cluster->tcpsocket[0] = INVALID_SOCKET;
|
||||||
cluster->tcpsocket[1] = INVALID_SOCKET;
|
cluster->tcpsocket[1] = INVALID_SOCKET;
|
||||||
|
cluster->anticheattime = 1*1000;
|
||||||
|
cluster->tooslowdelay = 100;
|
||||||
cluster->qwlistenportnum = 0;
|
cluster->qwlistenportnum = 0;
|
||||||
cluster->allownqclients = true;
|
cluster->allownqclients = true;
|
||||||
strcpy(cluster->hostname, DEFAULT_HOSTNAME);
|
strcpy(cluster->hostname, DEFAULT_HOSTNAME);
|
||||||
|
@ -526,12 +528,8 @@ int main(int argc, char **argv)
|
||||||
if (cluster->qwdsocket[0] == INVALID_SOCKET && cluster->qwdsocket[1] == INVALID_SOCKET && !cluster->qwlistenportnum)
|
if (cluster->qwdsocket[0] == INVALID_SOCKET && cluster->qwdsocket[1] == INVALID_SOCKET && !cluster->qwlistenportnum)
|
||||||
{
|
{
|
||||||
cluster->qwlistenportnum = 27599;
|
cluster->qwlistenportnum = 27599;
|
||||||
cluster->qwdsocket[0] = QW_InitUDPSocket(cluster->qwlistenportnum, false);
|
NET_InitUDPSocket(cluster, cluster->qwlistenportnum, true);
|
||||||
if (cluster->qwdsocket[0] != INVALID_SOCKET)
|
NET_InitUDPSocket(cluster, cluster->qwlistenportnum, false);
|
||||||
Sys_Printf(cluster, "opened udp4 port %i\n", cluster->qwlistenportnum);
|
|
||||||
cluster->qwdsocket[1] = QW_InitUDPSocket(cluster->qwlistenportnum, true);
|
|
||||||
if (cluster->qwdsocket[1] != INVALID_SOCKET)
|
|
||||||
Sys_Printf(cluster, "opened udp6 port %i\n", cluster->qwlistenportnum);
|
|
||||||
}
|
}
|
||||||
if (cluster->tcpsocket[0] == INVALID_SOCKET && cluster->tcpsocket[1] == INVALID_SOCKET && !cluster->tcplistenportnum)
|
if (cluster->tcpsocket[0] == INVALID_SOCKET && cluster->tcpsocket[1] == INVALID_SOCKET && !cluster->tcplistenportnum)
|
||||||
{
|
{
|
||||||
|
|
|
@ -43,8 +43,7 @@
|
||||||
<Tool
|
<Tool
|
||||||
Name="VCCLCompilerTool"
|
Name="VCCLCompilerTool"
|
||||||
Optimization="0"
|
Optimization="0"
|
||||||
PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE"
|
PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS"
|
||||||
MinimalRebuild="true"
|
|
||||||
BasicRuntimeChecks="3"
|
BasicRuntimeChecks="3"
|
||||||
RuntimeLibrary="1"
|
RuntimeLibrary="1"
|
||||||
UsePrecompiledHeader="0"
|
UsePrecompiledHeader="0"
|
||||||
|
@ -74,6 +73,7 @@
|
||||||
OutputFile=".\Debug/qtvprox.exe"
|
OutputFile=".\Debug/qtvprox.exe"
|
||||||
LinkIncremental="2"
|
LinkIncremental="2"
|
||||||
SuppressStartupBanner="true"
|
SuppressStartupBanner="true"
|
||||||
|
GenerateManifest="false"
|
||||||
GenerateDebugInformation="true"
|
GenerateDebugInformation="true"
|
||||||
ProgramDatabaseFile=".\Debug/qtvprox.pdb"
|
ProgramDatabaseFile=".\Debug/qtvprox.pdb"
|
||||||
SubSystem="1"
|
SubSystem="1"
|
||||||
|
@ -264,6 +264,10 @@
|
||||||
RelativePath="..\rcon.c"
|
RelativePath="..\rcon.c"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\engine\common\sha1.c"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\source.c"
|
RelativePath="..\source.c"
|
||||||
>
|
>
|
||||||
|
|
283
fteqtv/forward.c
283
fteqtv/forward.c
|
@ -119,7 +119,7 @@ void Fwd_ParseCommands(cluster_t *cluster, oproxy_t *prox)
|
||||||
netmsg_t buf;
|
netmsg_t buf;
|
||||||
int packetlength;
|
int packetlength;
|
||||||
int bytes;
|
int bytes;
|
||||||
bytes = recv(prox->sock, prox->inbuffer+prox->inbuffersize, sizeof(prox->inbuffer)-prox->inbuffersize, 0);
|
bytes = NET_WebSocketRecv(prox->sock, &prox->websocket, prox->inbuffer+prox->inbuffersize, sizeof(prox->inbuffer)-prox->inbuffersize, NULL);
|
||||||
if (bytes < 0)
|
if (bytes < 0)
|
||||||
{
|
{
|
||||||
if (qerrno != EWOULDBLOCK && qerrno != EAGAIN) //not a problem, so long as we can flush it later.
|
if (qerrno != EWOULDBLOCK && qerrno != EAGAIN) //not a problem, so long as we can flush it later.
|
||||||
|
@ -234,6 +234,67 @@ void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length)
|
||||||
{
|
{
|
||||||
int wrap;
|
int wrap;
|
||||||
|
|
||||||
|
if (!length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (prox->websocket.websocket)
|
||||||
|
{
|
||||||
|
unsigned int c;
|
||||||
|
int enclen = 0;
|
||||||
|
|
||||||
|
/*work out how much buffer space we'll need*/
|
||||||
|
for (c = 0; c < length; c++)
|
||||||
|
{
|
||||||
|
if (((unsigned char*)buffer)[c] == 0 || ((unsigned char*)buffer)[c] >= 0x80)
|
||||||
|
enclen += 2;
|
||||||
|
else
|
||||||
|
enclen += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prox->buffersize-prox->bufferpos + (enclen+4) > MAX_PROXY_BUFFER)
|
||||||
|
{
|
||||||
|
Net_TryFlushProxyBuffer(cluster, prox); //try flushing
|
||||||
|
if (prox->buffersize-prox->bufferpos + (enclen+4) > MAX_PROXY_BUFFER) //damn, still too big.
|
||||||
|
{ //they're too slow. hopefully it was just momentary lag
|
||||||
|
if (!prox->flushing)
|
||||||
|
{
|
||||||
|
printf("QTV client is too lagged\n");
|
||||||
|
prox->flushing = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enclen >= 126)
|
||||||
|
{
|
||||||
|
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x81;
|
||||||
|
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 126;
|
||||||
|
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = enclen>>8;
|
||||||
|
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = enclen;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x81;
|
||||||
|
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = enclen;
|
||||||
|
}
|
||||||
|
while(length-->0)
|
||||||
|
{
|
||||||
|
c = *(unsigned char*)buffer;
|
||||||
|
buffer = (char*)buffer+1;
|
||||||
|
if (!c)
|
||||||
|
c |= 0x100; /*will get truncated at the other end*/
|
||||||
|
if (c >= 0x80)
|
||||||
|
{
|
||||||
|
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0xc0 | (c>>6);
|
||||||
|
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = 0x80 | (c & 0x3f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
prox->buffer[prox->buffersize++&(MAX_PROXY_BUFFER-1)] = c;
|
||||||
|
}
|
||||||
|
Net_TryFlushProxyBuffer(cluster, prox); //try flushing in a desperate attempt to reduce bugs in google chrome.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER)
|
if (prox->buffersize-prox->bufferpos + length > MAX_PROXY_BUFFER)
|
||||||
{
|
{
|
||||||
Net_TryFlushProxyBuffer(cluster, prox); //try flushing
|
Net_TryFlushProxyBuffer(cluster, prox); //try flushing
|
||||||
|
@ -622,22 +683,166 @@ void SV_ForwardStream(sv_t *qtv, void *buffer, int length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*wrapper around recv to handle websocket connections*/
|
||||||
|
int NET_WebSocketRecv(SOCKET sock, wsrbuf_t *ws, unsigned char *out, unsigned int outlen, int *clen)
|
||||||
|
{
|
||||||
|
unsigned int mask = 0;
|
||||||
|
unsigned int paylen;
|
||||||
|
int len, i;
|
||||||
|
|
||||||
|
if (clen)
|
||||||
|
*clen = -1;
|
||||||
|
if (!ws->websocket)
|
||||||
|
return recv(sock, out, outlen, 0);
|
||||||
|
|
||||||
|
if (!ws->wsbuflen)
|
||||||
|
{
|
||||||
|
len = recv(sock, ws->wsbuf, 2, 0);
|
||||||
|
if (len > 0)
|
||||||
|
ws->wsbuflen+=2;
|
||||||
|
else
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
if (ws->wsbuflen >= 2)
|
||||||
|
{
|
||||||
|
unsigned short ctrl = (ws->wsbuf[0]<<8) | ws->wsbuf[1];
|
||||||
|
len = 2;
|
||||||
|
if ((ctrl & 0x7f) == 127)
|
||||||
|
len += 8;
|
||||||
|
else if ((ctrl & 0x7f) == 126)
|
||||||
|
len += 2;
|
||||||
|
if (ctrl & 0x80)
|
||||||
|
len += 4;
|
||||||
|
if (ws->wsbuflen < len)
|
||||||
|
{
|
||||||
|
paylen = recv(sock, ws->wsbuf+ws->wsbuflen, len - ws->wsbuflen, 0);
|
||||||
|
if (paylen > 0)
|
||||||
|
ws->wsbuflen += paylen;
|
||||||
|
else
|
||||||
|
return paylen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*headers are complete*/
|
||||||
|
if (ws->wsbuflen >= len)
|
||||||
|
{
|
||||||
|
if ((ctrl & 0x7f) == 127)
|
||||||
|
{
|
||||||
|
// paylen = paylen = ((ws->wsbuf[2]<<56) | (ws->wsbuf[3]<<48) | (ws->wsbuf[4]<<40) | (ws->wsbuf[5]<<32) | (ws->wsbuf[6]<<24) | (ws->wsbuf[7]<<16) | (ws->wsbuf[8]<<8) | (ws->wsbuf[9]<<0);
|
||||||
|
// if (paylen < 65536)
|
||||||
|
return 0; //error
|
||||||
|
}
|
||||||
|
else if ((ctrl & 0x7f) == 126)
|
||||||
|
{
|
||||||
|
paylen = (ws->wsbuf[2]<<8) | ws->wsbuf[3];
|
||||||
|
if (paylen < 126)
|
||||||
|
return 0; //error
|
||||||
|
}
|
||||||
|
else
|
||||||
|
paylen = ctrl & 0x7f;
|
||||||
|
|
||||||
|
if (ctrl & 0x80)
|
||||||
|
{
|
||||||
|
((unsigned char*)&mask)[0] = ws->wsbuf[ws->wsbuflen-4];
|
||||||
|
((unsigned char*)&mask)[1] = ws->wsbuf[ws->wsbuflen-3];
|
||||||
|
((unsigned char*)&mask)[2] = ws->wsbuf[ws->wsbuflen-2];
|
||||||
|
((unsigned char*)&mask)[3] = ws->wsbuf[ws->wsbuflen-1];
|
||||||
|
}
|
||||||
|
if (!(ctrl & 0x8000))
|
||||||
|
return 0; //can't handle fragmented frames
|
||||||
|
|
||||||
|
switch((ctrl>>8) & 0xf)
|
||||||
|
{
|
||||||
|
case 1: /*text frame*/
|
||||||
|
len = 0;
|
||||||
|
while (outlen>len && ws->wspushed < paylen)
|
||||||
|
{
|
||||||
|
unsigned char n;
|
||||||
|
ctrl = recv(sock, &n, 1, 0); //FIXME: not my fastest code...
|
||||||
|
if (ctrl <= 0)
|
||||||
|
return len>0?len:ctrl;
|
||||||
|
|
||||||
|
n ^= ((unsigned char*)&mask)[(ws->wspushed++)&3];
|
||||||
|
|
||||||
|
if (ws->wsbits)
|
||||||
|
{
|
||||||
|
*out++ = ((ws->wsbits&0x1f)<<6) | (n & 0x3f);
|
||||||
|
len++;
|
||||||
|
ws->wsbits = 0;
|
||||||
|
}
|
||||||
|
else if ((n & 0xe0) == 0xc0)
|
||||||
|
{
|
||||||
|
ws->wsbits = n;
|
||||||
|
}
|
||||||
|
else if (n & 0x80)
|
||||||
|
return 0; //error
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*out++ = n;
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ws->wspushed == paylen)
|
||||||
|
{
|
||||||
|
if (ws->wsbits)
|
||||||
|
return 0; //error
|
||||||
|
if (clen)
|
||||||
|
*clen = ws->wspushed;
|
||||||
|
ws->wspushed = 0;
|
||||||
|
ws->wsbuflen = 0;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
case 2: /*binary frame*/
|
||||||
|
if (outlen > paylen - ws->wspushed)
|
||||||
|
outlen = paylen - ws->wspushed;
|
||||||
|
len = recv(sock, out, outlen, 0);
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
for(i = 0; i < len; i++)
|
||||||
|
out[i] ^= ((unsigned char*)&mask)[(ws->wspushed+i)&3];
|
||||||
|
ws->wspushed += len;
|
||||||
|
}
|
||||||
|
if (paylen == ws->wspushed)
|
||||||
|
{
|
||||||
|
/*success! move on to the next*/
|
||||||
|
if (clen)
|
||||||
|
*clen = ws->wspushed;
|
||||||
|
ws->wspushed = 0;
|
||||||
|
ws->wsbuflen = 0;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
case 8: /*close*/
|
||||||
|
case 9: /*ping*/
|
||||||
|
case 10: /*pong*/
|
||||||
|
default:
|
||||||
|
return 0; //unsupported
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
//returns true if the pending proxy should be unlinked
|
//returns true if the pending proxy should be unlinked
|
||||||
//truth does not imply that it should be freed/released, just unlinked.
|
//truth does not imply that it should be freed/released, just unlinked.
|
||||||
qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
||||||
{
|
{
|
||||||
|
//drop: ignored if flushing is still set. clear flushing and set drop on errors.
|
||||||
char tempbuf[512];
|
char tempbuf[512];
|
||||||
unsigned char *s;
|
unsigned char *s;
|
||||||
unsigned char *e;
|
unsigned char *e;
|
||||||
char *colon;
|
char *colon;
|
||||||
float clientversion = 0;
|
float clientversion = 0;
|
||||||
int len;
|
int len;
|
||||||
|
qboolean eoh;
|
||||||
int headersize;
|
int headersize;
|
||||||
qboolean raw;
|
qboolean raw;
|
||||||
sv_t *qtv;
|
sv_t *qtv;
|
||||||
|
|
||||||
if (pend->drop)
|
if (pend->drop && !pend->flushing)
|
||||||
{
|
{
|
||||||
|
printf("pending drop\n");
|
||||||
if (pend->srcfile)
|
if (pend->srcfile)
|
||||||
fclose(pend->srcfile);
|
fclose(pend->srcfile);
|
||||||
closesocket(pend->sock);
|
closesocket(pend->sock);
|
||||||
|
@ -672,6 +877,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
||||||
if (pend->bufferpos == pend->buffersize)
|
if (pend->bufferpos == pend->buffersize)
|
||||||
{
|
{
|
||||||
char buffer[MAX_PROXY_BUFFER/2];
|
char buffer[MAX_PROXY_BUFFER/2];
|
||||||
|
pend->droptime = cluster->curtime + 5*1000;
|
||||||
len = fread(buffer, 1, sizeof(buffer), pend->srcfile);
|
len = fread(buffer, 1, sizeof(buffer), pend->srcfile);
|
||||||
if (!len)
|
if (!len)
|
||||||
{
|
{
|
||||||
|
@ -684,31 +890,32 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
||||||
return false; //don't try reading anything yet
|
return false; //don't try reading anything yet
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pend->bufferpos == pend->buffersize)
|
if (pend->bufferpos != pend->buffersize)
|
||||||
{
|
|
||||||
pend->drop = true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
pend->flushing = false;
|
||||||
else
|
if (pend->drop)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pend->droptime < cluster->curtime)
|
if (pend->droptime < cluster->curtime)
|
||||||
{
|
{
|
||||||
|
printf("pending timeout\n");
|
||||||
pend->drop = true;
|
pend->drop = true;
|
||||||
|
pend->flushing = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = sizeof(pend->inbuffer) - pend->inbuffersize - 1;
|
len = sizeof(pend->inbuffer) - pend->inbuffersize - 1;
|
||||||
len = recv(pend->sock, pend->inbuffer+pend->inbuffersize, len, 0);
|
len = NET_WebSocketRecv(pend->sock, &pend->websocket, pend->inbuffer+pend->inbuffersize, len, NULL);
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
{
|
{
|
||||||
pend->drop = true;
|
pend->drop = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
else if (len > 0)
|
||||||
|
pend->droptime = cluster->curtime + 5*1000;
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,6 +924,39 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
||||||
|
|
||||||
if (pend->inbuffersize >= 4)
|
if (pend->inbuffersize >= 4)
|
||||||
{
|
{
|
||||||
|
if (!strncmp(pend->inbuffer, "qizmo\n", 6))
|
||||||
|
{
|
||||||
|
wsrbuf_t ws = pend->websocket;
|
||||||
|
SOCKET sock;
|
||||||
|
//carries unreliable packets
|
||||||
|
printf("tcpconnect\n");
|
||||||
|
if (pend->srcfile)
|
||||||
|
fclose(pend->srcfile);
|
||||||
|
sock = pend->sock;
|
||||||
|
free(pend);
|
||||||
|
cluster->numproxies--;
|
||||||
|
|
||||||
|
send(sock, "qizmo\n", 6, 0);
|
||||||
|
QW_TCPConnection(cluster, sock, ws);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (pend->websocket.websocket && !strncmp(pend->inbuffer, "\xff\xff\xff\xff", 4))
|
||||||
|
{
|
||||||
|
wsrbuf_t ws = pend->websocket;
|
||||||
|
SOCKET sock;
|
||||||
|
//carries unreliable packets
|
||||||
|
printf("wsconnect\n");
|
||||||
|
if (pend->srcfile)
|
||||||
|
fclose(pend->srcfile);
|
||||||
|
sock = pend->sock;
|
||||||
|
free(pend);
|
||||||
|
cluster->numproxies--;
|
||||||
|
|
||||||
|
QW_TCPConnection(cluster, sock, ws);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (ustrncmp(pend->inbuffer, "QTV\r", 4) && ustrncmp(pend->inbuffer, "QTV\n", 4) && ustrncmp(pend->inbuffer, "GET ", 4) && ustrncmp(pend->inbuffer, "POST ", 5))
|
if (ustrncmp(pend->inbuffer, "QTV\r", 4) && ustrncmp(pend->inbuffer, "QTV\n", 4) && ustrncmp(pend->inbuffer, "GET ", 4) && ustrncmp(pend->inbuffer, "POST ", 5))
|
||||||
{ //I have no idea what the smeg you are.
|
{ //I have no idea what the smeg you are.
|
||||||
pend->drop = true;
|
pend->drop = true;
|
||||||
|
@ -729,15 +969,25 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
||||||
|
|
||||||
//make sure there's a double \n somewhere
|
//make sure there's a double \n somewhere
|
||||||
|
|
||||||
for (s = pend->inbuffer; s<pend->inbuffer+pend->inbuffersize; s++)
|
eoh = false;
|
||||||
|
for (s = pend->inbuffer; s<=pend->inbuffer+pend->inbuffersize; s++)
|
||||||
{
|
{
|
||||||
if (s[0] == '\n' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n')))
|
if (s[0] == '\n' && s[1] == '\n')
|
||||||
|
{
|
||||||
|
s += 2;
|
||||||
|
eoh = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!*s)
|
if (s[0] == '\n' && s[1] == '\r' && s[2] == '\n')
|
||||||
|
{
|
||||||
|
s += 3;
|
||||||
|
eoh = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!eoh)
|
||||||
return false; //don't have enough yet
|
return false; //don't have enough yet
|
||||||
s+=3; //Fixme: this is wrong
|
headersize = s - pend->inbuffer;
|
||||||
headersize = s - pend->inbuffer - 1;
|
|
||||||
|
|
||||||
if (!ustrncmp(pend->inbuffer, "POST ", 5))
|
if (!ustrncmp(pend->inbuffer, "POST ", 5))
|
||||||
{
|
{
|
||||||
|
@ -747,9 +997,12 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
||||||
}
|
}
|
||||||
else if (!ustrncmp(pend->inbuffer, "GET ", 4))
|
else if (!ustrncmp(pend->inbuffer, "GET ", 4))
|
||||||
{
|
{
|
||||||
|
pend->drop = true;
|
||||||
HTTPSV_GetMethod(cluster, pend);
|
HTTPSV_GetMethod(cluster, pend);
|
||||||
pend->flushing = true;
|
pend->flushing = true;
|
||||||
return false;
|
memmove(pend->inbuffer, pend->inbuffer+headersize, pend->inbuffersize-headersize);
|
||||||
|
pend->inbuffersize -= headersize;
|
||||||
|
return SV_ReadPendingProxy(cluster, pend);
|
||||||
}
|
}
|
||||||
|
|
||||||
raw = false;
|
raw = false;
|
||||||
|
|
101
fteqtv/httpsv.c
101
fteqtv/httpsv.c
|
@ -4,7 +4,40 @@
|
||||||
//main reason to use connection close is because we're lazy and don't want to give sizes in advance (yes, we could use chunks..)
|
//main reason to use connection close is because we're lazy and don't want to give sizes in advance (yes, we could use chunks..)
|
||||||
|
|
||||||
|
|
||||||
|
void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen)
|
||||||
|
{
|
||||||
|
static tab[64] =
|
||||||
|
{
|
||||||
|
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
|
||||||
|
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
|
||||||
|
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
|
||||||
|
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
|
||||||
|
};
|
||||||
|
unsigned int usedbits = 0;
|
||||||
|
unsigned int val = 0;
|
||||||
|
outlen--;
|
||||||
|
while(inlen)
|
||||||
|
{
|
||||||
|
while(usedbits < 24 && inlen)
|
||||||
|
{
|
||||||
|
val <<= 8;
|
||||||
|
val |= (*in++);
|
||||||
|
inlen--;
|
||||||
|
usedbits += 8;
|
||||||
|
}
|
||||||
|
if (outlen < 4)
|
||||||
|
return;
|
||||||
|
val <<= 24 - usedbits;
|
||||||
|
|
||||||
|
*out++ = (usedbits > 0)?tab[(val>>18)&0x3f]:'=';
|
||||||
|
*out++ = (usedbits > 6)?tab[(val>>12)&0x3f]:'=';
|
||||||
|
*out++ = (usedbits > 12)?tab[(val>>6)&0x3f]:'=';
|
||||||
|
*out++ = (usedbits > 18)?tab[(val>>0)&0x3f]:'=';
|
||||||
|
val=0;
|
||||||
|
usedbits = 0;
|
||||||
|
}
|
||||||
|
*out = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const char qfont_table[256] = {
|
static const char qfont_table[256] = {
|
||||||
'\0', '#', '#', '#', '#', '.', '#', '#',
|
'\0', '#', '#', '#', '#', '.', '#', '#',
|
||||||
|
@ -110,19 +143,20 @@ static void HTTPSV_SendHTTPHeader(cluster_t *cluster, oproxy_t *dest, char *erro
|
||||||
|
|
||||||
if (nocache)
|
if (nocache)
|
||||||
{
|
{
|
||||||
s = "HTTP/1.1 %s OK\n"
|
s = "HTTP/1.1 %s OK\r\n"
|
||||||
"Content-Type: %s\n"
|
"Content-Type: %s\r\n"
|
||||||
"Cache-Control: no-cache, must-revalidate\n"
|
"Cache-Control: no-cache, must-revalidate\r\n"
|
||||||
"Expires: Mon, 26 Jul 1997 05:00:00 GMT\n"
|
"Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
|
||||||
"Connection: close\n"
|
"Connection: close\r\n"
|
||||||
"\n";
|
"\r\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
s = "HTTP/1.1 %s OK\n"
|
s = "HTTP/1.1 %s OK\r\n"
|
||||||
"Content-Type: %s\n"
|
"Cache-Control: public, max-age=3600\r\n"
|
||||||
"Connection: close\n"
|
"Content-Type: %s\r\n"
|
||||||
"\n";
|
"Connection: close\r\n"
|
||||||
|
"\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(buffer, sizeof(buffer), s, error_code, content_type);
|
snprintf(buffer, sizeof(buffer), s, error_code, content_type);
|
||||||
|
@ -635,7 +669,7 @@ static void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streami
|
||||||
}
|
}
|
||||||
if (o != result)
|
if (o != result)
|
||||||
{
|
{
|
||||||
strcpy(result, o);
|
strlcpy(result, o, sizeof(result));
|
||||||
o = result;
|
o = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1077,6 +1111,9 @@ void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata)
|
||||||
|
|
||||||
void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
|
void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
|
||||||
{
|
{
|
||||||
|
char connection[64];
|
||||||
|
char upgrade[64];
|
||||||
|
|
||||||
char *s;
|
char *s;
|
||||||
char *uri, *uriend;
|
char *uri, *uriend;
|
||||||
char *args;
|
char *args;
|
||||||
|
@ -1090,6 +1127,48 @@ void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
|
||||||
uriend = s;
|
uriend = s;
|
||||||
urilen = uriend - uri;
|
urilen = uriend - uri;
|
||||||
|
|
||||||
|
if (!pend->websocket.websocket && HTTPSV_GetHeaderField((char*)pend->inbuffer, "Connection", connection, sizeof(connection)) && !stricmp(connection, "Upgrade"))
|
||||||
|
{
|
||||||
|
if (HTTPSV_GetHeaderField((char*)pend->inbuffer, "Upgrade", upgrade, sizeof(upgrade)) && !stricmp(upgrade, "websocket"))
|
||||||
|
{
|
||||||
|
char ver[64];
|
||||||
|
char key[64];
|
||||||
|
HTTPSV_GetHeaderField((char*)pend->inbuffer, "Sec-WebSocket-Key", key, sizeof(key));
|
||||||
|
|
||||||
|
if (HTTPSV_GetHeaderField((char*)pend->inbuffer, "Sec-WebSocket-Version", ver, sizeof(ver)) && atoi(ver) != 13)
|
||||||
|
{
|
||||||
|
s = "HTTP/1.1 426 Upgrade Required\r\n"
|
||||||
|
"Sec-WebSocket-Version: 13\r\n"
|
||||||
|
"\r\n";
|
||||||
|
Net_ProxySend(cluster, pend, s, strlen(s));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char acceptkey[20*2];
|
||||||
|
unsigned char sha1digest[20];
|
||||||
|
char padkey[512];
|
||||||
|
snprintf(padkey, sizeof(padkey), "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key);
|
||||||
|
tobase64(acceptkey, sizeof(acceptkey), sha1digest, SHA1(sha1digest, sizeof(sha1digest), padkey));
|
||||||
|
|
||||||
|
snprintf(padkey, sizeof(padkey),
|
||||||
|
"HTTP/1.1 101 Switching Protocols\r\n"
|
||||||
|
"Upgrade: websocket\r\n"
|
||||||
|
"Connection: Upgrade\r\n"
|
||||||
|
"Sec-WebSocket-Accept: %s\r\n"
|
||||||
|
// "Sec-WebSocket-Protocol: FTEQTVWebSocket\r\n"
|
||||||
|
"\r\n", acceptkey);
|
||||||
|
//send the websocket handshake response.
|
||||||
|
Net_ProxySend(cluster, pend, padkey, strlen(padkey));
|
||||||
|
pend->websocket.websocket = true;
|
||||||
|
|
||||||
|
printf("websocket upgrade\n");
|
||||||
|
}
|
||||||
|
pend->drop = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (urimatch(uri, "/plugin.html", urilen))
|
if (urimatch(uri, "/plugin.html", urilen))
|
||||||
{
|
{
|
||||||
HTTPSV_GeneratePlugin(cluster, pend);
|
HTTPSV_GeneratePlugin(cluster, pend);
|
||||||
|
|
185
fteqtv/netchan.c
185
fteqtv/netchan.c
|
@ -21,13 +21,102 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
#include "qtv.h"
|
#include "qtv.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef IPV6_V6ONLY
|
||||||
|
#define IPV6_V6ONLY 27
|
||||||
|
#endif
|
||||||
|
|
||||||
#define curtime Sys_Milliseconds()
|
#define curtime Sys_Milliseconds()
|
||||||
|
|
||||||
|
|
||||||
|
void NET_InitUDPSocket(cluster_t *cluster, int port, qboolean ipv6)
|
||||||
|
{
|
||||||
|
int sock;
|
||||||
|
|
||||||
|
int pf;
|
||||||
|
struct sockaddr *address;
|
||||||
|
struct sockaddr_in address4;
|
||||||
|
struct sockaddr_in6 address6;
|
||||||
|
int addrlen;
|
||||||
|
|
||||||
|
unsigned long nonblocking = true;
|
||||||
|
unsigned long v6only = false;
|
||||||
|
|
||||||
|
#pragma message("fixme")
|
||||||
|
if (ipv6)
|
||||||
|
{
|
||||||
|
pf = PF_INET6;
|
||||||
|
memset(&address6, 0, sizeof(address6));
|
||||||
|
address6.sin6_family = AF_INET6;
|
||||||
|
address6.sin6_port = htons((u_short)port);
|
||||||
|
address = (struct sockaddr*)&address6;
|
||||||
|
addrlen = sizeof(address6);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pf = PF_INET;
|
||||||
|
address4.sin_family = AF_INET;
|
||||||
|
address4.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
address4.sin_port = htons((u_short)port);
|
||||||
|
address = (struct sockaddr*)&address4;
|
||||||
|
addrlen = sizeof(address4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ipv6 && !v6only && cluster->qwdsocket[1] != INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
int sz = sizeof(v6only);
|
||||||
|
if (getsockopt(cluster->qwdsocket[1], IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, &sz) == 0 && !v6only)
|
||||||
|
port = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!port)
|
||||||
|
{
|
||||||
|
if (cluster->qwdsocket[ipv6] != INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
closesocket(cluster->qwdsocket[ipv6]);
|
||||||
|
cluster->qwdsocket[ipv6] = INVALID_SOCKET;
|
||||||
|
Sys_Printf(cluster, "closed udp%i port\n", ipv6?6:4);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sock = socket (pf, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctlsocket (sock, FIONBIO, &nonblocking) == -1)
|
||||||
|
{
|
||||||
|
closesocket(sock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pf == AF_INET6)
|
||||||
|
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only)) == -1)
|
||||||
|
v6only = true;
|
||||||
|
|
||||||
|
if (bind (sock, (void *)address, addrlen) == -1)
|
||||||
|
{
|
||||||
|
printf("socket bind error %i (%s)\n", qerrno, strerror(qerrno));
|
||||||
|
closesocket(sock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cluster->qwdsocket[ipv6] != INVALID_SOCKET)
|
||||||
|
{
|
||||||
|
closesocket(cluster->qwdsocket[ipv6]);
|
||||||
|
Sys_Printf(cluster, "closed udp%i port\n", ipv6?6:4);
|
||||||
|
}
|
||||||
|
cluster->qwdsocket[ipv6] = sock;
|
||||||
|
if (v6only)
|
||||||
|
Sys_Printf(cluster, "opened udp%i port %i\n", ipv6?6:4, port);
|
||||||
|
else
|
||||||
|
Sys_Printf(cluster, "opened udp port %i\n", port);
|
||||||
|
}
|
||||||
|
|
||||||
SOCKET NET_ChooseSocket(SOCKET sock[2], netadr_t *adr)
|
SOCKET NET_ChooseSocket(SOCKET sock[2], netadr_t *adr)
|
||||||
{
|
{
|
||||||
#ifdef AF_INET6
|
#ifdef AF_INET6
|
||||||
if (((struct sockaddr *)adr)->sa_family == AF_INET6)
|
if (((struct sockaddr *)adr->sockaddr)->sa_family == AF_INET6)
|
||||||
return sock[1];
|
return sock[1];
|
||||||
#endif
|
#endif
|
||||||
return sock[0];
|
return sock[0];
|
||||||
|
@ -36,8 +125,94 @@ SOCKET NET_ChooseSocket(SOCKET sock[2], netadr_t *adr)
|
||||||
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr)
|
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
int alen;
|
||||||
|
|
||||||
ret = sendto(sock, data, length, 0, (struct sockaddr *)&adr, sizeof(struct sockaddr_in));
|
#ifdef AF_INET6
|
||||||
|
if (((struct sockaddr *)&adr.sockaddr)->sa_family == AF_INET6)
|
||||||
|
alen = sizeof(struct sockaddr_in6);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
alen = sizeof(struct sockaddr_in);
|
||||||
|
|
||||||
|
if (adr.tcpcon)
|
||||||
|
{
|
||||||
|
tcpconnect_t *dest;
|
||||||
|
for (dest = cluster->tcpconnects; dest; dest = dest->next)
|
||||||
|
{
|
||||||
|
if (dest == adr.tcpcon)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest)
|
||||||
|
{
|
||||||
|
int l;
|
||||||
|
|
||||||
|
if (dest->websocket.websocket)
|
||||||
|
{
|
||||||
|
int enclen = 0, c;
|
||||||
|
for (c = 0; c < length; c++)
|
||||||
|
{
|
||||||
|
if (((unsigned char*)data)[c] == 0 || ((unsigned char*)data)[c] >= 0x80)
|
||||||
|
enclen += 2;
|
||||||
|
else
|
||||||
|
enclen += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest->outbuffersize + 4+enclen < sizeof(dest->outbuffer))
|
||||||
|
{
|
||||||
|
if (enclen >= 126)
|
||||||
|
{
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = 0x81;
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = 126;
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = enclen>>8;
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = enclen;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = 0x81;
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = enclen;
|
||||||
|
}
|
||||||
|
while(length-->0)
|
||||||
|
{
|
||||||
|
c = *(unsigned char*)data;
|
||||||
|
data = (char*)data+1;
|
||||||
|
if (!c)
|
||||||
|
c |= 0x100; /*will get truncated at the other end*/
|
||||||
|
if (c >= 0x80)
|
||||||
|
{
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = 0xc0 | (c>>6);
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = 0x80 | (c & 0x3f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dest->outbuffersize + length < sizeof(dest->outbuffer))
|
||||||
|
{
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = length>>8;
|
||||||
|
dest->outbuffer[dest->outbuffersize++] = length&0xff;
|
||||||
|
memcpy(dest->outbuffer+dest->outbuffersize, data, length);
|
||||||
|
dest->outbuffersize += length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest->outbuffersize)
|
||||||
|
{
|
||||||
|
l = send(dest->sock, dest->outbuffer, dest->outbuffersize, 0);
|
||||||
|
if (l > 0)
|
||||||
|
{
|
||||||
|
memmove(dest->outbuffer, dest->outbuffer+l, dest->outbuffersize-l);
|
||||||
|
dest->outbuffersize-=l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sendto(sock, data, length, 0, (struct sockaddr *)&adr.sockaddr, alen);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
int er = qerrno;
|
int er = qerrno;
|
||||||
|
@ -56,7 +231,7 @@ int Netchan_IsLocal (netadr_t adr)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
bytes = (unsigned char *)&((struct sockaddr_in *)&adr)->sin_addr;
|
bytes = (unsigned char *)&((struct sockaddr_in *)&adr)->sin_addr;
|
||||||
if (bytes[0] == 127 &&
|
if (bytes[0] == 127 && /*actualy, it should be only the first octet, but hey*/
|
||||||
bytes[1] == 0 &&
|
bytes[1] == 0 &&
|
||||||
bytes[2] == 0 &&
|
bytes[2] == 0 &&
|
||||||
bytes[3] == 1)
|
bytes[3] == 1)
|
||||||
|
@ -177,7 +352,7 @@ Netchan_OutOfBandPrint
|
||||||
Sends a text message in an out-of-band datagram
|
Sends a text message in an out-of-band datagram
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock[], netadr_t adr, char *format, ...)
|
void Netchan_OutOfBandPrint (cluster_t *cluster, netadr_t adr, char *format, ...)
|
||||||
{
|
{
|
||||||
va_list argptr;
|
va_list argptr;
|
||||||
char string[8192];
|
char string[8192];
|
||||||
|
@ -191,7 +366,7 @@ void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock[], netadr_t adr, ch
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
va_end (argptr);
|
va_end (argptr);
|
||||||
|
|
||||||
Netchan_OutOfBand (cluster, NET_ChooseSocket(sock, &adr), adr, strlen(string), (unsigned char *)string);
|
Netchan_OutOfBand (cluster, NET_ChooseSocket(cluster->qwdsocket, &adr), adr, strlen(string), (unsigned char *)string);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -398,6 +398,21 @@ static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
||||||
|
|
||||||
if (level == 3)
|
if (level == 3)
|
||||||
{
|
{
|
||||||
|
//FIXME: that number shouldn't be hard-coded
|
||||||
|
if (!strncmp(text, "#0:qtv_say:#", 12) || !strncmp(text, "#0:qtv_say_game:#", 17) || !strncmp(text, "#0:qtv_say_team_game:#", 22))
|
||||||
|
{
|
||||||
|
char *colon;
|
||||||
|
colon = strchr(text, ':');
|
||||||
|
colon = strchr(colon+1, ':');
|
||||||
|
colon = strchr(colon+1, ':');
|
||||||
|
if (colon)
|
||||||
|
{
|
||||||
|
//de-fuck qqshka's extra gibberish.
|
||||||
|
snprintf(buffer, sizeof(buffer), "%c%c[QTV]%s\n", svc_print, 3, colon+1);
|
||||||
|
Multicast(tv, buffer, strlen(buffer)+1, to, mask, QW|CONNECTING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
strcpy(buffer+2, text);
|
strcpy(buffer+2, text);
|
||||||
buffer[1] = 1;
|
buffer[1] = 1;
|
||||||
}
|
}
|
||||||
|
@ -416,7 +431,7 @@ static void ParsePrint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
Multicast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW|CONNECTING);
|
Multicast(tv, (char*)m->data+m->startpos, m->readpos - m->startpos, to, mask, QW|CONNECTING);
|
||||||
// Multicast(tv, buffer, strlen(buffer), to, mask, NQ);
|
// Multicast(tv, buffer, strlen(buffer)+1, to, mask, NQ);
|
||||||
}
|
}
|
||||||
static void ParseCenterprint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
static void ParseCenterprint(sv_t *tv, netmsg_t *m, int to, unsigned int mask)
|
||||||
{
|
{
|
||||||
|
@ -562,6 +577,7 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)
|
||||||
for (i = 0; i < MAX_CLIENTS; i++)
|
for (i = 0; i < MAX_CLIENTS; i++)
|
||||||
{ //hide players
|
{ //hide players
|
||||||
//they'll be sent after this packet.
|
//they'll be sent after this packet.
|
||||||
|
tv->map.players[i].oldactive = tv->map.players[i].active;
|
||||||
tv->map.players[i].active = false;
|
tv->map.players[i].active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -576,7 +592,6 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)
|
||||||
|
|
||||||
if (tv->usequakeworldprotocols)
|
if (tv->usequakeworldprotocols)
|
||||||
{
|
{
|
||||||
tv->map.players[num].old = tv->map.players[num].current;
|
|
||||||
flags = (unsigned short)ReadShort (m);
|
flags = (unsigned short)ReadShort (m);
|
||||||
|
|
||||||
tv->map.players[num].current.origin[0] = ReadShort (m);
|
tv->map.players[num].current.origin[0] = ReadShort (m);
|
||||||
|
|
41
fteqtv/qtv.h
41
fteqtv/qtv.h
|
@ -224,7 +224,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
#define ustrncmp(s1,s2,l) strncmp((char*)(s1),(char*)(s2),l)
|
#define ustrncmp(s1,s2,l) strncmp((char*)(s1),(char*)(s2),l)
|
||||||
|
|
||||||
|
|
||||||
|
size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||||
|
|
||||||
#define VERSION "0.01" //this will be added to the serverinfo
|
#define VERSION "0.01" //this will be added to the serverinfo
|
||||||
|
|
||||||
|
@ -249,8 +249,10 @@ typedef int qboolean;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct{
|
typedef struct
|
||||||
char blob[64];
|
{
|
||||||
|
void *tcpcon; /*value not indirected, only compared*/
|
||||||
|
char sockaddr[64];
|
||||||
} netadr_t;
|
} netadr_t;
|
||||||
|
|
||||||
|
|
||||||
|
@ -351,6 +353,7 @@ typedef struct {
|
||||||
int leafcount;
|
int leafcount;
|
||||||
unsigned short leafs[MAX_ENTITY_LEAFS];
|
unsigned short leafs[MAX_ENTITY_LEAFS];
|
||||||
|
|
||||||
|
qboolean oldactive:1;
|
||||||
qboolean active:1;
|
qboolean active:1;
|
||||||
qboolean gibbed:1;
|
qboolean gibbed:1;
|
||||||
qboolean dead:1;
|
qboolean dead:1;
|
||||||
|
@ -457,6 +460,15 @@ typedef struct viewer_s {
|
||||||
int firstconnect;
|
int firstconnect;
|
||||||
} viewer_t;
|
} viewer_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
qboolean websocket; //true if we need to use special handling
|
||||||
|
unsigned char wsbuf[16];
|
||||||
|
int wsbuflen;
|
||||||
|
int wspushed;
|
||||||
|
int wsbits;
|
||||||
|
} wsrbuf_t;
|
||||||
|
|
||||||
//'other proxy', these are mvd stream clients.
|
//'other proxy', these are mvd stream clients.
|
||||||
typedef struct oproxy_s {
|
typedef struct oproxy_s {
|
||||||
int authkey;
|
int authkey;
|
||||||
|
@ -471,6 +483,7 @@ typedef struct oproxy_s {
|
||||||
FILE *srcfile; //buffer is padded with data from this file when its empty
|
FILE *srcfile; //buffer is padded with data from this file when its empty
|
||||||
FILE *file; //recording a demo (written to)
|
FILE *file; //recording a demo (written to)
|
||||||
SOCKET sock; //playing to a proxy
|
SOCKET sock; //playing to a proxy
|
||||||
|
wsrbuf_t websocket;
|
||||||
|
|
||||||
unsigned char inbuffer[MAX_PROXY_INBUFFER];
|
unsigned char inbuffer[MAX_PROXY_INBUFFER];
|
||||||
unsigned int inbuffersize; //amount of data available.
|
unsigned int inbuffersize; //amount of data available.
|
||||||
|
@ -481,6 +494,20 @@ typedef struct oproxy_s {
|
||||||
struct oproxy_s *next;
|
struct oproxy_s *next;
|
||||||
} oproxy_t;
|
} oproxy_t;
|
||||||
|
|
||||||
|
typedef struct tcpconnect_s
|
||||||
|
{
|
||||||
|
struct tcpconnect_s *next;
|
||||||
|
SOCKET sock;
|
||||||
|
wsrbuf_t websocket;
|
||||||
|
netadr_t peeraddr;
|
||||||
|
|
||||||
|
unsigned char inbuffer[MAX_PROXY_INBUFFER];
|
||||||
|
unsigned int inbuffersize; //amount of data available.
|
||||||
|
|
||||||
|
unsigned char outbuffer[MAX_PROXY_INBUFFER];
|
||||||
|
unsigned int outbuffersize; //amount of data available.
|
||||||
|
} tcpconnect_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
short origin[3];
|
short origin[3];
|
||||||
unsigned char soundindex;
|
unsigned char soundindex;
|
||||||
|
@ -679,6 +706,7 @@ typedef struct {
|
||||||
struct cluster_s {
|
struct cluster_s {
|
||||||
SOCKET qwdsocket[2]; //udp + quakeworld protocols
|
SOCKET qwdsocket[2]; //udp + quakeworld protocols
|
||||||
SOCKET tcpsocket[2]; //tcp listening socket (for mvd and listings and stuff)
|
SOCKET tcpsocket[2]; //tcp listening socket (for mvd and listings and stuff)
|
||||||
|
tcpconnect_t *tcpconnects; //'tcpconnect' qizmo-compatible quakeworld-over-tcp connection
|
||||||
|
|
||||||
char commandinput[512];
|
char commandinput[512];
|
||||||
int inputlength;
|
int inputlength;
|
||||||
|
@ -713,6 +741,8 @@ struct cluster_s {
|
||||||
qboolean nobsp;
|
qboolean nobsp;
|
||||||
qboolean allownqclients; //nq clients require no challenge
|
qboolean allownqclients; //nq clients require no challenge
|
||||||
qboolean nouserconnects; //prohibit users from connecting to new streams.
|
qboolean nouserconnects; //prohibit users from connecting to new streams.
|
||||||
|
int anticheattime; //intial connection buffer delay (set high to block specing enemies)
|
||||||
|
int tooslowdelay; //if stream ran out of data, stop parsing for this long
|
||||||
|
|
||||||
int maxviewers;
|
int maxviewers;
|
||||||
|
|
||||||
|
@ -801,7 +831,6 @@ void Broadcast(cluster_t *cluster, void *buffer, int length, int suitablefor);
|
||||||
void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask);
|
void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask);
|
||||||
void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *spectatorflag);
|
void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *spectatorflag);
|
||||||
void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);
|
void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount);
|
||||||
SOCKET QW_InitUDPSocket(int port, qboolean ipv6);
|
|
||||||
void QW_UpdateUDPStuff(cluster_t *qtv);
|
void QW_UpdateUDPStuff(cluster_t *qtv);
|
||||||
unsigned int Sys_Milliseconds(void);
|
unsigned int Sys_Milliseconds(void);
|
||||||
void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg);
|
void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg);
|
||||||
|
@ -820,8 +849,9 @@ void QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcomman
|
||||||
void PM_PlayerMove (pmove_t *pmove);
|
void PM_PlayerMove (pmove_t *pmove);
|
||||||
|
|
||||||
void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient);
|
void Netchan_Setup (SOCKET sock, netchan_t *chan, netadr_t adr, int qport, qboolean isclient);
|
||||||
void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock[], netadr_t adr, char *format, ...) PRINTFWARNING(4);
|
void Netchan_OutOfBandPrint (cluster_t *cluster, netadr_t adr, char *format, ...) PRINTFWARNING(4);
|
||||||
int Netchan_IsLocal (netadr_t adr);
|
int Netchan_IsLocal (netadr_t adr);
|
||||||
|
void NET_InitUDPSocket(cluster_t *cluster, int port, qboolean ipv6);
|
||||||
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr);
|
void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, netadr_t adr);
|
||||||
SOCKET NET_ChooseSocket(SOCKET sock[], netadr_t *adr);
|
SOCKET NET_ChooseSocket(SOCKET sock[], netadr_t *adr);
|
||||||
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2);
|
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2);
|
||||||
|
@ -830,6 +860,7 @@ qboolean NQNetchan_Process(cluster_t *cluster, netchan_t *chan, netmsg_t *msg);
|
||||||
void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const void *data);
|
void Netchan_Transmit (cluster_t *cluster, netchan_t *chan, int length, const void *data);
|
||||||
void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, void *data);
|
void Netchan_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, void *data);
|
||||||
qboolean Netchan_CanPacket (netchan_t *chan);
|
qboolean Netchan_CanPacket (netchan_t *chan);
|
||||||
|
int NET_WebSocketRecv(SOCKET sock, wsrbuf_t *ws, unsigned char *out, unsigned int outlen, int *wslen);
|
||||||
|
|
||||||
int SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg);
|
int SendList(sv_t *qtv, int first, const filename_t *list, int svc, netmsg_t *msg);
|
||||||
int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum, int thisplayer);
|
int Prespawn(sv_t *qtv, int curmsgsize, netmsg_t *msg, int bufnum, int thisplayer);
|
||||||
|
|
461
fteqtv/qw.c
461
fteqtv/qw.c
|
@ -154,59 +154,7 @@ void WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SOCKET QW_InitUDPSocket(int port, qboolean ipv6)
|
|
||||||
{
|
|
||||||
int sock;
|
|
||||||
|
|
||||||
int pf;
|
|
||||||
struct sockaddr *address;
|
|
||||||
struct sockaddr_in address4;
|
|
||||||
struct sockaddr_in6 address6;
|
|
||||||
int addrlen;
|
|
||||||
|
|
||||||
unsigned long nonblocking = true;
|
|
||||||
|
|
||||||
#pragma message("fixme")
|
|
||||||
if (ipv6)
|
|
||||||
{
|
|
||||||
pf = PF_INET6;
|
|
||||||
memset(&address6, 0, sizeof(address6));
|
|
||||||
address6.sin6_family = AF_INET6;
|
|
||||||
address6.sin6_port = htons((u_short)port);
|
|
||||||
address = (struct sockaddr*)&address6;
|
|
||||||
addrlen = sizeof(address6);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pf = PF_INET;
|
|
||||||
address4.sin_family = AF_INET;
|
|
||||||
address4.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
address4.sin_port = htons((u_short)port);
|
|
||||||
address = (struct sockaddr*)&address4;
|
|
||||||
addrlen = sizeof(address4);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if ((sock = socket (pf, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
|
|
||||||
{
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctlsocket (sock, FIONBIO, &nonblocking) == -1)
|
|
||||||
{
|
|
||||||
closesocket(sock);
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( bind (sock, (void *)address, addrlen) == -1)
|
|
||||||
{
|
|
||||||
printf("socket bind error %i (%s)\n", qerrno, strerror(qerrno));
|
|
||||||
closesocket(sock);
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
return sock;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *viewer)
|
void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *viewer)
|
||||||
{
|
{
|
||||||
|
@ -934,7 +882,7 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
|
||||||
|
|
||||||
if (!ChallengePasses(addr, atoi(challenge)))
|
if (!ChallengePasses(addr, atoi(challenge)))
|
||||||
{
|
{
|
||||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "n" "Bad challenge");
|
Netchan_OutOfBandPrint(cluster, *addr, "n" "Bad challenge");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -942,7 +890,7 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
|
||||||
viewer = malloc(sizeof(viewer_t));
|
viewer = malloc(sizeof(viewer_t));
|
||||||
if (!viewer)
|
if (!viewer)
|
||||||
{
|
{
|
||||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "n" "Out of memory");
|
Netchan_OutOfBandPrint(cluster, *addr, "n" "Out of memory");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memset(viewer, 0, sizeof(viewer_t));
|
memset(viewer, 0, sizeof(viewer_t));
|
||||||
|
@ -976,7 +924,7 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
|
||||||
strlcpy(viewer->userinfo, infostring, sizeof(viewer->userinfo));
|
strlcpy(viewer->userinfo, infostring, sizeof(viewer->userinfo));
|
||||||
ParseUserInfo(cluster, viewer);
|
ParseUserInfo(cluster, viewer);
|
||||||
|
|
||||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "j");
|
Netchan_OutOfBandPrint(cluster, *addr, "j");
|
||||||
|
|
||||||
NewClient(cluster, viewer);
|
NewClient(cluster, viewer);
|
||||||
}
|
}
|
||||||
|
@ -1056,7 +1004,7 @@ void QTV_Rcon(cluster_t *cluster, char *message, netadr_t *from)
|
||||||
|
|
||||||
if (!*cluster->adminpassword)
|
if (!*cluster->adminpassword)
|
||||||
{
|
{
|
||||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n" "Bad rcon_password.\n");
|
Netchan_OutOfBandPrint(cluster, *from, "n" "Bad rcon_password.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1067,11 +1015,11 @@ void QTV_Rcon(cluster_t *cluster, char *message, netadr_t *from)
|
||||||
passlen = command-message;
|
passlen = command-message;
|
||||||
if (passlen != strlen(cluster->adminpassword) || strncmp(message, cluster->adminpassword, passlen))
|
if (passlen != strlen(cluster->adminpassword) || strncmp(message, cluster->adminpassword, passlen))
|
||||||
{
|
{
|
||||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n" "Bad rcon_password.\n");
|
Netchan_OutOfBandPrint(cluster, *from, "n" "Bad rcon_password.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n%s", Rcon_Command(cluster, NULL, command, buffer, sizeof(buffer), false));
|
Netchan_OutOfBandPrint(cluster, *from, "n%s", Rcon_Command(cluster, NULL, command, buffer, sizeof(buffer), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
void QTV_Status(cluster_t *cluster, netadr_t *from)
|
void QTV_Status(cluster_t *cluster, netadr_t *from)
|
||||||
|
@ -1287,13 +1235,13 @@ void ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m)
|
||||||
if (!strncmp(buffer, "getchallenge", 12))
|
if (!strncmp(buffer, "getchallenge", 12))
|
||||||
{
|
{
|
||||||
i = NewChallenge(from);
|
i = NewChallenge(from);
|
||||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "c%i", i);
|
Netchan_OutOfBandPrint(cluster, *from, "c%i", i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!strncmp(buffer, "connect 28 ", 11))
|
if (!strncmp(buffer, "connect 28 ", 11))
|
||||||
{
|
{
|
||||||
if (cluster->numviewers >= cluster->maxviewers && cluster->maxviewers)
|
if (cluster->numviewers >= cluster->maxviewers && cluster->maxviewers)
|
||||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n" "Sorry, proxy is full.\n");
|
Netchan_OutOfBandPrint(cluster, *from, "n" "Sorry, proxy is full.\n");
|
||||||
else
|
else
|
||||||
NewQWClient(cluster, from, buffer);
|
NewQWClient(cluster, from, buffer);
|
||||||
return;
|
return;
|
||||||
|
@ -2032,7 +1980,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
|
||||||
|
|
||||||
if (tv)
|
if (tv)
|
||||||
{
|
{
|
||||||
if (tv->physicstime != v->settime && tv->cluster->chokeonnotupdated)
|
if (tv->physicstime != v->settime)// && tv->cluster->chokeonnotupdated)
|
||||||
{
|
{
|
||||||
WriteByte(msg, svc_updatestatlong);
|
WriteByte(msg, svc_updatestatlong);
|
||||||
WriteByte(msg, STAT_TIME);
|
WriteByte(msg, STAT_TIME);
|
||||||
|
@ -2117,7 +2065,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
|
||||||
WriteByte(msg, i);
|
WriteByte(msg, i);
|
||||||
WriteShort(msg, flags);
|
WriteShort(msg, flags);
|
||||||
|
|
||||||
if (!tv->map.players[i].active ||
|
if (!tv->map.players[i].active || !tv->map.players[i].oldactive ||
|
||||||
(tv->map.players[i].current.origin[0] - tv->map.players[i].old.origin[0])*(tv->map.players[i].current.origin[0] - tv->map.players[i].old.origin[0]) > snapdist ||
|
(tv->map.players[i].current.origin[0] - tv->map.players[i].old.origin[0])*(tv->map.players[i].current.origin[0] - tv->map.players[i].old.origin[0]) > snapdist ||
|
||||||
(tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1])*(tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1]) > snapdist ||
|
(tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1])*(tv->map.players[i].current.origin[1] - tv->map.players[i].old.origin[1]) > snapdist ||
|
||||||
(tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2])*(tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2]) > snapdist)
|
(tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2])*(tv->map.players[i].current.origin[2] - tv->map.players[i].old.origin[2]) > snapdist)
|
||||||
|
@ -2145,7 +2093,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
|
||||||
}
|
}
|
||||||
if (flags & PF_COMMAND)
|
if (flags & PF_COMMAND)
|
||||||
{
|
{
|
||||||
if (!tv->map.players[i].active)
|
if (!tv->map.players[i].active || !tv->map.players[i].oldactive)
|
||||||
{
|
{
|
||||||
to.angles[0] = tv->map.players[i].current.angles[0];
|
to.angles[0] = tv->map.players[i].current.angles[0];
|
||||||
to.angles[1] = tv->map.players[i].current.angles[1];
|
to.angles[1] = tv->map.players[i].current.angles[1];
|
||||||
|
@ -2465,7 +2413,7 @@ void QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcomman
|
||||||
QW_PrintfToViewer(v, "Website: http://www.fteqw.com/\n"
|
QW_PrintfToViewer(v, "Website: http://www.fteqw.com/\n"
|
||||||
"Commands:\n"
|
"Commands:\n"
|
||||||
".bind\n"
|
".bind\n"
|
||||||
" Bind your keys to match Qizmo.\n"
|
" Bind your keys to drive the menu.\n"
|
||||||
".clients\n"
|
".clients\n"
|
||||||
" Lists the users connected to this\n"
|
" Lists the users connected to this\n"
|
||||||
" proxy.\n"
|
" proxy.\n"
|
||||||
|
@ -4176,19 +4124,204 @@ void SendViewerPackets(cluster_t *cluster, viewer_t *v)
|
||||||
// else
|
// else
|
||||||
// printf("maynotsend (%i, %i)\n", cluster->curtime, v->nextpacket);
|
// printf("maynotsend (%i, %i)\n", cluster->curtime, v->nextpacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from)
|
||||||
|
{
|
||||||
|
char tempbuffer[256];
|
||||||
|
int fromsize = sizeof(from);
|
||||||
|
int qport;
|
||||||
|
|
||||||
|
viewer_t *v;
|
||||||
|
sv_t *useserver;
|
||||||
|
|
||||||
|
if (*(int*)m->data == -1)
|
||||||
|
{ //connectionless message
|
||||||
|
ConnectionlessPacket(cluster, &from, m);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m->cursize < 10) //otherwise it's a runt or bad.
|
||||||
|
{
|
||||||
|
qport = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//read the qport
|
||||||
|
ReadLong(m);
|
||||||
|
ReadLong(m);
|
||||||
|
qport = ReadShort(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (v = cluster->viewers; v; v = v->next)
|
||||||
|
{
|
||||||
|
if (v->netchan.isnqprotocol)
|
||||||
|
{
|
||||||
|
if (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 0))
|
||||||
|
{
|
||||||
|
if (NQNetchan_Process(cluster, &v->netchan, m))
|
||||||
|
{
|
||||||
|
useserver = v->server;
|
||||||
|
if (useserver && useserver->parsingconnectiondata)
|
||||||
|
useserver = NULL;
|
||||||
|
|
||||||
|
v->timeout = cluster->curtime + 15*1000;
|
||||||
|
|
||||||
|
ParseNQC(cluster, useserver, v, m);
|
||||||
|
|
||||||
|
if (v->server && v->server->controller == v)
|
||||||
|
{
|
||||||
|
QTV_Run(v->server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Net_CompareAddress(&v->netchan.remote_address, &from, v->netchan.qport, qport))
|
||||||
|
{
|
||||||
|
if (v->server && v->server->controller == v && v->maysend)
|
||||||
|
SendViewerPackets(cluster, v); //do this before we read the new sequences
|
||||||
|
|
||||||
|
if (Netchan_Process(&v->netchan, m))
|
||||||
|
{
|
||||||
|
useserver = v->server;
|
||||||
|
if (useserver && useserver->parsingconnectiondata && useserver->controller != v)
|
||||||
|
useserver = NULL;
|
||||||
|
|
||||||
|
v->timeout = cluster->curtime + 15*1000;
|
||||||
|
|
||||||
|
if (v->server && v->server->controller == v)
|
||||||
|
{
|
||||||
|
// v->maysend = true;
|
||||||
|
v->server->maysend = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v->netchan.outgoing_sequence = v->netchan.incoming_sequence; //compensate for client->server packetloss.
|
||||||
|
if (!v->server)
|
||||||
|
v->maysend = true;
|
||||||
|
else if (!v->chokeme || !cluster->chokeonnotupdated)
|
||||||
|
{
|
||||||
|
v->maysend = true;
|
||||||
|
v->chokeme = cluster->chokeonnotupdated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseQWC(cluster, useserver, v, m);
|
||||||
|
|
||||||
|
if (v->server && v->server->controller == v)
|
||||||
|
{
|
||||||
|
QTV_Run(v->server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!v && cluster->allownqclients)
|
||||||
|
{
|
||||||
|
unsigned int ctrl;
|
||||||
|
//NQ connectionless packet?
|
||||||
|
m->readpos = 0;
|
||||||
|
ctrl = ReadLong(m);
|
||||||
|
ctrl = SwapLong(ctrl);
|
||||||
|
if (ctrl & NETFLAG_CTL)
|
||||||
|
{ //looks hopeful
|
||||||
|
switch(ReadByte(m))
|
||||||
|
{
|
||||||
|
case CCREQ_SERVER_INFO:
|
||||||
|
ReadString(m, tempbuffer, sizeof(tempbuffer));
|
||||||
|
if (!strcmp(tempbuffer, NET_GAMENAME_NQ))
|
||||||
|
{
|
||||||
|
m->cursize = 0;
|
||||||
|
WriteLong(m, 0);
|
||||||
|
WriteByte(m, CCREP_SERVER_INFO);
|
||||||
|
WriteString(m, "??");
|
||||||
|
WriteString(m, cluster->hostname);
|
||||||
|
WriteString(m, "Quake TV");
|
||||||
|
WriteByte(m, cluster->numviewers>255?255:cluster->numviewers);
|
||||||
|
WriteByte(m, cluster->maxviewers>255?255:cluster->maxviewers);
|
||||||
|
WriteByte(m, NET_PROTOCOL_VERSION);
|
||||||
|
*(int*)m->data = BigLong(NETFLAG_CTL | m->cursize);
|
||||||
|
NET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, &from), m->cursize, m->data, from);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CCREQ_CONNECT:
|
||||||
|
ReadString(m, tempbuffer, sizeof(tempbuffer));
|
||||||
|
if (!strcmp(tempbuffer, NET_GAMENAME_NQ))
|
||||||
|
{
|
||||||
|
if (ReadByte(m) == NET_PROTOCOL_VERSION)
|
||||||
|
{
|
||||||
|
//proquake extensions
|
||||||
|
int mod = ReadByte(m);
|
||||||
|
int modver = ReadByte(m);
|
||||||
|
int flags = ReadByte(m);
|
||||||
|
int passwd = ReadLong(m);
|
||||||
|
|
||||||
|
//fte extension, sent so that dual-protocol servers will not create connections for dual-protocol clients
|
||||||
|
ReadString(m, tempbuffer, sizeof(tempbuffer));
|
||||||
|
if (!strncmp(tempbuffer, "getchallenge", 12))
|
||||||
|
break;
|
||||||
|
|
||||||
|
//drop any old nq clients from this address
|
||||||
|
for (v = cluster->viewers; v; v = v->next)
|
||||||
|
{
|
||||||
|
if (v->netchan.isnqprotocol)
|
||||||
|
{
|
||||||
|
if (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 0))
|
||||||
|
{
|
||||||
|
Sys_Printf(cluster, "Dup connect from %s\n", v->name);
|
||||||
|
v->drop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NewNQClient(cluster, &from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QW_TCPConnection(cluster_t *cluster, SOCKET sock, wsrbuf_t ws)
|
||||||
|
{
|
||||||
|
int alen;
|
||||||
|
tcpconnect_t *tc;
|
||||||
|
tc = malloc(sizeof(*tc));
|
||||||
|
if (!tc)
|
||||||
|
{
|
||||||
|
closesocket(sock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tc->sock = sock;
|
||||||
|
tc->websocket = ws;
|
||||||
|
tc->inbuffersize = 0;
|
||||||
|
tc->outbuffersize = 0;
|
||||||
|
|
||||||
|
memset(&tc->peeraddr, 0, sizeof(tc->peeraddr));
|
||||||
|
tc->peeraddr.tcpcon = tc;
|
||||||
|
|
||||||
|
alen = sizeof(tc->peeraddr.sockaddr);
|
||||||
|
getpeername(sock, (struct sockaddr*)&tc->peeraddr.sockaddr, &alen);
|
||||||
|
|
||||||
|
tc->next = cluster->tcpconnects;
|
||||||
|
cluster->tcpconnects = tc;
|
||||||
|
}
|
||||||
|
|
||||||
void QW_UpdateUDPStuff(cluster_t *cluster)
|
void QW_UpdateUDPStuff(cluster_t *cluster)
|
||||||
{
|
{
|
||||||
char buffer[MAX_MSGLEN]; //contains read info
|
char buffer[MAX_MSGLEN]; //contains read info
|
||||||
char tempbuffer[256];
|
|
||||||
netadr_t from;
|
netadr_t from;
|
||||||
int fromsize = sizeof(from);
|
int fromsize = sizeof(from.sockaddr);
|
||||||
int read;
|
int read;
|
||||||
int qport;
|
|
||||||
netmsg_t m;
|
netmsg_t m;
|
||||||
int socketno;
|
int socketno;
|
||||||
|
tcpconnect_t *tc, **l;
|
||||||
|
|
||||||
viewer_t *v, *f;
|
viewer_t *v, *f;
|
||||||
sv_t *useserver;
|
|
||||||
|
|
||||||
if (*cluster->master && (cluster->curtime > cluster->mastersendtime || cluster->mastersendtime > cluster->curtime + 4*1000*60)) //urm... time wrapped?
|
if (*cluster->master && (cluster->curtime > cluster->mastersendtime || cluster->mastersendtime > cluster->curtime + 4*1000*60)) //urm... time wrapped?
|
||||||
{
|
{
|
||||||
|
@ -4215,167 +4348,89 @@ void QW_UpdateUDPStuff(cluster_t *cluster)
|
||||||
socketno++;
|
socketno++;
|
||||||
if (socketno >= SOCKETGROUPS)
|
if (socketno >= SOCKETGROUPS)
|
||||||
break;
|
break;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
read = recvfrom(cluster->qwdsocket[socketno], buffer, sizeof(buffer), 0, (struct sockaddr*)&from, (unsigned*)&fromsize);
|
from.tcpcon = NULL;
|
||||||
|
read = recvfrom(cluster->qwdsocket[socketno], buffer, sizeof(buffer), 0, (struct sockaddr*)&from.sockaddr, (unsigned*)&fromsize);
|
||||||
|
|
||||||
if (read <= 5) //otherwise it's a runt or bad.
|
|
||||||
{
|
|
||||||
if (read < 0) //it's bad.
|
if (read < 0) //it's bad.
|
||||||
{
|
{
|
||||||
socketno++;
|
socketno++;
|
||||||
if (socketno >= SOCKETGROUPS)
|
if (socketno >= SOCKETGROUPS)
|
||||||
break;
|
break;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (read <= 5) //otherwise it's a runt or bad.
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
m.cursize = read;
|
m.cursize = read;
|
||||||
|
m.data = buffer;
|
||||||
m.readpos = 0;
|
m.readpos = 0;
|
||||||
|
|
||||||
if (*(int*)buffer == -1)
|
QW_ProcessUDPPacket(cluster, &m, from);
|
||||||
{ //connectionless message
|
}
|
||||||
ConnectionlessPacket(cluster, &from, &m);
|
|
||||||
|
for (l = &cluster->tcpconnects; *l; )
|
||||||
|
{
|
||||||
|
int clen;
|
||||||
|
tc = *l;
|
||||||
|
read = sizeof(tc->inbuffer) - tc->inbuffersize;
|
||||||
|
read = NET_WebSocketRecv(tc->sock, &tc->websocket, tc->inbuffer+tc->inbuffersize, read, &clen);
|
||||||
|
if (read > 0)
|
||||||
|
tc->inbuffersize += read;
|
||||||
|
if (read == 0 || read < 0)
|
||||||
|
{
|
||||||
|
if (read == 0 || qerrno != EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
*l = tc->next;
|
||||||
|
closesocket(tc->sock);
|
||||||
|
free(tc);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read < 10) //otherwise it's a runt or bad.
|
|
||||||
{
|
|
||||||
if (read < 0) //it's bad.
|
|
||||||
break;
|
|
||||||
|
|
||||||
qport = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//read the qport
|
|
||||||
ReadLong(&m);
|
|
||||||
ReadLong(&m);
|
|
||||||
qport = ReadShort(&m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (v = cluster->viewers; v; v = v->next)
|
if (clen >= 0)
|
||||||
{
|
{
|
||||||
if (v->netchan.isnqprotocol)
|
/*if it really is a webclient connection, then the stream will be packetized already
|
||||||
{
|
so we don't waste extra space*/
|
||||||
if (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 0))
|
m.data = tc->inbuffer;
|
||||||
{
|
|
||||||
if (NQNetchan_Process(cluster, &v->netchan, &m))
|
|
||||||
{
|
|
||||||
useserver = v->server;
|
|
||||||
if (useserver && useserver->parsingconnectiondata)
|
|
||||||
useserver = NULL;
|
|
||||||
|
|
||||||
v->timeout = cluster->curtime + 15*1000;
|
|
||||||
|
|
||||||
ParseNQC(cluster, useserver, v, &m);
|
|
||||||
|
|
||||||
if (v->server && v->server->controller == v)
|
|
||||||
{
|
|
||||||
QTV_Run(v->server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Net_CompareAddress(&v->netchan.remote_address, &from, v->netchan.qport, qport))
|
|
||||||
{
|
|
||||||
if (v->server && v->server->controller == v && v->maysend)
|
|
||||||
SendViewerPackets(cluster, v); //do this before we read the new sequences
|
|
||||||
|
|
||||||
if (Netchan_Process(&v->netchan, &m))
|
|
||||||
{
|
|
||||||
useserver = v->server;
|
|
||||||
if (useserver && useserver->parsingconnectiondata && useserver->controller != v)
|
|
||||||
useserver = NULL;
|
|
||||||
|
|
||||||
v->timeout = cluster->curtime + 15*1000;
|
|
||||||
|
|
||||||
if (v->server && v->server->controller == v)
|
|
||||||
{
|
|
||||||
// v->maysend = true;
|
|
||||||
v->server->maysend = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
v->netchan.outgoing_sequence = v->netchan.incoming_sequence; //compensate for client->server packetloss.
|
|
||||||
if (!v->server)
|
|
||||||
v->maysend = true;
|
|
||||||
else if (!v->chokeme || !cluster->chokeonnotupdated)
|
|
||||||
{
|
|
||||||
v->maysend = true;
|
|
||||||
v->chokeme = cluster->chokeonnotupdated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseQWC(cluster, useserver, v, &m);
|
|
||||||
|
|
||||||
if (v->server && v->server->controller == v)
|
|
||||||
{
|
|
||||||
QTV_Run(v->server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!v && cluster->allownqclients)
|
|
||||||
{
|
|
||||||
//NQ connectionless packet?
|
|
||||||
m.readpos = 0;
|
m.readpos = 0;
|
||||||
read = ReadLong(&m);
|
m.cursize = read;
|
||||||
read = SwapLong(read);
|
|
||||||
if (read & NETFLAG_CTL)
|
QW_ProcessUDPPacket(cluster, &m, tc->peeraddr);
|
||||||
{ //looks hopeful
|
|
||||||
switch(ReadByte(&m))
|
memmove(tc->inbuffer, tc->inbuffer+read, tc->inbuffersize - (read));
|
||||||
{
|
tc->inbuffersize -= read;
|
||||||
case CCREQ_SERVER_INFO:
|
continue; //ask to read the next packet
|
||||||
ReadString(&m, tempbuffer, sizeof(tempbuffer));
|
|
||||||
if (!strcmp(tempbuffer, NET_GAMENAME_NQ))
|
|
||||||
{
|
|
||||||
m.cursize = 0;
|
|
||||||
WriteLong(&m, 0);
|
|
||||||
WriteByte(&m, CCREP_SERVER_INFO);
|
|
||||||
WriteString(&m, "??");
|
|
||||||
WriteString(&m, cluster->hostname);
|
|
||||||
WriteString(&m, "Quake TV");
|
|
||||||
WriteByte(&m, cluster->numviewers>255?255:cluster->numviewers);
|
|
||||||
WriteByte(&m, cluster->maxviewers>255?255:cluster->maxviewers);
|
|
||||||
WriteByte(&m, NET_PROTOCOL_VERSION);
|
|
||||||
*(int*)m.data = BigLong(NETFLAG_CTL | m.cursize);
|
|
||||||
NET_SendPacket(cluster, NET_ChooseSocket(cluster->qwdsocket, &from), m.cursize, m.data, from);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (tc->inbuffersize >= 2)
|
||||||
|
{
|
||||||
|
read = (tc->inbuffer[0]<<8) | tc->inbuffer[1];
|
||||||
|
if (tc->inbuffersize >= 2+read)
|
||||||
|
{
|
||||||
|
m.data = tc->inbuffer+2;
|
||||||
|
m.readpos = 0;
|
||||||
|
m.cursize = read;
|
||||||
|
|
||||||
|
QW_ProcessUDPPacket(cluster, &m, tc->peeraddr);
|
||||||
|
|
||||||
|
memmove(tc->inbuffer, tc->inbuffer+2+read, tc->inbuffersize - (2+read));
|
||||||
|
tc->inbuffersize -= 2+read;
|
||||||
|
}
|
||||||
|
else
|
||||||
break;
|
break;
|
||||||
case CCREQ_CONNECT:
|
|
||||||
ReadString(&m, tempbuffer, sizeof(tempbuffer));
|
|
||||||
if (!strcmp(tempbuffer, NET_GAMENAME_NQ))
|
|
||||||
{
|
|
||||||
if (ReadByte(&m) == NET_PROTOCOL_VERSION)
|
|
||||||
{
|
|
||||||
//drop any old nq clients from this address
|
|
||||||
for (v = cluster->viewers; v; v = v->next)
|
|
||||||
{
|
|
||||||
if (v->netchan.isnqprotocol)
|
|
||||||
{
|
|
||||||
if (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 0))
|
|
||||||
{
|
|
||||||
Sys_Printf(cluster, "Dup connect from %s\n", v->name);
|
|
||||||
v->drop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NewNQClient(cluster, &from);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l = &(*l)->next;
|
||||||
|
}
|
||||||
|
|
||||||
if (cluster->viewers && cluster->viewers->drop)
|
if (cluster->viewers && cluster->viewers->drop)
|
||||||
{
|
{
|
||||||
// Sys_Printf(cluster, "Dropping viewer %s\n", v->name);
|
// Sys_Printf(cluster, "Dropping viewer %s\n", v->name);
|
||||||
|
|
|
@ -363,6 +363,7 @@ void Cmd_Hostname(cmdctxt_t *ctx)
|
||||||
|
|
||||||
void Cmd_Master(cmdctxt_t *ctx)
|
void Cmd_Master(cmdctxt_t *ctx)
|
||||||
{
|
{
|
||||||
|
SOCKET s;
|
||||||
char *newval = Cmd_Argv(ctx, 1);
|
char *newval = Cmd_Argv(ctx, 1);
|
||||||
netadr_t addr;
|
netadr_t addr;
|
||||||
|
|
||||||
|
@ -391,41 +392,17 @@ void Cmd_Master(cmdctxt_t *ctx)
|
||||||
strlcpy(ctx->cluster->master, newval, sizeof(ctx->cluster->master));
|
strlcpy(ctx->cluster->master, newval, sizeof(ctx->cluster->master));
|
||||||
ctx->cluster->mastersendtime = ctx->cluster->curtime;
|
ctx->cluster->mastersendtime = ctx->cluster->curtime;
|
||||||
|
|
||||||
if (ctx->cluster->qwdsocket != INVALID_SOCKET)
|
s = NET_ChooseSocket(ctx->cluster->qwdsocket, &addr);
|
||||||
NET_SendPacket (ctx->cluster, NET_ChooseSocket(ctx->cluster->qwdsocket, &addr), 1, "k", addr);
|
if (s != INVALID_SOCKET)
|
||||||
|
NET_SendPacket (ctx->cluster, s, 1, "k", addr);
|
||||||
Cmd_Printf(ctx, "Master server set.\n");
|
Cmd_Printf(ctx, "Master server set.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cmd_UDPPort(cmdctxt_t *ctx)
|
void Cmd_UDPPort(cmdctxt_t *ctx)
|
||||||
{
|
{
|
||||||
int news;
|
|
||||||
int newp = atoi(Cmd_Argv(ctx, 1));
|
int newp = atoi(Cmd_Argv(ctx, 1));
|
||||||
news = QW_InitUDPSocket(newp, false);
|
NET_InitUDPSocket(ctx->cluster, newp, true);
|
||||||
if (news != INVALID_SOCKET)
|
NET_InitUDPSocket(ctx->cluster, newp, false);
|
||||||
{
|
|
||||||
ctx->cluster->mastersendtime = ctx->cluster->curtime;
|
|
||||||
closesocket(ctx->cluster->qwdsocket[0]);
|
|
||||||
ctx->cluster->qwdsocket[0] = news;
|
|
||||||
ctx->cluster->qwlistenportnum = newp;
|
|
||||||
|
|
||||||
Cmd_Printf(ctx, "Opened udp4 port %i (all connected qw clients will time out)\n", newp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Cmd_Printf(ctx, "Failed to open udp4 port %i\n", newp);
|
|
||||||
|
|
||||||
news = QW_InitUDPSocket(newp, true);
|
|
||||||
|
|
||||||
if (news != INVALID_SOCKET)
|
|
||||||
{
|
|
||||||
ctx->cluster->mastersendtime = ctx->cluster->curtime;
|
|
||||||
closesocket(ctx->cluster->qwdsocket[1]);
|
|
||||||
ctx->cluster->qwdsocket[1] = news;
|
|
||||||
ctx->cluster->qwlistenportnum = newp;
|
|
||||||
|
|
||||||
Cmd_Printf(ctx, "Opened udp6 port %i (all connected qw clients will time out)\n", newp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Cmd_Printf(ctx, "Failed to open udp6 port %i\n", newp);
|
|
||||||
}
|
}
|
||||||
void Cmd_AdminPassword(cmdctxt_t *ctx)
|
void Cmd_AdminPassword(cmdctxt_t *ctx)
|
||||||
{
|
{
|
||||||
|
@ -622,11 +599,11 @@ void Cmd_Status(cmdctxt_t *ctx)
|
||||||
Cmd_Printf(ctx, " Talking allowed\n");
|
Cmd_Printf(ctx, " Talking allowed\n");
|
||||||
if (ctx->cluster->nobsp)
|
if (ctx->cluster->nobsp)
|
||||||
Cmd_Printf(ctx, " No BSP loading\n");
|
Cmd_Printf(ctx, " No BSP loading\n");
|
||||||
if (ctx->cluster->tcpsocket != INVALID_SOCKET)
|
if (ctx->cluster->tcpsocket[0] != INVALID_SOCKET || ctx->cluster->tcpsocket[1] != INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
Cmd_Printf(ctx, " tcp port %i\n", ctx->cluster->tcplistenportnum);
|
Cmd_Printf(ctx, " tcp port %i\n", ctx->cluster->tcplistenportnum);
|
||||||
}
|
}
|
||||||
if (ctx->cluster->tcpsocket != INVALID_SOCKET)
|
if (ctx->cluster->qwdsocket[0] != INVALID_SOCKET || ctx->cluster->qwdsocket[1] != INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
Cmd_Printf(ctx, " udp port %i\n", ctx->cluster->qwlistenportnum);
|
Cmd_Printf(ctx, " udp port %i\n", ctx->cluster->qwlistenportnum);
|
||||||
}
|
}
|
||||||
|
@ -780,6 +757,39 @@ void Cmd_AllowNQ(cmdctxt_t *ctx)
|
||||||
Cmd_Printf(ctx, "allownq set\n");
|
Cmd_Printf(ctx, "allownq set\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Cmd_InitialDelay(cmdctxt_t *ctx)
|
||||||
|
{
|
||||||
|
char *val = Cmd_Argv(ctx, 1);
|
||||||
|
if (!*val)
|
||||||
|
{
|
||||||
|
Cmd_Printf(ctx, "initialdelay is currently %g seconds\n", ctx->cluster->anticheattime/1000.f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ctx->cluster->anticheattime = atof(val)*1000;
|
||||||
|
if (ctx->cluster->anticheattime < 1)
|
||||||
|
ctx->cluster->anticheattime = 1;
|
||||||
|
Cmd_Printf(ctx, "initialdelay set\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cmd_SlowDelay(cmdctxt_t *ctx)
|
||||||
|
{
|
||||||
|
char *val = Cmd_Argv(ctx, 1);
|
||||||
|
if (!*val)
|
||||||
|
{
|
||||||
|
Cmd_Printf(ctx, "slowdelay is currently %g seconds\n", ctx->cluster->tooslowdelay/1000.f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ctx->cluster->tooslowdelay = atof(val)*1000;
|
||||||
|
if (ctx->cluster->tooslowdelay < 1)
|
||||||
|
ctx->cluster->tooslowdelay = 1;
|
||||||
|
Cmd_Printf(ctx, "slowdelay set\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Cmd_MaxProxies(cmdctxt_t *ctx)
|
void Cmd_MaxProxies(cmdctxt_t *ctx)
|
||||||
{
|
{
|
||||||
char *val = Cmd_Argv(ctx, 1);
|
char *val = Cmd_Argv(ctx, 1);
|
||||||
|
@ -1305,6 +1315,8 @@ const rconcommands_t rconcommands[] =
|
||||||
{"exit", 0, 1, Cmd_Quit},
|
{"exit", 0, 1, Cmd_Quit},
|
||||||
{"streams", 0, 1, Cmd_Streams, "shows a list of active streams"},
|
{"streams", 0, 1, Cmd_Streams, "shows a list of active streams"},
|
||||||
{"allownq", 0, 1, Cmd_AllowNQ, "permits nq clients to connect. This can be disabled as this code is less tested than the rest"},
|
{"allownq", 0, 1, Cmd_AllowNQ, "permits nq clients to connect. This can be disabled as this code is less tested than the rest"},
|
||||||
|
{"initialdelay",0, 1, Cmd_InitialDelay, "Specifies the duration for which new connections will be buffered. Large values prevents players from spectating their enemies as a cheap wallhack."},
|
||||||
|
{"slowdelay", 0, 1, Cmd_SlowDelay, "If a server is not sending enough data, the proxy will delay parsing for this long."},
|
||||||
|
|
||||||
|
|
||||||
{"halt", 1, 0, Cmd_Halt, "disables a stream, preventing it from reconnecting until someone tries watching it anew. Boots current spectators"},
|
{"halt", 1, 0, Cmd_Halt, "disables a stream, preventing it from reconnecting until someone tries watching it anew. Boots current spectators"},
|
||||||
|
|
|
@ -168,9 +168,10 @@ dblbreak:
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{ //old fashioned method
|
{ //old fashioned method
|
||||||
((struct sockaddr_in *)sadr)->sin_family = AF_INET;
|
struct sockaddr_in *sin = (struct sockaddr_in *)sadr->sockaddr;
|
||||||
|
sin->sin_family = AF_INET;
|
||||||
|
|
||||||
((struct sockaddr_in *)sadr)->sin_port = htons(defaultport);
|
sin->sin_port = htons(defaultport);
|
||||||
|
|
||||||
strcpy (copy, s);
|
strcpy (copy, s);
|
||||||
// strip off a trailing :port if present
|
// strip off a trailing :port if present
|
||||||
|
@ -178,12 +179,12 @@ dblbreak:
|
||||||
if (*colon == ':')
|
if (*colon == ':')
|
||||||
{
|
{
|
||||||
*colon = 0;
|
*colon = 0;
|
||||||
((struct sockaddr_in *)sadr)->sin_port = htons((short)atoi(colon+1));
|
sin->sin_port = htons((short)atoi(colon+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copy[0] >= '0' && copy[0] <= '9') //this is the wrong way to test. a server name may start with a number.
|
if (copy[0] >= '0' && copy[0] <= '9') //this is the wrong way to test. a server name may start with a number.
|
||||||
{
|
{
|
||||||
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy);
|
*(int *)&sin->sin_addr = inet_addr(copy);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -191,7 +192,7 @@ dblbreak:
|
||||||
return 0;
|
return 0;
|
||||||
if (h->h_addrtype != AF_INET)
|
if (h->h_addrtype != AF_INET)
|
||||||
return 0;
|
return 0;
|
||||||
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0];
|
*(int *)&sin->sin_addr = *(int *)h->h_addr_list[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,17 +201,37 @@ dblbreak:
|
||||||
|
|
||||||
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2)
|
qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2)
|
||||||
{
|
{
|
||||||
struct sockaddr_in *i1=(void*)s1, *i2=(void*)s2;
|
struct sockaddr *g1=(void*)s1->sockaddr, *g2=(void*)s2->sockaddr;
|
||||||
if (i1->sin_family != i2->sin_family)
|
if (g1->sa_family != g2->sa_family)
|
||||||
return false;
|
return false;
|
||||||
if (i1->sin_family == AF_INET)
|
switch(g1->sa_family)
|
||||||
{
|
{
|
||||||
|
case AF_INET:
|
||||||
|
{
|
||||||
|
struct sockaddr_in *i1=(void*)s1->sockaddr, *i2=(void*)s2->sockaddr;
|
||||||
if (*(unsigned int*)&i1->sin_addr != *(unsigned int*)&i2->sin_addr)
|
if (*(unsigned int*)&i1->sin_addr != *(unsigned int*)&i2->sin_addr)
|
||||||
return false;
|
return false;
|
||||||
if (i1->sin_port != i2->sin_port && qp1 != qp2) //allow qports to match instead of ports, if required.
|
if (i1->sin_port != i2->sin_port && qp1 != qp2) //allow qports to match instead of ports, if required.
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AF_INET6:
|
||||||
|
{
|
||||||
|
struct sockaddr_in6 *i1=(void*)s1->sockaddr, *i2=(void*)s2->sockaddr;
|
||||||
|
if (((unsigned int*)&i1->sin6_addr)[0] != ((unsigned int*)&i2->sin6_addr)[0])
|
||||||
|
return false;
|
||||||
|
if (((unsigned int*)&i1->sin6_addr)[1] != ((unsigned int*)&i2->sin6_addr)[1])
|
||||||
|
return false;
|
||||||
|
if (((unsigned int*)&i1->sin6_addr)[2] != ((unsigned int*)&i2->sin6_addr)[2])
|
||||||
|
return false;
|
||||||
|
if (((unsigned int*)&i1->sin6_addr)[3] != ((unsigned int*)&i2->sin6_addr)[3])
|
||||||
|
return false;
|
||||||
|
if (i1->sin6_port != i2->sin6_port && qp1 != qp2) //allow qports to match instead of ports, if required.
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +441,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
|
||||||
strcpy(qtv->status, "Unable to resolve server\n");
|
strcpy(qtv->status, "Unable to resolve server\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
afam = ((struct sockaddr*)&qtv->serveraddress)->sa_family;
|
afam = ((struct sockaddr*)&qtv->serveraddress.sockaddr)->sa_family;
|
||||||
pfam = ((afam==AF_INET6)?PF_INET6:PF_INET);
|
pfam = ((afam==AF_INET6)?PF_INET6:PF_INET);
|
||||||
asz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));
|
asz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));
|
||||||
qtv->sourcesock = socket(pfam, SOCK_STREAM, IPPROTO_TCP);
|
qtv->sourcesock = socket(pfam, SOCK_STREAM, IPPROTO_TCP);
|
||||||
|
@ -448,7 +469,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress, asz) == INVALID_SOCKET)
|
if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress.sockaddr, asz) == INVALID_SOCKET)
|
||||||
{
|
{
|
||||||
err = qerrno;
|
err = qerrno;
|
||||||
if (err != EINPROGRESS && err != EAGAIN && err != EWOULDBLOCK) //bsd sockets are meant to return EINPROGRESS, but some winsock drivers use EWOULDBLOCK instead. *sigh*...
|
if (err != EINPROGRESS && err != EAGAIN && err != EWOULDBLOCK) //bsd sockets are meant to return EINPROGRESS, but some winsock drivers use EWOULDBLOCK instead. *sigh*...
|
||||||
|
@ -480,7 +501,7 @@ qboolean Net_ConnectToUDPServer(sv_t *qtv, char *ip)
|
||||||
Sys_Printf(qtv->cluster, "Stream %i: Unable to resolve %s\n", qtv->streamid, ip);
|
Sys_Printf(qtv->cluster, "Stream %i: Unable to resolve %s\n", qtv->streamid, ip);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
afam = ((struct sockaddr*)&qtv->serveraddress)->sa_family;
|
afam = ((struct sockaddr*)&qtv->serveraddress.sockaddr)->sa_family;
|
||||||
pfam = ((afam==AF_INET6)?PF_INET6:PF_INET);
|
pfam = ((afam==AF_INET6)?PF_INET6:PF_INET);
|
||||||
asz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));
|
asz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));
|
||||||
qtv->sourcesock = socket(pfam, SOCK_DGRAM, IPPROTO_UDP);
|
qtv->sourcesock = socket(pfam, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
@ -899,8 +920,6 @@ qboolean Net_ReadStream(sv_t *qtv)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BUFFERTIME 10 //secords for artificial delay, so we can buffer things properly.
|
|
||||||
|
|
||||||
unsigned int Sys_Milliseconds(void)
|
unsigned int Sys_Milliseconds(void)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -1100,7 +1119,7 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000;
|
qtv->parsetime = Sys_Milliseconds() + qtv->cluster->anticheattime;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1379,11 +1398,12 @@ void QTV_ParseQWStream(sv_t *qtv)
|
||||||
unsigned int fromlen;
|
unsigned int fromlen;
|
||||||
int readlen;
|
int readlen;
|
||||||
netmsg_t msg;
|
netmsg_t msg;
|
||||||
fromlen = sizeof(from); //bug: this won't work on (free)bsd
|
fromlen = sizeof(from.sockaddr); //bug: this won't work on (free)bsd
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
readlen = recvfrom(qtv->sourcesock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&from, &fromlen);
|
from.tcpcon = NULL;
|
||||||
|
readlen = recvfrom(qtv->sourcesock, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&from.sockaddr, &fromlen);
|
||||||
if (readlen < 0)
|
if (readlen < 0)
|
||||||
{
|
{
|
||||||
//FIXME: Check for error
|
//FIXME: Check for error
|
||||||
|
@ -2015,9 +2035,9 @@ void QTV_Run(sv_t *qtv)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000;
|
qtv->parsetime = Sys_Milliseconds() + qtv->cluster->anticheattime;
|
||||||
if (!qtv->usequakeworldprotocols)
|
if (!qtv->usequakeworldprotocols)
|
||||||
Sys_Printf(qtv->cluster, "Stream %i: Connection established, buffering for %i seconds\n", qtv->streamid, BUFFERTIME);
|
Sys_Printf(qtv->cluster, "Stream %i: Connection established, buffering for %g seconds\n", qtv->streamid, qtv->cluster->anticheattime/1000.0f);
|
||||||
|
|
||||||
SV_ForwardStream(qtv, qtv->buffer, qtv->forwardpoint);
|
SV_ForwardStream(qtv, qtv->buffer, qtv->forwardpoint);
|
||||||
}
|
}
|
||||||
|
@ -2030,9 +2050,9 @@ void QTV_Run(sv_t *qtv)
|
||||||
{ //not enough stuff to play.
|
{ //not enough stuff to play.
|
||||||
if (qtv->parsetime < qtv->curtime)
|
if (qtv->parsetime < qtv->curtime)
|
||||||
{
|
{
|
||||||
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
|
qtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;
|
||||||
if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
// if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||||
QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
// QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2045,9 +2065,9 @@ void QTV_Run(sv_t *qtv)
|
||||||
length = 10;
|
length = 10;
|
||||||
if (qtv->buffersize < length)
|
if (qtv->buffersize < length)
|
||||||
{ //not enough stuff to play.
|
{ //not enough stuff to play.
|
||||||
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
|
qtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;
|
||||||
if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
// if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||||
QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
// QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
qtv->parsetime += buffer[0]; //well this was pointless
|
qtv->parsetime += buffer[0]; //well this was pointless
|
||||||
|
@ -2072,9 +2092,9 @@ void QTV_Run(sv_t *qtv)
|
||||||
|
|
||||||
if (qtv->buffersize < lengthofs+4)
|
if (qtv->buffersize < lengthofs+4)
|
||||||
{ //the size parameter doesn't fit.
|
{ //the size parameter doesn't fit.
|
||||||
if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
// if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||||
QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
// QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||||
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
|
qtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2101,9 +2121,9 @@ void QTV_Run(sv_t *qtv)
|
||||||
|
|
||||||
if (length+lengthofs+4 > qtv->buffersize)
|
if (length+lengthofs+4 > qtv->buffersize)
|
||||||
{
|
{
|
||||||
if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
// if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||||
QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
// QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||||
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
|
qtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay; //add two seconds
|
||||||
break; //can't parse it yet.
|
break; //can't parse it yet.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue