Add some sha2 stuff to qtv connections. Fix up 'qtvplay' command to support passwords.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6073 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
8ae9fe1141
commit
7404d81d0b
5 changed files with 446 additions and 391 deletions
|
@ -2551,258 +2551,9 @@ void CL_Demo_ClientCommand(char *commandtext)
|
|||
}
|
||||
}
|
||||
|
||||
static char qtvhostname[1024];
|
||||
static char qtvrequestbuffer[4096];
|
||||
static size_t qtvrequestsize;
|
||||
static char qtvrequestcmdbuffer[4096];
|
||||
static int qtvrequestcmdsize;
|
||||
static vfsfile_t *qtvrequest;
|
||||
|
||||
void CL_QTVPoll (void)
|
||||
{
|
||||
char *s, *e, *colon;
|
||||
char *tail = NULL;
|
||||
int len;
|
||||
qboolean streamavailable = false;
|
||||
qboolean saidheader = false;
|
||||
#ifndef NOBUILTINMENUS
|
||||
emenu_t *sourcesmenu = NULL;
|
||||
#endif
|
||||
int sourcenum = 0;
|
||||
|
||||
int streamid;
|
||||
int numplayers = 0;
|
||||
int numviewers = 0;
|
||||
qboolean init_numplayers = false;
|
||||
qboolean init_numviewers = false;
|
||||
qboolean iseztv = false;
|
||||
char srchost[256];
|
||||
char auth[64];
|
||||
char challenge[128];
|
||||
|
||||
if (!qtvrequest)
|
||||
return;
|
||||
|
||||
if (qtvrequestcmdsize)
|
||||
{
|
||||
len = VFS_WRITE(qtvrequest, qtvrequestcmdbuffer, qtvrequestcmdsize);
|
||||
if (len > 0)
|
||||
{
|
||||
memmove(qtvrequestcmdbuffer, qtvrequestcmdbuffer+len, qtvrequestcmdsize-len);
|
||||
qtvrequestcmdsize -= len;
|
||||
}
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
len = VFS_READ(qtvrequest, qtvrequestbuffer+qtvrequestsize, (sizeof(qtvrequestbuffer) - qtvrequestsize -1 > 0)?1:0);
|
||||
if (len <= 0)
|
||||
break;
|
||||
qtvrequestsize += len;
|
||||
}
|
||||
qtvrequestbuffer[qtvrequestsize] = '\0';
|
||||
|
||||
if (qtvrequestsize >= sizeof(qtvrequestbuffer) - 1)
|
||||
{
|
||||
//flag it as an error if the response is larger than we can handle.
|
||||
//this error gets ignored if the header is okay (any actual errors will get reported again by the demo code anyway), and only counts if the end of the reply header was not found.
|
||||
len = -1;
|
||||
}
|
||||
if (!qtvrequestsize && len == 0)
|
||||
return;
|
||||
|
||||
//make sure it's a compleate chunk.
|
||||
for (s = qtvrequestbuffer; *s; s++)
|
||||
{
|
||||
if (s[0] == '\n' && s[1] == '\n')
|
||||
{
|
||||
tail = s+2;
|
||||
break;
|
||||
}
|
||||
if (s[0] == '\r' && s[1] == '\n' && s[2] == '\r' && s[3] == '\n')
|
||||
{
|
||||
tail = s+4;
|
||||
break;
|
||||
}
|
||||
if (s[0] == '\r' && s[1] == '\n' && s[2] == '\n')
|
||||
{
|
||||
tail = s+3;
|
||||
break;
|
||||
}
|
||||
if (s[0] == '\n' && s[1] == '\r' && s[2] == '\n')
|
||||
{
|
||||
tail = s+3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!tail)
|
||||
{
|
||||
if (len < 0)
|
||||
{
|
||||
if (!qtvrequestsize)
|
||||
Con_Printf("Connection to QTV server closed without any reply.\n");
|
||||
else
|
||||
Con_Printf("invalid QTV handshake\n");
|
||||
SCR_SetLoadingStage(LS_NONE);
|
||||
VFS_CLOSE(qtvrequest);
|
||||
qtvrequest = NULL;
|
||||
qtvrequestsize = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
s[1] = '\0'; //make sure its null terminated before the data payload
|
||||
s = qtvrequestbuffer;
|
||||
|
||||
*auth = *challenge = 0;
|
||||
for (e = s; *e; )
|
||||
{
|
||||
if (*e == '\r')
|
||||
*e = '\0';
|
||||
else if (*e == '\n')
|
||||
{
|
||||
*e = '\0';
|
||||
colon = strchr(s, ':');
|
||||
if (colon)
|
||||
*colon++ = '\0';
|
||||
else
|
||||
colon = "";
|
||||
|
||||
if (!strcmp(s, "PERROR"))
|
||||
{ //permanent printable error
|
||||
Con_Printf("QTV Error:\n%s\n", colon);
|
||||
}
|
||||
else if (!strcmp(s, "PRINT"))
|
||||
{ //printable error
|
||||
Con_Printf("QTV:\n%s\n", colon);
|
||||
}
|
||||
else if (!strcmp(s, "TERROR"))
|
||||
{ //temporary printable error
|
||||
Con_Printf("QTV Error:\n%s\n", colon);
|
||||
}
|
||||
else if (!strcmp(s, "ADEMO"))
|
||||
{ //printable error
|
||||
Con_Printf("Demo%s is available\n", colon);
|
||||
}
|
||||
else if (!strcmp(s, "AUTH"))
|
||||
{
|
||||
while (*colon && *(unsigned char*)colon <= ' ')
|
||||
colon++;
|
||||
Q_strncpyz(auth, colon, sizeof(auth));
|
||||
}
|
||||
else if (!strcmp(s, "CHALLENGE"))
|
||||
Q_strncpyz(challenge, colon, sizeof(challenge));
|
||||
|
||||
//generic sourcelist responce
|
||||
else if (!strcmp(s, "ASOURCE"))
|
||||
{ //printable source
|
||||
if (!saidheader)
|
||||
{
|
||||
saidheader=true;
|
||||
Con_Printf("Available Sources:\n");
|
||||
}
|
||||
Con_Printf("%s\n", colon);
|
||||
//we're too lazy to even try and parse this
|
||||
}
|
||||
|
||||
else if (!strcmp(s, "BEGIN"))
|
||||
{
|
||||
while (*colon && *(unsigned char*)colon <= ' ')
|
||||
colon++;
|
||||
if (*colon)
|
||||
Con_Printf("streaming \"%s\" from qtv\n", colon);
|
||||
else
|
||||
Con_Printf("qtv connection established to %s\n", qtvhostname);
|
||||
streamavailable = true;
|
||||
}
|
||||
|
||||
//eztv extensions to v1.0
|
||||
else if (!strcmp(s, "QTV_EZQUAKE_EXT"))
|
||||
{
|
||||
iseztv = true;
|
||||
Con_Printf("Warning: eztv extensions %s\n", colon);
|
||||
}
|
||||
|
||||
//v1.1 sourcelist response includes SRCSRV, SRCHOST, SRCPLYRS, SRCVIEWS, SRCID
|
||||
else if (!strcmp(s, "SRCSRV"))
|
||||
{
|
||||
//the proxy's source string (beware of file:blah without file:blah@blah)
|
||||
}
|
||||
else if (!strcmp(s, "SRCHOST"))
|
||||
{
|
||||
//the hostname from the server the stream came from
|
||||
Q_strncpyz(srchost, colon, sizeof(srchost));
|
||||
}
|
||||
else if (!strcmp(s, "SRCPLYRS"))
|
||||
{
|
||||
//number of active players actually playing on that stream
|
||||
numplayers = atoi(colon);
|
||||
init_numplayers = true;
|
||||
}
|
||||
else if (!strcmp(s, "SRCVIEWS"))
|
||||
{
|
||||
//number of people watching this stream on the proxy itself
|
||||
numviewers = atoi(colon);
|
||||
init_numviewers = true;
|
||||
}
|
||||
else if (!strcmp(s, "SRCID"))
|
||||
{
|
||||
streamid = atoi(colon);
|
||||
|
||||
#ifndef NOBUILTINMENUS
|
||||
//now put it on a menu
|
||||
if (!sourcesmenu)
|
||||
{
|
||||
sourcesmenu = M_CreateMenu(0);
|
||||
|
||||
MC_AddPicture(sourcesmenu, 16, 4, 32, 144, "gfx/qplaque.lmp");
|
||||
MC_AddCenterPicture(sourcesmenu, 4, 24, "gfx/p_option.lmp");
|
||||
}
|
||||
if (init_numplayers == true && init_numviewers == true)
|
||||
MC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", srchost, numplayers, numviewers), va("qtvplay %i@%s\n", streamid, qtvhostname));
|
||||
//else
|
||||
// FIXME: add error message here
|
||||
#else
|
||||
(void)init_numviewers;
|
||||
(void)numviewers;
|
||||
(void)init_numplayers;
|
||||
(void)numplayers;
|
||||
(void)streamid;
|
||||
(void)sourcenum;
|
||||
#endif
|
||||
}
|
||||
//end of sourcelist entry
|
||||
|
||||
//from e to s, we have a line
|
||||
s = e+1;
|
||||
}
|
||||
e++;
|
||||
}
|
||||
|
||||
if (streamavailable)
|
||||
{
|
||||
CL_PlayDemoStream(qtvrequest, NULL, false, iseztv?DPB_EZTV:DPB_MVD, BUFFERTIME);
|
||||
qtvrequest = NULL;
|
||||
demo_resetcache(qtvrequestsize - (tail-qtvrequestbuffer), tail);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(auth, "NONE"))
|
||||
;
|
||||
// else if (!strcmp(auth, "PLAIN"))
|
||||
// else if (!strcmp(auth, "MD4"))
|
||||
// else if (!strcmp(auth, "SHA1"))
|
||||
else if (*auth)
|
||||
Con_Printf("Server requires unsupported auth method: %s\n", auth);
|
||||
|
||||
SCR_SetLoadingStage(LS_NONE);
|
||||
VFS_CLOSE(qtvrequest);
|
||||
qtvrequest = NULL;
|
||||
qtvrequestsize = 0;
|
||||
}
|
||||
|
||||
char *strchrrev(char *str, char chr)
|
||||
{
|
||||
char *firstchar = str;
|
||||
const char *firstchar = str;
|
||||
for (str = str + strlen(str)-1; str>=firstchar; str--)
|
||||
if (*str == chr)
|
||||
return str;
|
||||
|
@ -2978,58 +2729,343 @@ void CL_ParseQTVDescriptor(vfsfile_t *f, const char *name)
|
|||
}
|
||||
|
||||
#include "netinc.h"
|
||||
void CL_QTVPlay_f (void)
|
||||
{
|
||||
qboolean raw=0;
|
||||
char *connrequest;
|
||||
vfsfile_t *newf;
|
||||
char *host;
|
||||
char msg[4096];
|
||||
int msglen=0;
|
||||
char *password;
|
||||
|
||||
if (Cmd_Argc() < 2)
|
||||
static struct pendingqtv_s
|
||||
{
|
||||
struct pendingqtv_s *next;
|
||||
qboolean raw;
|
||||
char hostname[1024];
|
||||
char password[1024];
|
||||
char requestbuffer[4096];
|
||||
size_t requestsize;
|
||||
char requestcmdbuffer[4096];
|
||||
int requestcmdsize;
|
||||
vfsfile_t *stream;
|
||||
|
||||
char postauth[1];
|
||||
} *pendingqtv;
|
||||
|
||||
void CL_QTVPoll (void)
|
||||
{
|
||||
struct pendingqtv_s **link, *qtv;
|
||||
for (link = &pendingqtv; (qtv = *link); link = &qtv->next)
|
||||
{
|
||||
Con_Printf("Usage: qtvplay [stream@][tls://]hostname[:port] [password]\n");
|
||||
char *s, *e, *colon;
|
||||
char *tail = NULL;
|
||||
int len;
|
||||
char *streamavailable = NULL;
|
||||
qboolean saidheader = false;
|
||||
#ifndef NOBUILTINMENUS
|
||||
emenu_t *sourcesmenu = NULL;
|
||||
#endif
|
||||
int sourcenum = 0;
|
||||
|
||||
int numplayers = 0;
|
||||
int numviewers = 0;
|
||||
qboolean init_numplayers = false;
|
||||
qboolean init_numviewers = false;
|
||||
qboolean iseztv = false;
|
||||
char srchost[256];
|
||||
char auth[64];
|
||||
char challenge[128];
|
||||
hashfunc_t *hashfunc = NULL;
|
||||
|
||||
//try to finish sending
|
||||
if (qtv->requestcmdsize)
|
||||
{
|
||||
len = VFS_WRITE(qtv->stream, qtv->requestcmdbuffer, qtv->requestcmdsize);
|
||||
if (len > 0)
|
||||
{
|
||||
memmove(qtv->requestcmdbuffer, qtv->requestcmdbuffer+len, qtv->requestcmdsize-len);
|
||||
qtv->requestcmdsize -= len;
|
||||
}
|
||||
if (len < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
len = VFS_READ(qtv->stream, qtv->requestbuffer+qtv->requestsize, (sizeof(qtv->requestbuffer) - qtv->requestsize -1 > 0)?1:0);
|
||||
if (len <= 0)
|
||||
break;
|
||||
qtv->requestsize += len;
|
||||
}
|
||||
qtv->requestbuffer[qtv->requestsize] = '\0';
|
||||
|
||||
if (qtv->raw)
|
||||
{
|
||||
tail = qtv->requestbuffer;
|
||||
streamavailable = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (qtv->requestsize >= sizeof(qtv->requestbuffer) - 1)
|
||||
{
|
||||
//flag it as an error if the response is larger than we can handle.
|
||||
//this error gets ignored if the header is okay (any actual errors will get reported again by the demo code anyway), and only counts if the end of the reply header was not found.
|
||||
len = -1;
|
||||
}
|
||||
if (!qtv->requestsize && len == 0)
|
||||
continue; //still trying.
|
||||
|
||||
//make sure it's a compleate chunk.
|
||||
for (s = qtv->requestbuffer; *s; s++)
|
||||
{
|
||||
if (s[0] == '\n' && s[1] == '\n')
|
||||
{
|
||||
tail = s+2;
|
||||
break;
|
||||
}
|
||||
if (s[0] == '\r' && s[1] == '\n' && s[2] == '\r' && s[3] == '\n')
|
||||
{
|
||||
tail = s+4;
|
||||
break;
|
||||
}
|
||||
if (s[0] == '\r' && s[1] == '\n' && s[2] == '\n')
|
||||
{
|
||||
tail = s+3;
|
||||
break;
|
||||
}
|
||||
if (s[0] == '\n' && s[1] == '\r' && s[2] == '\n')
|
||||
{
|
||||
tail = s+3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!tail)
|
||||
{
|
||||
if (len < 0)
|
||||
{
|
||||
if (!qtv->requestsize)
|
||||
Con_Printf("Connection to QTV server closed without any reply.\n");
|
||||
else
|
||||
Con_Printf("invalid QTV handshake\n");
|
||||
fail:
|
||||
SCR_SetLoadingStage(LS_NONE);
|
||||
if (qtv->stream)
|
||||
VFS_CLOSE(qtv->stream);
|
||||
qtv->stream = NULL;
|
||||
qtv->requestsize = 0;
|
||||
*link = qtv->next;
|
||||
Z_Free(qtv);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
s = qtv->requestbuffer;
|
||||
|
||||
colon = "";
|
||||
*auth = *challenge = 0;
|
||||
for (e = s; e < tail; )
|
||||
{
|
||||
if (*e == '\r')
|
||||
*e = '\0';
|
||||
else if (*e == '\n')
|
||||
{
|
||||
*e = '\0';
|
||||
colon = strchr(s, ':');
|
||||
if (colon)
|
||||
{
|
||||
*colon++ = '\0';
|
||||
if (*colon && *(unsigned char*)colon <= ' ')
|
||||
colon++;
|
||||
}
|
||||
else
|
||||
colon = "";
|
||||
|
||||
if (!strcmp(s, "PERROR"))
|
||||
{ //permanent printable error
|
||||
Con_Printf("QTV Error:\n%s\n", colon);
|
||||
}
|
||||
else if (!strcmp(s, "PRINT"))
|
||||
{ //printable error
|
||||
Con_Printf("QTV:\n%s\n", colon);
|
||||
}
|
||||
else if (!strcmp(s, "TERROR"))
|
||||
{ //temporary printable error
|
||||
Con_Printf("QTV Error:\n%s\n", colon);
|
||||
}
|
||||
else if (!strcmp(s, "ADEMO"))
|
||||
{ //printable error
|
||||
Con_Printf("Demo%s is available\n", colon);
|
||||
}
|
||||
else if (!strcmp(s, "AUTH"))
|
||||
{
|
||||
while (*colon && *(unsigned char*)colon <= ' ')
|
||||
colon++;
|
||||
Q_strncpyz(auth, colon, sizeof(auth));
|
||||
}
|
||||
else if (!strcmp(s, "CHALLENGE"))
|
||||
{
|
||||
while (*colon && *(unsigned char*)colon <= ' ')
|
||||
colon++;
|
||||
Q_strncpyz(challenge, colon, sizeof(challenge));
|
||||
}
|
||||
//generic sourcelist responce
|
||||
else if (!strcmp(s, "ASOURCE"))
|
||||
{ //printable source
|
||||
if (!saidheader)
|
||||
{
|
||||
saidheader=true;
|
||||
Con_Printf("Available Sources:\n");
|
||||
}
|
||||
Con_Printf("%s\n", colon);
|
||||
//we're too lazy to even try and parse this
|
||||
}
|
||||
|
||||
else if (!strcmp(s, "BEGIN"))
|
||||
{
|
||||
while (*colon && *(unsigned char*)colon <= ' ')
|
||||
colon++;
|
||||
streamavailable = colon;
|
||||
}
|
||||
|
||||
//eztv extensions to v1.0
|
||||
else if (!strcmp(s, "QTV_EZQUAKE_EXT"))
|
||||
{
|
||||
iseztv = true;
|
||||
Con_Printf("Warning: eztv extensions %s\n", colon);
|
||||
}
|
||||
|
||||
//v1.1 sourcelist response includes SRCSRV, SRCHOST, SRCPLYRS, SRCVIEWS, SRCID
|
||||
else if (!strcmp(s, "SRCSRV"))
|
||||
{
|
||||
//the proxy's source string (beware of file:blah without file:blah@blah)
|
||||
}
|
||||
else if (!strcmp(s, "SRCHOST"))
|
||||
{
|
||||
//the hostname from the server the stream came from
|
||||
Q_strncpyz(srchost, colon, sizeof(srchost));
|
||||
}
|
||||
else if (!strcmp(s, "SRCPLYRS"))
|
||||
{
|
||||
//number of active players actually playing on that stream
|
||||
numplayers = atoi(colon);
|
||||
init_numplayers = true;
|
||||
}
|
||||
else if (!strcmp(s, "SRCVIEWS"))
|
||||
{
|
||||
//number of people watching this stream on the proxy itself
|
||||
numviewers = atoi(colon);
|
||||
init_numviewers = true;
|
||||
}
|
||||
else if (!strcmp(s, "SRCID") && !streamavailable)
|
||||
{
|
||||
char *streamid = colon;
|
||||
|
||||
#ifndef NOBUILTINMENUS
|
||||
//now put it on a menu
|
||||
if (!sourcesmenu)
|
||||
{
|
||||
sourcesmenu = M_CreateMenu(0);
|
||||
|
||||
MC_AddPicture(sourcesmenu, 16, 4, 32, 144, "gfx/qplaque.lmp");
|
||||
MC_AddCenterPicture(sourcesmenu, 4, 24, "gfx/p_option.lmp");
|
||||
}
|
||||
if (init_numplayers == true && init_numviewers == true)
|
||||
MC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", srchost, numplayers, numviewers), va("qtvplay %s@%s\n", streamid, qtv->hostname));
|
||||
//else
|
||||
// FIXME: add error message here
|
||||
#else
|
||||
(void)init_numviewers;
|
||||
(void)numviewers;
|
||||
(void)init_numplayers;
|
||||
(void)numplayers;
|
||||
(void)streamid;
|
||||
(void)sourcenum;
|
||||
#endif
|
||||
}
|
||||
//end of sourcelist entry
|
||||
|
||||
//from e to s, we have a line
|
||||
s = e+1;
|
||||
}
|
||||
e++;
|
||||
}
|
||||
|
||||
if (streamavailable)
|
||||
{
|
||||
if (*streamavailable)
|
||||
Con_Printf("streaming \"%s\" from qtv\n", streamavailable);
|
||||
else
|
||||
Con_Printf("qtv connection established to %s\n", qtv->hostname);
|
||||
CL_PlayDemoStream(qtv->stream, NULL, false, iseztv?DPB_EZTV:DPB_MVD, BUFFERTIME);
|
||||
qtv->stream = NULL;
|
||||
demo_resetcache(qtv->requestsize - (tail-qtv->requestbuffer), tail);
|
||||
*link = qtv->next;
|
||||
Z_Free(qtv);
|
||||
return;
|
||||
}
|
||||
|
||||
//something failed. if its giving us an auth type then we should be authing before sending our request...
|
||||
|
||||
if (!strcmp(auth, "NONE"))
|
||||
;
|
||||
// else if (!strcmp(auth, "PLAIN"))
|
||||
// else if (!strcmp(auth, "MD4"))
|
||||
else if (!strcmp(auth, "SHA1"))
|
||||
hashfunc = &hash_sha1;
|
||||
else if (!strcmp(auth, "SHA2_256"))
|
||||
hashfunc = &hash_sha256;
|
||||
else if (!strcmp(auth, "SHA2_512"))
|
||||
hashfunc = &hash_sha512;
|
||||
else if (*auth)
|
||||
Con_Printf("Server requires unsupported auth method: %s\n", auth);
|
||||
|
||||
qtv->requestsize -= tail-qtv->requestbuffer;
|
||||
memmove(qtv->requestbuffer, tail, qtv->requestsize);
|
||||
if (hashfunc && qtv->postauth)
|
||||
{
|
||||
if (*qtv->password)
|
||||
{
|
||||
char hash[DIGEST_MAXSIZE*2+1];
|
||||
qbyte digest[DIGEST_MAXSIZE];
|
||||
|
||||
Q_snprintfz(hash, sizeof(hash), "%s%s", challenge, qtv->password);
|
||||
CalcHash(hashfunc, digest, sizeof(digest), hash, strlen(hash));
|
||||
Base64_EncodeBlock(digest, hashfunc->digestsize, hash, sizeof(hash));
|
||||
|
||||
Q_snprintfz(qtv->requestcmdbuffer, sizeof(qtv->requestcmdbuffer),
|
||||
"QTV\n"
|
||||
"VERSION: 1.1\n"
|
||||
"AUTH: %s\n"
|
||||
"PASSWORD: \"%s\"\n"
|
||||
"%s\n",
|
||||
auth, hash, qtv->postauth);
|
||||
qtv->requestcmdsize = strlen(qtv->requestcmdbuffer);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
Con_Printf("QTV server requires a password\n");
|
||||
}
|
||||
|
||||
SCR_SetLoadingStage(LS_NONE);
|
||||
VFS_CLOSE(qtv->stream);
|
||||
qtv->stream = NULL;
|
||||
qtv->requestsize = 0;
|
||||
*link = qtv->next;
|
||||
Z_Free(qtv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
void CL_QTVPlay_Establish (const char *host, const char *password, const char *command)
|
||||
{
|
||||
struct pendingqtv_s *qtv = Z_Malloc(sizeof(*qtv) + strlen(command));
|
||||
char msg[4096];
|
||||
int msglen=0;
|
||||
|
||||
connrequest = Cmd_Argv(1);
|
||||
|
||||
/*if (*connrequest == '#')
|
||||
{
|
||||
//#FILENAME is a local system path
|
||||
CL_ParseQTVDescriptor(VFSOS_Open(connrequest+1, "rt"), connrequest+1);
|
||||
return;
|
||||
}*/
|
||||
strcpy(cls.servername, "qtv:");
|
||||
Q_strncpyz(cls.servername+4, connrequest, sizeof(cls.servername)-4);
|
||||
|
||||
SCR_SetLoadingStage(LS_CONNECTION);
|
||||
|
||||
host = connrequest;
|
||||
|
||||
connrequest = strchrrev(connrequest, '@');
|
||||
if (connrequest)
|
||||
host = connrequest+1;
|
||||
Q_strncpyz(qtvhostname, host, sizeof(qtvhostname));
|
||||
newf = FS_OpenTCP(qtvhostname, 27599, false);
|
||||
|
||||
|
||||
if (!newf)
|
||||
// SCR_SetLoadingStage(LS_CONNECTION);
|
||||
qtv->stream = FS_OpenTCP(host, 27599, false);
|
||||
if (!qtv->stream)
|
||||
{
|
||||
SCR_SetLoadingStage(LS_NONE);
|
||||
Con_Printf("Couldn't connect to proxy\n");
|
||||
Z_Free(qtv);
|
||||
return;
|
||||
}
|
||||
|
||||
host = Cmd_Argv(1);
|
||||
if (connrequest)
|
||||
*connrequest = '\0';
|
||||
else
|
||||
host = NULL;
|
||||
|
||||
password = Cmd_Argv(2);
|
||||
Q_strncpyz(qtv->password, password, sizeof(qtv->password));
|
||||
|
||||
if (qtvcl_forceversion1.ival)
|
||||
{
|
||||
|
@ -3045,23 +3081,31 @@ void CL_QTVPlay_f (void)
|
|||
}
|
||||
msglen += strlen(msg+msglen);
|
||||
|
||||
if (password)
|
||||
if (*password)
|
||||
{
|
||||
#if 0
|
||||
//just send it directly, we can't handle the tripple handshake for the challenge info
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
|
||||
"AUTH: PLAIN\n"
|
||||
"PASSWORD: %s\n"
|
||||
, password);
|
||||
#else
|
||||
//report supported auth methods to the server. it'll pick one and send us a challenge.
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
|
||||
// "AUTH: SHA1\n"
|
||||
// "AUTH: MD4\n"
|
||||
// "AUTH: CCITT\n"
|
||||
"AUTH: PLAIN\n");
|
||||
#endif
|
||||
if (qtv->raw)
|
||||
{
|
||||
//just send it directly, we can't handle any kind of response and that includes the tripple handshake for the challenge info
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
|
||||
"AUTH: PLAIN\n"
|
||||
"PASSWORD: %s\n"
|
||||
, password);
|
||||
}
|
||||
else
|
||||
{
|
||||
//report supported auth methods to the server. it'll pick one and send us a challenge.
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
|
||||
"AUTH: SHA2_512\n"
|
||||
"AUTH: SHA2_256\n"
|
||||
"AUTH: SHA1\n"
|
||||
// "AUTH: MD4\n"
|
||||
// "AUTH: CCITT\n"
|
||||
// "AUTH: PLAIN\n"
|
||||
);
|
||||
}
|
||||
msglen += strlen(msg+msglen);
|
||||
|
||||
strcpy(qtv->postauth, command);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3070,15 +3114,59 @@ void CL_QTVPlay_f (void)
|
|||
"AUTH: NONE\n"
|
||||
"");
|
||||
msglen += strlen(msg+msglen);
|
||||
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen, "%s", command);
|
||||
msglen += strlen(msg+msglen);
|
||||
*qtv->postauth = 0;
|
||||
}
|
||||
|
||||
if (raw)
|
||||
{
|
||||
if (qtv->raw)
|
||||
{ //peer must either disconnect instantly, or respond with an mvd file without extra headers.
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
|
||||
"RAW: 1\n");
|
||||
msglen += strlen(msg+msglen);
|
||||
}
|
||||
else if (host)
|
||||
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
|
||||
"\n");
|
||||
msglen += strlen(msg+msglen);
|
||||
|
||||
|
||||
memcpy(qtv->requestcmdbuffer, msg, msglen);
|
||||
qtv->requestcmdsize = msglen;
|
||||
qtv->requestsize = 0;
|
||||
|
||||
//and link it in.
|
||||
qtv->next = pendingqtv;
|
||||
pendingqtv = qtv;
|
||||
}
|
||||
|
||||
void CL_QTVPlay_f (void)
|
||||
{
|
||||
char *host;
|
||||
const char *password;
|
||||
char *streamid;
|
||||
char msg[4096];
|
||||
int msglen=0;
|
||||
|
||||
if (Cmd_Argc() < 2)
|
||||
{
|
||||
Con_Printf("Usage: qtvplay [stream@][tls://]hostname[:port] [password]\n");
|
||||
return;
|
||||
}
|
||||
|
||||
streamid = Cmd_Argv(1);
|
||||
password = Cmd_Argv(2);
|
||||
host = strchrrev(streamid, '@');
|
||||
if (host)
|
||||
*host++ = 0;
|
||||
else
|
||||
{
|
||||
host = streamid;
|
||||
streamid = NULL;
|
||||
}
|
||||
|
||||
if (streamid)
|
||||
{
|
||||
if (qtvcl_eztvextensions.ival)
|
||||
{
|
||||
|
@ -3093,95 +3181,27 @@ void CL_QTVPlay_f (void)
|
|||
}
|
||||
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
|
||||
"SOURCE: %s\n", host);
|
||||
"SOURCE: %s\n", streamid);
|
||||
msglen += strlen(msg+msglen);
|
||||
|
||||
SCR_SetLoadingStage(LS_CONNECTION);
|
||||
CL_QTVPlay_Establish(host, password, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
|
||||
"SOURCELIST\n");
|
||||
msglen += strlen(msg+msglen);
|
||||
CL_QTVPlay_Establish(host, password, "SOURCELIST\n");
|
||||
}
|
||||
|
||||
Q_snprintfz(msg+msglen, sizeof(msg)-msglen,
|
||||
"\n");
|
||||
msglen += strlen(msg+msglen);
|
||||
|
||||
if (raw)
|
||||
{
|
||||
VFS_WRITE(newf, msg, msglen);
|
||||
CL_PlayDemoStream(qtvrequest, qtvhostname, false, DPB_MVD, BUFFERTIME);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (qtvrequest)
|
||||
VFS_CLOSE(qtvrequest);
|
||||
|
||||
memcpy(qtvrequestcmdbuffer, msg, msglen);
|
||||
qtvrequestcmdsize = msglen;
|
||||
qtvrequest = newf;
|
||||
qtvrequestsize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CL_QTVList_f (void)
|
||||
{
|
||||
char *connrequest;
|
||||
vfsfile_t *newf;
|
||||
newf = FS_OpenTCP(qtvhostname, 27599, false);
|
||||
|
||||
if (!newf)
|
||||
{
|
||||
Con_Printf("Couldn't connect to proxy\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (qtvcl_forceversion1.ival)
|
||||
{
|
||||
connrequest = "QTV\n"
|
||||
"VERSION: 1.0\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
connrequest = "QTV\n"
|
||||
"VERSION: 1.1\n";
|
||||
}
|
||||
VFS_WRITE(newf, connrequest, strlen(connrequest));
|
||||
connrequest = "SOURCELIST\n";
|
||||
VFS_WRITE(newf, connrequest, strlen(connrequest));
|
||||
connrequest = "\n";
|
||||
VFS_WRITE(newf, connrequest, strlen(connrequest));
|
||||
|
||||
if (qtvrequest)
|
||||
VFS_CLOSE(qtvrequest);
|
||||
qtvrequest = newf;
|
||||
qtvrequestsize = 0;
|
||||
CL_QTVPlay_Establish(Cmd_Argv(1), Cmd_Argv(2), "SOURCELIST\n");
|
||||
}
|
||||
|
||||
void CL_QTVDemos_f (void)
|
||||
{
|
||||
char *connrequest;
|
||||
vfsfile_t *newf;
|
||||
newf = FS_OpenTCP(Cmd_Argv(1), 27599, false);
|
||||
|
||||
if (!newf)
|
||||
{
|
||||
Con_Printf("Couldn't connect to proxy\n");
|
||||
return;
|
||||
}
|
||||
|
||||
connrequest = "QTV\n"
|
||||
"VERSION: 1\n";
|
||||
VFS_WRITE(newf, connrequest, strlen(connrequest));
|
||||
connrequest = "DEMOLIST\n";
|
||||
VFS_WRITE(newf, connrequest, strlen(connrequest));
|
||||
connrequest = "\n";
|
||||
VFS_WRITE(newf, connrequest, strlen(connrequest));
|
||||
|
||||
if (qtvrequest)
|
||||
VFS_CLOSE(qtvrequest);
|
||||
qtvrequest = newf;
|
||||
qtvrequestsize = 0;
|
||||
CL_QTVPlay_Establish(Cmd_Argv(1), Cmd_Argv(2), "DEMOLIST\n");
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -4125,7 +4125,7 @@ typedef struct ftenet_tcp_connection_s {
|
|||
ftenet_tcp_stream_t *tcpstreams;
|
||||
} ftenet_tcp_connection_t;
|
||||
|
||||
void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen)
|
||||
void tobase64(unsigned char *out, int outlen, const unsigned char *in, int inlen)
|
||||
{
|
||||
static unsigned char tab[64] =
|
||||
{
|
||||
|
|
|
@ -272,11 +272,11 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
|
|||
QTVAM_PLAIN,
|
||||
#ifdef TCPCONNECT
|
||||
// QTVAM_CCITT, //16bit = ddos it
|
||||
QTVAM_MD4, //fucked
|
||||
QTVAM_MD4, //fucked. required for eztv compat
|
||||
// QTVAM_MD5, //fucked, no hash implemented
|
||||
QTVAM_SHA1, //fucked too nowadays
|
||||
// QTVAM_SHA2_256, //no hash implemented
|
||||
// QTVAM_SHA2_512, //no hash implemented
|
||||
QTVAM_SHA2_256, //
|
||||
QTVAM_SHA2_512, //
|
||||
#endif
|
||||
} authmethod = QTVAM_NONE;
|
||||
|
||||
|
@ -360,6 +360,10 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
|
|||
// thisauth = QTVAM_MD5;
|
||||
else if (!strcmp(com_token, "SHA1"))
|
||||
thisauth = QTVAM_SHA1;
|
||||
else if (!strcmp(com_token, "SHA2_256"))
|
||||
thisauth = QTVAM_SHA2_256;
|
||||
else if (!strcmp(com_token, "SHA2_512"))
|
||||
thisauth = QTVAM_SHA2_512;
|
||||
#endif
|
||||
else
|
||||
{
|
||||
|
@ -412,6 +416,7 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
|
|||
p->hasauthed = true; //no password, no need to auth.
|
||||
else if (*password)
|
||||
{
|
||||
hashfunc_t *func = NULL;
|
||||
if (!*p->challenge && authmethod>QTVAM_PLAIN)
|
||||
e = ("QTVSV 1\n"
|
||||
"PERROR: Challenge wasn't given...\n\n");
|
||||
|
@ -445,6 +450,7 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
|
|||
p->hasauthed = !strcmp(password, hash);
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_LEGACY //to be disabled at some point.
|
||||
case QTVAM_SHA1:
|
||||
{
|
||||
char hash[512];
|
||||
|
@ -454,15 +460,33 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
|
|||
CalcHash(&hash_sha1, (char*)digest, sizeof(digest), hash, strlen(hash));
|
||||
Q_snprintfz(hash, sizeof(hash), "%08X%08X%08X%08X%08X", digest[0], digest[1], digest[2], digest[3], digest[4]);
|
||||
p->hasauthed = !strcmp(password, hash);
|
||||
|
||||
if (!p->hasauthed)
|
||||
func = &hash_sha1;
|
||||
}
|
||||
break;
|
||||
// case QTVAM_MD5:
|
||||
#else
|
||||
case QTVAM_SHA1: func = &hash_sha1; break;
|
||||
#endif
|
||||
case QTVAM_SHA2_256: func = &hash_sha256; break;
|
||||
case QTVAM_SHA2_512: func = &hash_sha512; break;
|
||||
// case QTVAM_MD5: func = &hash_md5; break;
|
||||
#endif
|
||||
default:
|
||||
e = ("QTVSV 1\n"
|
||||
"PERROR: server bug detected.\n\n");
|
||||
break;
|
||||
}
|
||||
if (func)
|
||||
{
|
||||
char hash[DIGEST_MAXSIZE*2+1];
|
||||
qbyte digest[DIGEST_MAXSIZE];
|
||||
|
||||
Q_snprintfz(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
|
||||
CalcHash(func, digest, sizeof(digest), hash, strlen(hash));
|
||||
Base64_EncodeBlock(digest, func->digestsize, hash, sizeof(hash));
|
||||
p->hasauthed = !strcmp(password, hash);
|
||||
}
|
||||
if (!p->hasauthed && !e)
|
||||
{
|
||||
if (raw)
|
||||
|
@ -508,6 +532,16 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
|
|||
"AUTH: SHA1\n"
|
||||
"CHALLENGE: ");
|
||||
goto hashedpassword;
|
||||
case QTVAM_SHA2_256:
|
||||
e = ("QTVSV 1\n"
|
||||
"AUTH: SHA2_256\n"
|
||||
"CHALLENGE: ");
|
||||
goto hashedpassword;
|
||||
case QTVAM_SHA2_512:
|
||||
e = ("QTVSV 1\n"
|
||||
"AUTH: SHA2_512\n"
|
||||
"CHALLENGE: ");
|
||||
goto hashedpassword;
|
||||
hashedpassword:
|
||||
{
|
||||
char tmp[32];
|
||||
|
|
|
@ -1011,6 +1011,7 @@ void Fwd_SayToDownstream(sv_t *qtv, char *message);
|
|||
//httpsv.c
|
||||
void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend);
|
||||
void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata);
|
||||
void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen);
|
||||
|
||||
//menu.c
|
||||
void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum);
|
||||
|
|
|
@ -472,13 +472,13 @@ void Net_SendQTVConnectionRequest(sv_t *qtv, char *authmethod, char *challenge)
|
|||
}
|
||||
else if (challenge && strlen(challenge)>=8 && !strcmp(authmethod, "SHA1"))
|
||||
{
|
||||
unsigned int digest[5];
|
||||
unsigned char digest[20];
|
||||
str = "AUTH: SHA1\n"; Net_QueueUpstream(qtv, strlen(str), str);
|
||||
str = "PASSWORD: \""; Net_QueueUpstream(qtv, strlen(str), str);
|
||||
|
||||
snprintf(hash, sizeof(hash), "%s%s", challenge, qtv->connectpassword);
|
||||
CalcHash(&hash_sha1, (unsigned char*)digest, sizeof(digest), hash, strlen(hash));
|
||||
sprintf(hash, "%08X%08X%8X%08X%08X", digest[0], digest[1], digest[2], digest[3], digest[4]);
|
||||
tobase64(hash, sizeof(hash), digest, hash_sha1.digestsize);
|
||||
|
||||
str = hash; Net_QueueUpstream(qtv, strlen(str), str);
|
||||
str = "\"\n"; Net_QueueUpstream(qtv, strlen(str), str);
|
||||
|
|
Loading…
Reference in a new issue