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:
Spoike 2012-04-20 14:41:40 +00:00
parent 0e77fa7386
commit 6385756e75
10 changed files with 958 additions and 316 deletions

View file

@ -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)
{

View file

@ -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"
>

View file

@ -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[0] == '\n' && s[1] == '\r' && s[2] == '\n')
{
s += 3;
eoh = true;
break;
}
}
if (!*s)
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;

View file

@ -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);

View file

@ -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);
}
/*

View file

@ -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);

View file

@ -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);

View file

@ -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;
}
from.tcpcon = NULL;
read = recvfrom(cluster->qwdsocket[socketno], buffer, sizeof(buffer), 0, (struct sockaddr*)&from.sockaddr, (unsigned*)&fromsize);
if (read < 0) //it's bad.
{
socketno++;
if (socketno >= SOCKETGROUPS)
break;
continue;
}
read = recvfrom(cluster->qwdsocket[socketno], buffer, sizeof(buffer), 0, (struct sockaddr*)&from, (unsigned*)&fromsize);
if (read <= 5) //otherwise it's a runt or bad.
{
if (read < 0) //it's bad.
{
socketno++;
if (socketno >= SOCKETGROUPS)
break;
}
continue;
}
m.cursize = read;
m.data = buffer;
m.readpos = 0;
if (*(int*)buffer == -1)
{ //connectionless message
ConnectionlessPacket(cluster, &from, &m);
continue;
}
QW_ProcessUDPPacket(cluster, &m, from);
}
if (read < 10) //otherwise it's a runt or bad.
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) //it's bad.
break;
if (read == 0 || qerrno != EWOULDBLOCK)
{
*l = tc->next;
closesocket(tc->sock);
free(tc);
continue;
}
}
if (clen >= 0)
{
/*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;
m.cursize = read;
qport = 0;
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
{
//read the qport
ReadLong(&m);
ReadLong(&m);
qport = ReadShort(&m);
}
for (v = cluster->viewers; v; v = v->next)
{
if (v->netchan.isnqprotocol)
while (tc->inbuffersize >= 2)
{
if (Net_CompareAddress(&v->netchan.remote_address, &from, 0, 0))
read = (tc->inbuffer[0]<<8) | tc->inbuffer[1];
if (tc->inbuffersize >= 2+read)
{
if (NQNetchan_Process(cluster, &v->netchan, &m))
{
useserver = v->server;
if (useserver && useserver->parsingconnectiondata)
useserver = NULL;
m.data = tc->inbuffer+2;
m.readpos = 0;
m.cursize = read;
v->timeout = cluster->curtime + 15*1000;
QW_ProcessUDPPacket(cluster, &m, tc->peeraddr);
ParseNQC(cluster, useserver, v, &m);
if (v->server && v->server->controller == v)
{
QTV_Run(v->server);
}
}
memmove(tc->inbuffer, tc->inbuffer+2+read, tc->inbuffersize - (2+read));
tc->inbuffersize -= 2+read;
}
}
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);
}
}
else
break;
}
}
}
if (!v && cluster->allownqclients)
{
//NQ connectionless packet?
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);
}
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);

View file

@ -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"},

View file

@ -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,16 +201,36 @@ 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)
{
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_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.
}