mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-22 03:51:32 +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->tcpsocket[0] = INVALID_SOCKET;
|
||||
cluster->tcpsocket[1] = INVALID_SOCKET;
|
||||
cluster->anticheattime = 1*1000;
|
||||
cluster->tooslowdelay = 100;
|
||||
cluster->qwlistenportnum = 0;
|
||||
cluster->allownqclients = true;
|
||||
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)
|
||||
{
|
||||
cluster->qwlistenportnum = 27599;
|
||||
cluster->qwdsocket[0] = QW_InitUDPSocket(cluster->qwlistenportnum, false);
|
||||
if (cluster->qwdsocket[0] != INVALID_SOCKET)
|
||||
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);
|
||||
NET_InitUDPSocket(cluster, cluster->qwlistenportnum, true);
|
||||
NET_InitUDPSocket(cluster, cluster->qwlistenportnum, false);
|
||||
}
|
||||
if (cluster->tcpsocket[0] == INVALID_SOCKET && cluster->tcpsocket[1] == INVALID_SOCKET && !cluster->tcplistenportnum)
|
||||
{
|
||||
|
|
|
@ -43,8 +43,7 @@
|
|||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE"
|
||||
MinimalRebuild="true"
|
||||
PreprocessorDefinitions="_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
UsePrecompiledHeader="0"
|
||||
|
@ -74,6 +73,7 @@
|
|||
OutputFile=".\Debug/qtvprox.exe"
|
||||
LinkIncremental="2"
|
||||
SuppressStartupBanner="true"
|
||||
GenerateManifest="false"
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile=".\Debug/qtvprox.pdb"
|
||||
SubSystem="1"
|
||||
|
@ -264,6 +264,10 @@
|
|||
RelativePath="..\rcon.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\engine\common\sha1.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
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;
|
||||
int packetlength;
|
||||
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 (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;
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
//truth does not imply that it should be freed/released, just unlinked.
|
||||
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];
|
||||
unsigned char *s;
|
||||
unsigned char *e;
|
||||
char *colon;
|
||||
float clientversion = 0;
|
||||
int len;
|
||||
qboolean eoh;
|
||||
int headersize;
|
||||
qboolean raw;
|
||||
sv_t *qtv;
|
||||
|
||||
if (pend->drop)
|
||||
if (pend->drop && !pend->flushing)
|
||||
{
|
||||
printf("pending drop\n");
|
||||
if (pend->srcfile)
|
||||
fclose(pend->srcfile);
|
||||
closesocket(pend->sock);
|
||||
|
@ -672,6 +877,7 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
|||
if (pend->bufferpos == pend->buffersize)
|
||||
{
|
||||
char buffer[MAX_PROXY_BUFFER/2];
|
||||
pend->droptime = cluster->curtime + 5*1000;
|
||||
len = fread(buffer, 1, sizeof(buffer), pend->srcfile);
|
||||
if (!len)
|
||||
{
|
||||
|
@ -684,31 +890,32 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
|||
return false; //don't try reading anything yet
|
||||
}
|
||||
|
||||
if (pend->bufferpos == pend->buffersize)
|
||||
{
|
||||
pend->drop = true;
|
||||
if (pend->bufferpos != pend->buffersize)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
pend->flushing = false;
|
||||
if (pend->drop)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pend->droptime < cluster->curtime)
|
||||
{
|
||||
printf("pending timeout\n");
|
||||
pend->drop = true;
|
||||
pend->flushing = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
pend->drop = true;
|
||||
return false;
|
||||
}
|
||||
else if (len > 0)
|
||||
pend->droptime = cluster->curtime + 5*1000;
|
||||
if (len < 0)
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -717,6 +924,39 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
|||
|
||||
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))
|
||||
{ //I have no idea what the smeg you are.
|
||||
pend->drop = true;
|
||||
|
@ -729,15 +969,25 @@ qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend)
|
|||
|
||||
//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;
|
||||
}
|
||||
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
|
||||
s+=3; //Fixme: this is wrong
|
||||
headersize = s - pend->inbuffer - 1;
|
||||
headersize = s - pend->inbuffer;
|
||||
|
||||
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))
|
||||
{
|
||||
pend->drop = true;
|
||||
HTTPSV_GetMethod(cluster, pend);
|
||||
pend->flushing = true;
|
||||
return false;
|
||||
memmove(pend->inbuffer, pend->inbuffer+headersize, pend->inbuffersize-headersize);
|
||||
pend->inbuffersize -= headersize;
|
||||
return SV_ReadPendingProxy(cluster, pend);
|
||||
}
|
||||
|
||||
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..)
|
||||
|
||||
|
||||
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] = {
|
||||
'\0', '#', '#', '#', '#', '.', '#', '#',
|
||||
|
@ -110,19 +143,20 @@ static void HTTPSV_SendHTTPHeader(cluster_t *cluster, oproxy_t *dest, char *erro
|
|||
|
||||
if (nocache)
|
||||
{
|
||||
s = "HTTP/1.1 %s OK\n"
|
||||
"Content-Type: %s\n"
|
||||
"Cache-Control: no-cache, must-revalidate\n"
|
||||
"Expires: Mon, 26 Jul 1997 05:00:00 GMT\n"
|
||||
"Connection: close\n"
|
||||
"\n";
|
||||
s = "HTTP/1.1 %s OK\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Cache-Control: no-cache, must-revalidate\r\n"
|
||||
"Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
s = "HTTP/1.1 %s OK\n"
|
||||
"Content-Type: %s\n"
|
||||
"Connection: close\n"
|
||||
"\n";
|
||||
s = "HTTP/1.1 %s OK\r\n"
|
||||
"Cache-Control: public, max-age=3600\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n";
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
strcpy(result, o);
|
||||
strlcpy(result, o, sizeof(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)
|
||||
{
|
||||
char connection[64];
|
||||
char upgrade[64];
|
||||
|
||||
char *s;
|
||||
char *uri, *uriend;
|
||||
char *args;
|
||||
|
@ -1090,6 +1127,48 @@ void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend)
|
|||
uriend = s;
|
||||
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))
|
||||
{
|
||||
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 <string.h>
|
||||
|
||||
#ifndef IPV6_V6ONLY
|
||||
#define IPV6_V6ONLY 27
|
||||
#endif
|
||||
|
||||
#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)
|
||||
{
|
||||
#ifdef AF_INET6
|
||||
if (((struct sockaddr *)adr)->sa_family == AF_INET6)
|
||||
if (((struct sockaddr *)adr->sockaddr)->sa_family == AF_INET6)
|
||||
return sock[1];
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int er = qerrno;
|
||||
|
@ -56,7 +231,7 @@ int Netchan_IsLocal (netadr_t adr)
|
|||
{
|
||||
case AF_INET:
|
||||
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[2] == 0 &&
|
||||
bytes[3] == 1)
|
||||
|
@ -177,7 +352,7 @@ Netchan_OutOfBandPrint
|
|||
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;
|
||||
char string[8192];
|
||||
|
@ -191,7 +366,7 @@ void Netchan_OutOfBandPrint (cluster_t *cluster, SOCKET sock[], netadr_t adr, ch
|
|||
#endif // _WIN32
|
||||
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)
|
||||
{
|
||||
//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);
|
||||
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, 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)
|
||||
{
|
||||
|
@ -562,6 +577,7 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)
|
|||
for (i = 0; i < MAX_CLIENTS; i++)
|
||||
{ //hide players
|
||||
//they'll be sent after this packet.
|
||||
tv->map.players[i].oldactive = tv->map.players[i].active;
|
||||
tv->map.players[i].active = false;
|
||||
}
|
||||
}
|
||||
|
@ -576,7 +592,6 @@ static void ParsePlayerInfo(sv_t *tv, netmsg_t *m, qboolean clearoldplayers)
|
|||
|
||||
if (tv->usequakeworldprotocols)
|
||||
{
|
||||
tv->map.players[num].old = tv->map.players[num].current;
|
||||
flags = (unsigned short)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)
|
||||
|
||||
|
||||
|
||||
size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||
|
||||
#define VERSION "0.01" //this will be added to the serverinfo
|
||||
|
||||
|
@ -249,8 +249,10 @@ typedef int qboolean;
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct{
|
||||
char blob[64];
|
||||
typedef struct
|
||||
{
|
||||
void *tcpcon; /*value not indirected, only compared*/
|
||||
char sockaddr[64];
|
||||
} netadr_t;
|
||||
|
||||
|
||||
|
@ -351,6 +353,7 @@ typedef struct {
|
|||
int leafcount;
|
||||
unsigned short leafs[MAX_ENTITY_LEAFS];
|
||||
|
||||
qboolean oldactive:1;
|
||||
qboolean active:1;
|
||||
qboolean gibbed:1;
|
||||
qboolean dead:1;
|
||||
|
@ -457,6 +460,15 @@ typedef struct viewer_s {
|
|||
int firstconnect;
|
||||
} 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.
|
||||
typedef struct oproxy_s {
|
||||
int authkey;
|
||||
|
@ -471,6 +483,7 @@ typedef struct oproxy_s {
|
|||
FILE *srcfile; //buffer is padded with data from this file when its empty
|
||||
FILE *file; //recording a demo (written to)
|
||||
SOCKET sock; //playing to a proxy
|
||||
wsrbuf_t websocket;
|
||||
|
||||
unsigned char inbuffer[MAX_PROXY_INBUFFER];
|
||||
unsigned int inbuffersize; //amount of data available.
|
||||
|
@ -481,6 +494,20 @@ typedef struct oproxy_s {
|
|||
struct oproxy_s *next;
|
||||
} 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 {
|
||||
short origin[3];
|
||||
unsigned char soundindex;
|
||||
|
@ -679,6 +706,7 @@ typedef struct {
|
|||
struct cluster_s {
|
||||
SOCKET qwdsocket[2]; //udp + quakeworld protocols
|
||||
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];
|
||||
int inputlength;
|
||||
|
@ -713,6 +741,8 @@ struct cluster_s {
|
|||
qboolean nobsp;
|
||||
qboolean allownqclients; //nq clients require no challenge
|
||||
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;
|
||||
|
||||
|
@ -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 BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *spectatorflag);
|
||||
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);
|
||||
unsigned int Sys_Milliseconds(void);
|
||||
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 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);
|
||||
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);
|
||||
SOCKET NET_ChooseSocket(SOCKET sock[], netadr_t *adr);
|
||||
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_OutOfBand (cluster_t *cluster, SOCKET sock, netadr_t adr, int length, void *data);
|
||||
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 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)
|
||||
{
|
||||
|
@ -934,7 +882,7 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
|
|||
|
||||
if (!ChallengePasses(addr, atoi(challenge)))
|
||||
{
|
||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "n" "Bad challenge");
|
||||
Netchan_OutOfBandPrint(cluster, *addr, "n" "Bad challenge");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -942,7 +890,7 @@ void NewQWClient(cluster_t *cluster, netadr_t *addr, char *connectmessage)
|
|||
viewer = malloc(sizeof(viewer_t));
|
||||
if (!viewer)
|
||||
{
|
||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "n" "Out of memory");
|
||||
Netchan_OutOfBandPrint(cluster, *addr, "n" "Out of memory");
|
||||
return;
|
||||
}
|
||||
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));
|
||||
ParseUserInfo(cluster, viewer);
|
||||
|
||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *addr, "j");
|
||||
Netchan_OutOfBandPrint(cluster, *addr, "j");
|
||||
|
||||
NewClient(cluster, viewer);
|
||||
}
|
||||
|
@ -1056,7 +1004,7 @@ void QTV_Rcon(cluster_t *cluster, char *message, netadr_t *from)
|
|||
|
||||
if (!*cluster->adminpassword)
|
||||
{
|
||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "n" "Bad rcon_password.\n");
|
||||
Netchan_OutOfBandPrint(cluster, *from, "n" "Bad rcon_password.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1067,11 +1015,11 @@ void QTV_Rcon(cluster_t *cluster, char *message, netadr_t *from)
|
|||
passlen = command-message;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -1287,13 +1235,13 @@ void ConnectionlessPacket(cluster_t *cluster, netadr_t *from, netmsg_t *m)
|
|||
if (!strncmp(buffer, "getchallenge", 12))
|
||||
{
|
||||
i = NewChallenge(from);
|
||||
Netchan_OutOfBandPrint(cluster, cluster->qwdsocket, *from, "c%i", i);
|
||||
Netchan_OutOfBandPrint(cluster, *from, "c%i", i);
|
||||
return;
|
||||
}
|
||||
if (!strncmp(buffer, "connect 28 ", 11))
|
||||
{
|
||||
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
|
||||
NewQWClient(cluster, from, buffer);
|
||||
return;
|
||||
|
@ -2032,7 +1980,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
|
|||
|
||||
if (tv)
|
||||
{
|
||||
if (tv->physicstime != v->settime && tv->cluster->chokeonnotupdated)
|
||||
if (tv->physicstime != v->settime)// && tv->cluster->chokeonnotupdated)
|
||||
{
|
||||
WriteByte(msg, svc_updatestatlong);
|
||||
WriteByte(msg, STAT_TIME);
|
||||
|
@ -2117,7 +2065,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
|
|||
WriteByte(msg, i);
|
||||
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[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)
|
||||
|
@ -2145,7 +2093,7 @@ void SendPlayerStates(sv_t *tv, viewer_t *v, netmsg_t *msg)
|
|||
}
|
||||
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[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"
|
||||
"Commands:\n"
|
||||
".bind\n"
|
||||
" Bind your keys to match Qizmo.\n"
|
||||
" Bind your keys to drive the menu.\n"
|
||||
".clients\n"
|
||||
" Lists the users connected to this\n"
|
||||
" proxy.\n"
|
||||
|
@ -4176,19 +4124,204 @@ void SendViewerPackets(cluster_t *cluster, viewer_t *v)
|
|||
// else
|
||||
// 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)
|
||||
{
|
||||
char buffer[MAX_MSGLEN]; //contains read info
|
||||
char tempbuffer[256];
|
||||
netadr_t from;
|
||||
int fromsize = sizeof(from);
|
||||
int fromsize = sizeof(from.sockaddr);
|
||||
int read;
|
||||
int qport;
|
||||
netmsg_t m;
|
||||
int socketno;
|
||||
tcpconnect_t *tc, **l;
|
||||
|
||||
viewer_t *v, *f;
|
||||
sv_t *useserver;
|
||||
|
||||
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++;
|
||||
if (socketno >= SOCKETGROUPS)
|
||||
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.
|
||||
{
|
||||
socketno++;
|
||||
if (socketno >= SOCKETGROUPS)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (read <= 5) //otherwise it's a runt or bad.
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m.cursize = read;
|
||||
m.data = buffer;
|
||||
m.readpos = 0;
|
||||
|
||||
if (*(int*)buffer == -1)
|
||||
{ //connectionless message
|
||||
ConnectionlessPacket(cluster, &from, &m);
|
||||
QW_ProcessUDPPacket(cluster, &m, from);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 (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)
|
||||
{
|
||||
//NQ connectionless packet?
|
||||
/*if it really is a webclient connection, then the stream will be packetized already
|
||||
so we don't waste extra space*/
|
||||
m.data = tc->inbuffer;
|
||||
m.readpos = 0;
|
||||
read = ReadLong(&m);
|
||||
read = SwapLong(read);
|
||||
if (read & 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);
|
||||
m.cursize = read;
|
||||
|
||||
QW_ProcessUDPPacket(cluster, &m, tc->peeraddr);
|
||||
|
||||
memmove(tc->inbuffer, tc->inbuffer+read, tc->inbuffersize - (read));
|
||||
tc->inbuffersize -= read;
|
||||
continue; //ask to read the next packet
|
||||
}
|
||||
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;
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
SOCKET s;
|
||||
char *newval = Cmd_Argv(ctx, 1);
|
||||
netadr_t addr;
|
||||
|
||||
|
@ -391,41 +392,17 @@ void Cmd_Master(cmdctxt_t *ctx)
|
|||
strlcpy(ctx->cluster->master, newval, sizeof(ctx->cluster->master));
|
||||
ctx->cluster->mastersendtime = ctx->cluster->curtime;
|
||||
|
||||
if (ctx->cluster->qwdsocket != INVALID_SOCKET)
|
||||
NET_SendPacket (ctx->cluster, NET_ChooseSocket(ctx->cluster->qwdsocket, &addr), 1, "k", addr);
|
||||
s = NET_ChooseSocket(ctx->cluster->qwdsocket, &addr);
|
||||
if (s != INVALID_SOCKET)
|
||||
NET_SendPacket (ctx->cluster, s, 1, "k", addr);
|
||||
Cmd_Printf(ctx, "Master server set.\n");
|
||||
}
|
||||
|
||||
void Cmd_UDPPort(cmdctxt_t *ctx)
|
||||
{
|
||||
int news;
|
||||
int newp = atoi(Cmd_Argv(ctx, 1));
|
||||
news = QW_InitUDPSocket(newp, false);
|
||||
if (news != INVALID_SOCKET)
|
||||
{
|
||||
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);
|
||||
NET_InitUDPSocket(ctx->cluster, newp, true);
|
||||
NET_InitUDPSocket(ctx->cluster, newp, false);
|
||||
}
|
||||
void Cmd_AdminPassword(cmdctxt_t *ctx)
|
||||
{
|
||||
|
@ -622,11 +599,11 @@ void Cmd_Status(cmdctxt_t *ctx)
|
|||
Cmd_Printf(ctx, " Talking allowed\n");
|
||||
if (ctx->cluster->nobsp)
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
@ -780,6 +757,39 @@ void Cmd_AllowNQ(cmdctxt_t *ctx)
|
|||
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)
|
||||
{
|
||||
char *val = Cmd_Argv(ctx, 1);
|
||||
|
@ -1305,6 +1315,8 @@ const rconcommands_t rconcommands[] =
|
|||
{"exit", 0, 1, Cmd_Quit},
|
||||
{"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"},
|
||||
{"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"},
|
||||
|
|
|
@ -168,9 +168,10 @@ dblbreak:
|
|||
else
|
||||
#endif
|
||||
{ //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);
|
||||
// strip off a trailing :port if present
|
||||
|
@ -178,12 +179,12 @@ dblbreak:
|
|||
if (*colon == ':')
|
||||
{
|
||||
*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.
|
||||
{
|
||||
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy);
|
||||
*(int *)&sin->sin_addr = inet_addr(copy);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -191,7 +192,7 @@ dblbreak:
|
|||
return 0;
|
||||
if (h->h_addrtype != AF_INET)
|
||||
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)
|
||||
{
|
||||
struct sockaddr_in *i1=(void*)s1, *i2=(void*)s2;
|
||||
if (i1->sin_family != i2->sin_family)
|
||||
struct sockaddr *g1=(void*)s1->sockaddr, *g2=(void*)s2->sockaddr;
|
||||
if (g1->sa_family != g2->sa_family)
|
||||
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)
|
||||
return false;
|
||||
if (i1->sin_port != i2->sin_port && qp1 != qp2) //allow qports to match instead of ports, if required.
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -259,7 +280,7 @@ SOCKET Net_TCPListen(int port, qboolean ipv6)
|
|||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
if( bind (sock, address, addrsize) == -1)
|
||||
if (bind (sock, address, addrsize) == -1)
|
||||
{
|
||||
printf("socket bind error %i (%s)\n", qerrno, strerror(qerrno));
|
||||
closesocket(sock);
|
||||
|
@ -420,7 +441,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
|
|||
strcpy(qtv->status, "Unable to resolve server\n");
|
||||
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);
|
||||
asz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));
|
||||
qtv->sourcesock = socket(pfam, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
@ -448,7 +469,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip)
|
|||
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;
|
||||
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);
|
||||
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);
|
||||
asz = ((afam==AF_INET6)?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in));
|
||||
qtv->sourcesock = socket(pfam, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
@ -899,8 +920,6 @@ qboolean Net_ReadStream(sv_t *qtv)
|
|||
return true;
|
||||
}
|
||||
|
||||
#define BUFFERTIME 10 //secords for artificial delay, so we can buffer things properly.
|
||||
|
||||
unsigned int Sys_Milliseconds(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
@ -1100,7 +1119,7 @@ qboolean QTV_Connect(sv_t *qtv, char *serverurl)
|
|||
}
|
||||
else
|
||||
{
|
||||
qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000;
|
||||
qtv->parsetime = Sys_Milliseconds() + qtv->cluster->anticheattime;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1379,11 +1398,12 @@ void QTV_ParseQWStream(sv_t *qtv)
|
|||
unsigned int fromlen;
|
||||
int readlen;
|
||||
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 (;;)
|
||||
{
|
||||
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)
|
||||
{
|
||||
//FIXME: Check for error
|
||||
|
@ -2015,9 +2035,9 @@ void QTV_Run(sv_t *qtv)
|
|||
return;
|
||||
}
|
||||
|
||||
qtv->parsetime = Sys_Milliseconds() + BUFFERTIME*1000;
|
||||
qtv->parsetime = Sys_Milliseconds() + qtv->cluster->anticheattime;
|
||||
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);
|
||||
}
|
||||
|
@ -2030,9 +2050,9 @@ void QTV_Run(sv_t *qtv)
|
|||
{ //not enough stuff to play.
|
||||
if (qtv->parsetime < qtv->curtime)
|
||||
{
|
||||
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
|
||||
if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||
QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||
qtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;
|
||||
// if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||
// QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2045,9 +2065,9 @@ void QTV_Run(sv_t *qtv)
|
|||
length = 10;
|
||||
if (qtv->buffersize < length)
|
||||
{ //not enough stuff to play.
|
||||
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
|
||||
if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||
QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||
qtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;
|
||||
// if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||
// QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||
continue;
|
||||
}
|
||||
qtv->parsetime += buffer[0]; //well this was pointless
|
||||
|
@ -2072,9 +2092,9 @@ void QTV_Run(sv_t *qtv)
|
|||
|
||||
if (qtv->buffersize < lengthofs+4)
|
||||
{ //the size parameter doesn't fit.
|
||||
if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||
QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
|
||||
// if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||
// QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||
qtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2101,9 +2121,9 @@ void QTV_Run(sv_t *qtv)
|
|||
|
||||
if (length+lengthofs+4 > qtv->buffersize)
|
||||
{
|
||||
if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||
QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||
qtv->parsetime = qtv->curtime + 2*1000; //add two seconds
|
||||
// if (qtv->sourcefile || qtv->sourcesock != INVALID_SOCKET)
|
||||
// QTV_Printf(qtv, "Stream %i: Not enough buffered\n", qtv->streamid);
|
||||
qtv->parsetime = qtv->curtime + qtv->cluster->tooslowdelay; //add two seconds
|
||||
break; //can't parse it yet.
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue