Rework some hashfunc_t stuff.

This commit is contained in:
Shpoike 2023-02-20 06:13:40 +00:00
parent 1fe478dfa1
commit c5f837d468
26 changed files with 184 additions and 278 deletions

View File

@ -3009,9 +3009,9 @@ fail:
else if (!strcmp(auth, "SHA1")) else if (!strcmp(auth, "SHA1"))
hashfunc = &hash_sha1; hashfunc = &hash_sha1;
else if (!strcmp(auth, "SHA2_256")) else if (!strcmp(auth, "SHA2_256"))
hashfunc = &hash_sha256; hashfunc = &hash_sha2_256;
else if (!strcmp(auth, "SHA2_512")) else if (!strcmp(auth, "SHA2_512"))
hashfunc = &hash_sha512; hashfunc = &hash_sha2_512;
else if (*auth) else if (*auth)
Con_Printf("Server requires unsupported auth method: %s\n", auth); Con_Printf("Server requires unsupported auth method: %s\n", auth);

View File

@ -536,10 +536,9 @@ char *CL_GUIDString(netadr_t *adr)
{ {
static qbyte buf[2048]; static qbyte buf[2048];
static int buflen; static int buflen;
unsigned int digest[4]; qbyte digest[DIGEST_MAXSIZE];
char serveraddr[256]; char serveraddr[256];
void *blocks[2]; void *ctx;
int lens[2];
if (!*cl_sendguid.string && *connectinfo.ext.guidsalt) if (!*cl_sendguid.string && *connectinfo.ext.guidsalt)
{ {
@ -587,11 +586,12 @@ char *CL_GUIDString(netadr_t *adr)
} }
} }
blocks[0] = buf;lens[0] = buflen; ctx = alloca(hash_md4.contextsize);
blocks[1] = serveraddr;lens[1] = strlen(serveraddr); hash_md4.init(ctx);
Com_BlocksChecksum(2, blocks, lens, (void*)digest); hash_md4.process(ctx, buf, buflen);
hash_md4.process(ctx, serveraddr, strlen(serveraddr));
Q_snprintfz(connectinfo.guid, sizeof(connectinfo.guid), "%08x%08x%08x%08x", digest[0], digest[1], digest[2], digest[3]); hash_md4.terminate(digest, ctx);
Base16_EncodeBlock(digest, hash_md4.digestsize, connectinfo.guid, sizeof(connectinfo.guid));
return connectinfo.guid; return connectinfo.guid;
} }

View File

@ -1498,7 +1498,7 @@ static qboolean PM_ParsePackageList(const char *f, unsigned int parseflags, cons
size_t signsize; size_t signsize;
enum hashvalidation_e r; enum hashvalidation_e r;
int i; int i;
hashfunc_t *hf = &hash_sha512; hashfunc_t *hf = &hash_sha2_512;
void *hashdata = Z_Malloc(hf->digestsize); void *hashdata = Z_Malloc(hf->digestsize);
void *hashctx = Z_Malloc(hf->contextsize); void *hashctx = Z_Malloc(hf->contextsize);
tokstart = COM_StringParse (tokstart, authority, sizeof(authority), false, false); tokstart = COM_StringParse (tokstart, authority, sizeof(authority), false, false);
@ -4071,7 +4071,7 @@ static void PM_StartADownload(void)
} }
if (p->filesha512 && tmpfile) if (p->filesha512 && tmpfile)
tmpfile = FS_Hash_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha512, p->filesha512); tmpfile = FS_Hash_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha2_512, p->filesha512);
else if (p->filesha1 && tmpfile) else if (p->filesha1 && tmpfile)
tmpfile = FS_Hash_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha1, p->filesha1); tmpfile = FS_Hash_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha1, p->filesha1);

View File

@ -7997,7 +7997,7 @@ static qboolean CSQC_ValidateMainCSProgs(void *file, size_t filesize, unsigned i
} }
else else
{ //FTE uses folded-md4. yeah, its broken but at least its still more awkward { //FTE uses folded-md4. yeah, its broken but at least its still more awkward
if (LittleLong(Com_BlockChecksum(file, filesize)) != checksum) if (LittleLong(CalcHashInt(&hash_md4, file, filesize)) != checksum)
return false; return false;
} }
return true; return true;

View File

@ -1219,7 +1219,7 @@ static void DoSign(const char *fname, int signtype)
} }
else if (f) else if (f)
{ {
hashfunc_t *h = (signtype==1)?&hash_sha256:&hash_sha512; hashfunc_t *h = (signtype==1)?&hash_sha2_256:&hash_sha2_512;
size_t l, ts = 0; size_t l, ts = 0;
void *ctx = alloca(h->contextsize); void *ctx = alloca(h->contextsize);
qbyte data[65536*16]; qbyte data[65536*16];

View File

@ -916,7 +916,7 @@ static void Cmd_Exec_f (void)
Cbuf_InsertText (fs_manifest->defaultoverrides, level, false); Cbuf_InsertText (fs_manifest->defaultoverrides, level, false);
#if defined(HAVE_LEGACY) && defined(HAVE_CLIENT) #if defined(HAVE_LEGACY) && defined(HAVE_CLIENT)
if (l == 1914 && Com_BlockChecksum(f, l) == 0x2d7b72b9) if (l == 1914 && CalcHashInt(&hash_md4, f, l) == 0x2d7b72b9)
s = (char*)replacementq1binds; s = (char*)replacementq1binds;
#endif #endif
} }

View File

@ -930,14 +930,12 @@ void InfoBuf_WriteToFile(vfsfile_t *f, infobuf_t *info, const char *commandname,
void InfoBuf_Enumerate (infobuf_t *info, void *ctx, void(*cb)(void *ctx, const char *key, const char *value)); void InfoBuf_Enumerate (infobuf_t *info, void *ctx, void(*cb)(void *ctx, const char *key, const char *value));
void Com_BlocksChecksum (int blocks, void **buffer, int *len, unsigned char *outbuf);
unsigned int Com_BlockChecksum (const void *buffer, int length);
void Com_BlockFullChecksum (const void *buffer, int len, unsigned char *outbuf);
qbyte COM_BlockSequenceCheckByte (qbyte *base, int length, int sequence, unsigned mapchecksum); qbyte COM_BlockSequenceCheckByte (qbyte *base, int length, int sequence, unsigned mapchecksum);
qbyte COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence); qbyte COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence);
qbyte Q2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence); qbyte Q2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence);
size_t Base64_EncodeBlock(const qbyte *in, size_t length, char *out, size_t outsize); //tries to null terminate, but returns length without termination. size_t Base64_EncodeBlock(const qbyte *in, size_t length, char *out, size_t outsize); //tries to null terminate, but returns length without termination.
size_t Base64_EncodeBlockURI(const qbyte *in, size_t length, char *out, size_t outsize); //slightly different chars for uri safety. also trims.
size_t Base64_DecodeBlock(const char *in, const char *in_end, qbyte *out, size_t outsize); // +/ and = size_t Base64_DecodeBlock(const char *in, const char *in_end, qbyte *out, size_t outsize); // +/ and =
size_t Base16_EncodeBlock(const char *in, size_t length, qbyte *out, size_t outsize); size_t Base16_EncodeBlock(const char *in, size_t length, qbyte *out, size_t outsize);
size_t Base16_DecodeBlock(const char *in, qbyte *out, size_t outsize); size_t Base16_DecodeBlock(const char *in, qbyte *out, size_t outsize);
@ -951,15 +949,16 @@ typedef struct
void (*process) (void *context, const void *data, size_t datasize); void (*process) (void *context, const void *data, size_t datasize);
void (*terminate) (unsigned char *digest, void *context); void (*terminate) (unsigned char *digest, void *context);
} hashfunc_t; } hashfunc_t;
extern hashfunc_t hash_sha1; extern hashfunc_t hash_md4; //required for vanilla qw mapchecks
extern hashfunc_t hash_sha224; extern hashfunc_t hash_sha1; //required for websockets, and ezquake's crypted rcon
extern hashfunc_t hash_sha256; extern hashfunc_t hash_sha2_224;
extern hashfunc_t hash_sha384; extern hashfunc_t hash_sha2_256; //required for webrtc
extern hashfunc_t hash_sha512; extern hashfunc_t hash_sha2_384;
extern hashfunc_t hash_crc16; extern hashfunc_t hash_sha2_512;
extern hashfunc_t hash_crc16; //aka ccitt, required for qw's clc_move and various bits of dp compat
extern hashfunc_t hash_crc16_lower; extern hashfunc_t hash_crc16_lower;
unsigned int hashfunc_terminate_uint(const hashfunc_t *hash, void *context); //terminate, except returning the digest as a uint instead of a blob. folds the digest if longer than 4 bytes. unsigned int hashfunc_terminate_uint(const hashfunc_t *hash, void *context); //terminate, except returning the digest as a uint instead of a blob. folds the digest if longer than 4 bytes.
unsigned int CalcHashInt(const hashfunc_t *hash, const unsigned char *data, size_t datasize); unsigned int CalcHashInt(const hashfunc_t *hash, const void *data, size_t datasize);
size_t CalcHash(const hashfunc_t *hash, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datasize); size_t CalcHash(const hashfunc_t *hash, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datasize);
size_t CalcHMAC(const hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen); size_t CalcHMAC(const hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen);

View File

@ -1330,10 +1330,10 @@ static void COM_CalcHash_Thread(void *ctx, void *fname, size_t a, size_t b)
// {"crc16", &hash_crc16}, // {"crc16", &hash_crc16},
{"sha1", &hash_sha1}, {"sha1", &hash_sha1},
#if defined(HAVE_SERVER) || defined(HAVE_CLIENT) #if defined(HAVE_SERVER) || defined(HAVE_CLIENT)
// {"sha224", &hash_sha224}, // {"sha224", &hash_sha2_224},
{"sha256", &hash_sha256}, {"sha256", &hash_sha2_256},
// {"sha384", &hash_sha384}, // {"sha384", &hash_sha2_384},
// {"sha512", &hash_sha512}, // {"sha512", &hash_sha2_512},
#endif #endif
}; };
qbyte digest[DIGEST_MAXSIZE]; qbyte digest[DIGEST_MAXSIZE];

View File

@ -1235,9 +1235,9 @@ static int QDECL FSDZ_GeneratePureCRC(searchpathfuncs_t *handle, int seed, int c
} }
if (crctype) if (crctype)
result = Com_BlockChecksum(filecrcs, numcrcs*sizeof(int)); result = CalcHashInt(&hash_md4, filecrcs, numcrcs*sizeof(int));
else else
result = Com_BlockChecksum(filecrcs+1, (numcrcs-1)*sizeof(int)); result = CalcHashInt(&hash_md4, filecrcs+1, (numcrcs-1)*sizeof(int));
BZ_Free(filecrcs); BZ_Free(filecrcs);
return result; return result;

View File

@ -176,9 +176,9 @@ static int QDECL FSPAK_GeneratePureCRC(searchpathfuncs_t *handle, int seed, int
} }
if (crctype) if (crctype)
result = Com_BlockChecksum(filecrcs, numcrcs*sizeof(int)); result = CalcHashInt(&hash_md4, filecrcs, numcrcs*sizeof(int));
else else
result = Com_BlockChecksum(filecrcs+1, (numcrcs-1)*sizeof(int)); result = CalcHashInt(&hash_md4, filecrcs+1, (numcrcs-1)*sizeof(int));
BZ_Free(filecrcs); BZ_Free(filecrcs);
return result; return result;

View File

@ -788,9 +788,9 @@ static int QDECL FSZIP_GeneratePureCRC(searchpathfuncs_t *handle, int seed, int
} }
if (crctype || numcrcs < 1) if (crctype || numcrcs < 1)
result = Com_BlockChecksum(filecrcs, numcrcs*sizeof(int)); result = CalcHashInt(&hash_md4, filecrcs, numcrcs*sizeof(int));
else else
result = Com_BlockChecksum(filecrcs+1, (numcrcs-1)*sizeof(int)); result = CalcHashInt(&hash_md4, filecrcs+1, (numcrcs-1)*sizeof(int));
BZ_Free(filecrcs); BZ_Free(filecrcs);
return result; return result;

View File

@ -4517,7 +4517,7 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole
return NULL; return NULL;
} }
checksum = LittleLong (Com_BlockChecksum (buf, length)); checksum = LittleLong (CalcHashInt(&hash_md4, buf, length));
header = *(q2dheader_t *)(buf); header = *(q2dheader_t *)(buf);
header.ident = LittleLong(header.ident); header.ident = LittleLong(header.ident);

View File

@ -51,7 +51,7 @@ typedef struct {
} MD4_CTX; } MD4_CTX;
void MD4Init (MD4_CTX *); void MD4Init (MD4_CTX *);
void MD4Update (MD4_CTX *, unsigned char *, unsigned int); void MD4Update (MD4_CTX *, const unsigned char *, size_t);
void MD4Final (unsigned char [16], MD4_CTX *); void MD4Final (unsigned char [16], MD4_CTX *);
@ -84,9 +84,9 @@ These notices must be retained in any copies of any part of this documentation a
#define S33 11 #define S33 11
#define S34 15 #define S34 15
static void MD4Transform (UINT4 [4], unsigned char [64]); static void MD4Transform (UINT4 [4], const unsigned char [64]);
static void Encode (unsigned char *, UINT4 *, unsigned int); static void Encode (unsigned char *, UINT4 *, unsigned int);
static void Decode (UINT4 *, unsigned char *, unsigned int); static void Decode (UINT4 *, const unsigned char *, unsigned int);
static unsigned char PADDING[64] = { static unsigned char PADDING[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
@ -122,7 +122,7 @@ context->state[3] = 0x10325476;
} }
/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */ /* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */
void MD4Update (MD4_CTX *context, unsigned char *input, unsigned int inputLen) void MD4Update (MD4_CTX *context, const unsigned char *input, size_t inputLen)
{ {
unsigned int i, index, partLen; unsigned int i, index, partLen;
@ -182,7 +182,7 @@ void MD4Final (unsigned char digest[16], MD4_CTX *context)
/* MD4 basic transformation. Transforms state based on block. */ /* MD4 basic transformation. Transforms state based on block. */
static void MD4Transform (UINT4 state[4], unsigned char block[64]) static void MD4Transform (UINT4 state[4], const unsigned char block[64])
{ {
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
@ -267,7 +267,7 @@ static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */ /* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */
static void Decode (UINT4 *output, unsigned char *input, unsigned int len) static void Decode (UINT4 *output, const unsigned char *input, unsigned int len)
{ {
unsigned int i, j; unsigned int i, j;
@ -276,40 +276,13 @@ for (i = 0, j = 0; j < len; i++, j += 4)
} }
//=================================================================== //===================================================================
#include "quakedef.h"
unsigned int Com_BlockChecksum (void *buffer, int length) hashfunc_t hash_md4 =
{ {
unsigned int digest[4]; 16, //digest size
unsigned int val; sizeof(MD4_CTX),
MD4_CTX ctx; (void(*)(void*ctx))MD4Init,
(void(*)(void*ctx,const void*in,size_t))MD4Update,
(void(*)(qbyte*out,void*ctx))MD4Final,
};
MD4Init (&ctx);
MD4Update (&ctx, (unsigned char *)buffer, length);
MD4Final ( (unsigned char *)digest, &ctx);
val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
return val;
}
void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf)
{
MD4_CTX ctx;
MD4Init (&ctx);
MD4Update (&ctx, (unsigned char *)buffer, len);
MD4Final ( outbuf, &ctx);
}
void Com_BlocksChecksum (int blocks, void **buffer, int *len, unsigned char *outbuf)
{
MD4_CTX ctx;
MD4Init (&ctx);
while(blocks --> 0)
{
MD4Update (&ctx, (unsigned char *)*buffer++, *len++);
}
MD4Final (outbuf, &ctx);
}

View File

@ -256,6 +256,21 @@ typedef struct sctp_s
unsigned short qstreamid; //in network endian. unsigned short qstreamid; //in network endian.
} sctp_t; } sctp_t;
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
static const struct
{
const char *name;
hashfunc_t *hash;
} webrtc_hashes[] =
{ //RFC8211 specifies this list of hashes
// {"md2", &hash_md2}, //deprecated, hopefully we won't see it
// {"md5", &hash_md5}, //deprecated, hopefully we won't see it
{"sha-1", &hash_sha1},
{"sha-224", &hash_sha2_224},
{"sha-256", &hash_sha2_256},
{"sha-384", &hash_sha2_384},
{"sha-512", &hash_sha2_512},
};
extern cvar_t net_enable_dtls; extern cvar_t net_enable_dtls;
static neterr_t SCTP_Transmit(sctp_t *sctp, struct icestate_s *peer, const void *data, size_t length); static neterr_t SCTP_Transmit(sctp_t *sctp, struct icestate_s *peer, const void *data, size_t length);
#endif #endif
@ -1912,26 +1927,24 @@ static void ICE_ParseSDPLine(struct icestate_s *con, const char *value)
} }
else if (!strncmp(value, "a=fingerprint:", 14)) else if (!strncmp(value, "a=fingerprint:", 14))
{ {
hashfunc_t *hash = NULL;
int i;
char name[64]; char name[64];
value = COM_ParseOut(value+14, name, sizeof(name)); value = COM_ParseOut(value+14, name, sizeof(name));
if (!strcasecmp(name, "sha-1")) for (i = 0; i < countof(webrtc_hashes); i++)
con->cred.peer.hash = &hash_sha1; {
else if (!strcasecmp(name, "sha-224")) if (!strcasecmp(name, webrtc_hashes[i].name))
con->cred.peer.hash = &hash_sha224; {
else if (!strcasecmp(name, "sha-256")) hash = webrtc_hashes[i].hash;
con->cred.peer.hash = &hash_sha256; break;
else if (!strcasecmp(name, "sha-384")) }
con->cred.peer.hash = &hash_sha384; }
else if (!strcasecmp(name, "sha-512")) if (hash && (!con->cred.peer.hash || hash->digestsize>con->cred.peer.hash->digestsize)) //FIXME: digest size is not a good indicator of whether its exploitable or not, but should work for sha1/sha2 options. the sender here is expected to be trustworthy anyway.
con->cred.peer.hash = &hash_sha512;
else
con->cred.peer.hash = NULL; //hash not recognised
if (con->cred.peer.hash)
{ {
int b, o, v; int b, o, v;
while (*value == ' ') while (*value == ' ')
value++; value++;
for (b = 0; b < con->cred.peer.hash->digestsize; ) for (b = 0; b < hash->digestsize; )
{ {
v = *value; v = *value;
if (v >= '0' && v <= '9') if (v >= '0' && v <= '9')
@ -1958,8 +1971,10 @@ static void ICE_ParseSDPLine(struct icestate_s *con, const char *value)
break; break;
value++; value++;
} }
if (b != con->cred.peer.hash->digestsize) if (b == hash->digestsize)
con->cred.peer.hash = NULL; //bad! con->cred.peer.hash = hash; //it was the right size, woo.
else
con->cred.peer.hash = NULL; //bad! (should we 0-pad?)
} }
} }
else if (!strncmp(value, "a=sctp-port:", 12)) else if (!strncmp(value, "a=sctp-port:", 12))
@ -2471,14 +2486,12 @@ static qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *va
{ {
int b; int b;
Q_strncatz(value, "a=fingerprint:", valuelen); Q_strncatz(value, "a=fingerprint:", valuelen);
if (con->cred.peer.hash == &hash_sha1) for (b = 0; b < countof(webrtc_hashes); b++)
Q_strncatz(value, "sha-1", valuelen); {
else if (con->cred.peer.hash == &hash_sha256) if (con->cred.peer.hash == webrtc_hashes[b].hash)
Q_strncatz(value, "sha-256", valuelen); break;
else if (con->cred.peer.hash == &hash_sha512) }
Q_strncatz(value, "sha-512", valuelen); Q_strncatz(value, (b==countof(webrtc_hashes))?"UNKNOWN":webrtc_hashes[b].name, valuelen);
else
Q_strncatz(value, "UNKNOWN", valuelen);
for (b = 0; b < con->cred.peer.hash->digestsize; b++) for (b = 0; b < con->cred.peer.hash->digestsize; b++)
Q_strncatz(value, va(b?":%02X":" %02X", con->cred.peer.digest[b]), valuelen); Q_strncatz(value, va(b?":%02X":" %02X", con->cred.peer.digest[b]), valuelen);
Q_strncatz(value, "\n", valuelen); Q_strncatz(value, "\n", valuelen);
@ -2533,16 +2546,17 @@ static qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *va
{ //this is a preliminary check to avoid wasting time { //this is a preliminary check to avoid wasting time
if (!con->cred.local.certsize) if (!con->cred.local.certsize)
return false; //fail if we cannot do dtls when its required. return false; //fail if we cannot do dtls when its required.
if (!strcmp(prop, "sdpanswer") || !con->cred.peer.hash) if (!strcmp(prop, "sdpanswer") && !con->cred.peer.hash)
return false; //don't answer if they failed to provide a cert return false; //don't answer if they failed to provide a cert
} }
if (con->cred.local.certsize) if (con->cred.local.certsize)
{ {
qbyte fingerprint[DIGEST_MAXSIZE]; qbyte fingerprint[DIGEST_MAXSIZE];
int b; int b;
CalcHash(&hash_sha256, fingerprint, sizeof(fingerprint), con->cred.local.cert, con->cred.local.certsize); hashfunc_t *hash = &hash_sha2_256; //browsers use sha-256, lets match them.
CalcHash(hash, fingerprint, sizeof(fingerprint), con->cred.local.cert, con->cred.local.certsize);
Q_strncatz(value, "a=fingerprint:sha-256", valuelen); Q_strncatz(value, "a=fingerprint:sha-256", valuelen);
for (b = 0; b < hash_sha256.digestsize; b++) for (b = 0; b < hash->digestsize; b++)
Q_strncatz(value, va(b?":%02X":" %02X", fingerprint[b]), valuelen); Q_strncatz(value, va(b?":%02X":" %02X", fingerprint[b]), valuelen);
Q_strncatz(value, "\n", valuelen); Q_strncatz(value, "\n", valuelen);

View File

@ -1191,6 +1191,11 @@ void QDECL Plug_FS_EnumerateFiles(enum fs_relative fsroot, const char *match, in
} }
} }
unsigned int Plug_BlockChecksum(const void *data, size_t datasize)
{ //convienience function. we use md4 for legacy reasons (every qw engine must have an implementation)
return CalcHashInt(&hash_md4, data, datasize);
}
#if defined(HAVE_SERVER) && defined(HAVE_CLIENT) #if defined(HAVE_SERVER) && defined(HAVE_CLIENT)
static qboolean QDECL Plug_MapLog_Query(const char *packagename, const char *mapname, float *vals) static qboolean QDECL Plug_MapLog_Query(const char *packagename, const char *mapname, float *vals)
{ {
@ -1930,7 +1935,7 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s
COM_GetFileExtension, COM_GetFileExtension,
COM_FileBase, COM_FileBase,
COM_CleanUpPath, COM_CleanUpPath,
Com_BlockChecksum, Plug_BlockChecksum,
FS_LoadMallocFile, FS_LoadMallocFile,
FS_GetPackHashes, FS_GetPackHashes,

View File

@ -5665,21 +5665,18 @@ static void QCBUILTIN PF_digest_internal (pubprogfuncs_t *prinst, struct globalv
unsigned char hexdig[sizeof(digest)*2+1]; unsigned char hexdig[sizeof(digest)*2+1];
if (!strcmp(hashtype, "MD4")) if (!strcmp(hashtype, "MD4"))
{ digestsize = CalcHash(&hash_md4, digest, sizeof(digest), str, len);
digestsize = 16;
Com_BlockFullChecksum(str, len, digest);
}
//md5? //md5?
else if (!strcmp(hashtype, "SHA1")) else if (!strcmp(hashtype, "SHA1"))
digestsize = CalcHash(&hash_sha1, digest, sizeof(digest), str, len); digestsize = CalcHash(&hash_sha1, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA224")) else if (!strcmp(hashtype, "SHA2-224") || !strcmp(hashtype, "SHA224"))
digestsize = CalcHash(&hash_sha224, digest, sizeof(digest), str, len); digestsize = CalcHash(&hash_sha2_224, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA256")) else if (!strcmp(hashtype, "SHA2-256") || !strcmp(hashtype, "SHA256"))
digestsize = CalcHash(&hash_sha256, digest, sizeof(digest), str, len); digestsize = CalcHash(&hash_sha2_256, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA384")) else if (!strcmp(hashtype, "SHA2-384") || !strcmp(hashtype, "SHA384"))
digestsize = CalcHash(&hash_sha384, digest, sizeof(digest), str, len); digestsize = CalcHash(&hash_sha2_384, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA512")) else if (!strcmp(hashtype, "SHA2-512") || !strcmp(hashtype, "SHA512"))
digestsize = CalcHash(&hash_sha512, digest, sizeof(digest), str, len); digestsize = CalcHash(&hash_sha2_512, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "CRC16")) else if (!strcmp(hashtype, "CRC16"))
digestsize = CalcHash(&hash_crc16, digest, sizeof(digest), str, len); digestsize = CalcHash(&hash_crc16, digest, sizeof(digest), str, len);
else else

View File

@ -194,7 +194,7 @@ unsigned int hashfunc_terminate_uint(const hashfunc_t *func, void *context)
r ^= digest[l]<<((l%sizeof(r))*8); r ^= digest[l]<<((l%sizeof(r))*8);
return r; return r;
} }
unsigned int CalcHashInt(const hashfunc_t *func, const unsigned char *data, size_t datasize) unsigned int CalcHashInt(const hashfunc_t *func, const void *data, size_t datasize)
{ {
void *ctx = alloca(func->contextsize); void *ctx = alloca(func->contextsize);
func->init(ctx); func->init(ctx);

View File

@ -515,7 +515,7 @@ static void sha256_finish (qbyte *digest, void *context)
memcpy(digest, hd->buf, 256/8); memcpy(digest, hd->buf, 256/8);
} }
hashfunc_t hash_sha224 = hashfunc_t hash_sha2_224 =
{ {
224/8, 224/8,
sizeof(SHA2_CONTEXT), sizeof(SHA2_CONTEXT),
@ -523,7 +523,7 @@ hashfunc_t hash_sha224 =
sha2_write, sha2_write,
sha224_finish sha224_finish
}; };
hashfunc_t hash_sha256 = hashfunc_t hash_sha2_256 =
{ {
256/8, 256/8,
sizeof(SHA2_CONTEXT), sizeof(SHA2_CONTEXT),
@ -547,7 +547,7 @@ static void sha512_finish (qbyte *digest, void *context)
memcpy(digest, hd->buf, 512/8); memcpy(digest, hd->buf, 512/8);
} }
hashfunc_t hash_sha384 = hashfunc_t hash_sha2_384 =
{ {
384/8, 384/8,
sizeof(SHA2_CONTEXT), sizeof(SHA2_CONTEXT),
@ -555,7 +555,7 @@ hashfunc_t hash_sha384 =
sha2_write, sha2_write,
sha384_finish sha384_finish
}; };
hashfunc_t hash_sha512 = hashfunc_t hash_sha2_512 =
{ {
512/8, 512/8,
sizeof(SHA2_CONTEXT), sizeof(SHA2_CONTEXT),

View File

@ -1461,7 +1461,7 @@ static const char *Mod_RemapBuggyTexture(const char *name, const qbyte *data, un
{ {
if (!strcmp(name, buggytextures[i].oldname)) if (!strcmp(name, buggytextures[i].oldname))
{ {
unsigned int sum = Com_BlockChecksum(data, datalen); unsigned int sum = CalcHashInt(&hash_md4, data, datalen);
for (; i < sizeof(buggytextures)/sizeof(buggytextures[0]); i++) for (; i < sizeof(buggytextures)/sizeof(buggytextures[0]); i++)
{ {
if (strcmp(name, buggytextures[i].oldname)) if (strcmp(name, buggytextures[i].oldname))
@ -5413,7 +5413,7 @@ static qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsi
} }
if (i == LUMP_ENTITIES) if (i == LUMP_ENTITIES)
continue; continue;
chksum = Com_BlockChecksum(mod_base + header.lumps[i].fileofs, header.lumps[i].filelen); chksum = CalcHashInt(&hash_md4, mod_base + header.lumps[i].fileofs, header.lumps[i].filelen);
mod->checksum ^= chksum; mod->checksum ^= chksum;
if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES) if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)

View File

@ -8924,7 +8924,7 @@ void QCBUILTIN PF_sj_strhash (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
{ //not quite the same, but oh well { //not quite the same, but oh well
const char *str = PF_VarString(prinst, 0, pr_globals); const char *str = PF_VarString(prinst, 0, pr_globals);
int len = strlen(str); int len = strlen(str);
G_FLOAT(OFS_RETURN) = Com_BlockChecksum(str, len); G_FLOAT(OFS_RETURN) = CalcHashInt(&hash_md4, str, len);
} }
#endif #endif
static void QCBUILTIN PF_StopSound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) static void QCBUILTIN PF_StopSound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)

View File

@ -1154,7 +1154,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
if (file) if (file)
{ {
char text[64]; char text[64];
sv.csqcchecksum = Com_BlockChecksum(file, fsz); sv.csqcchecksum = CalcHashInt(&hash_md4, file, fsz);
sprintf(text, "0x%x", sv.csqcchecksum); sprintf(text, "0x%x", sv.csqcchecksum);
InfoBuf_SetValueForStarKey(&svs.info, "*csprogs", text); InfoBuf_SetValueForStarKey(&svs.info, "*csprogs", text);
sprintf(text, "0x%x", (unsigned int)fsz); sprintf(text, "0x%x", (unsigned int)fsz);

View File

@ -266,19 +266,23 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
int raw = 0; int raw = 0;
char password[256] = ""; char password[256] = "";
char userinfo[1024]; char userinfo[1024];
enum { static struct
//MUST BE ORDERED HIGHEST-PRIORITY-LAST {
QTVAM_NONE, const char *name; //as seen in protocol
QTVAM_PLAIN, hashfunc_t *func;
#ifdef TCPCONNECT int base;
// QTVAM_CCITT, //16bit = ddos it } hashes[] = {
QTVAM_MD4, //fucked. required for eztv compat {"NONE", NULL, -1}, //for annonymous connections
// QTVAM_MD5, //fucked, no hash implemented {"PLAIN", NULL, 0},
QTVAM_SHA1, //fucked too nowadays // {"CCITT", &hash_crc16, 16}, //'the CCITT standard CRC used by XMODEM'. 16bit anyway, don't allow, too easy to guess.
QTVAM_SHA2_256, // // {"MD4", &hash_md4, 15}, //md4 is available to all QW clients, but probably too weak to really use.
QTVAM_SHA2_512, // // {"MD5", &hash_md5, 16}, //blurgh
#endif {"SHA1", &hash_sha1, 16},
} authmethod = QTVAM_NONE; {"SHA2_256", &hash_sha2_256, 64},
{"SHA2_512", &hash_sha2_512, 64},
// {"SHA3_512", &hash_sha3_512, 16}, //eztv apparently allows this
};
int authmethod = 0; //which of the above we're trying to use...
start = headerstart; start = headerstart;
@ -347,32 +351,20 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
{ {
int thisauth; int thisauth;
start = COM_ParseToken(start, NULL); start = COM_ParseToken(start, NULL);
if (!strcmp(com_token, "NONE")) for (thisauth = 1; ; thisauth++)
thisauth = QTVAM_NONE;
else if (!strcmp(com_token, "PLAIN"))
thisauth = QTVAM_PLAIN;
#ifdef TCPCONNECT
// else if (!strcmp(com_token, "CCIT"))
// thisauth = QTVAM_CCITT;
else if (!strcmp(com_token, "MD4"))
thisauth = QTVAM_MD4;
// else if (!strcmp(com_token, "MD5"))
// 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
{ {
thisauth = QTVAM_NONE; if (thisauth == countof(hashes))
Con_DPrintf("qtv: received unrecognised auth method (%s)\n", com_token); {
Con_DPrintf("qtv: received unrecognised auth method (%s)\n", com_token);
break;
}
if (!strcmp(com_token, hashes[thisauth].name))
{ //we know this one.
if (authmethod < thisauth)
authmethod = thisauth; //and its better than the previous one we saw
break;
}
} }
if (authmethod < thisauth)
authmethod = thisauth;
} }
else if (!strcmp(com_token, "SOURCE")) else if (!strcmp(com_token, "SOURCE"))
{ {
@ -416,77 +408,38 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
p->hasauthed = true; //no password, no need to auth. p->hasauthed = true; //no password, no need to auth.
else if (*password) else if (*password)
{ {
hashfunc_t *func = NULL; char hash[512];
if (!*p->challenge && authmethod>QTVAM_PLAIN) qbyte digest[DIGEST_MAXSIZE];
if (!*p->challenge && hashes[authmethod].func)
e = ("QTVSV 1\n" e = ("QTVSV 1\n"
"PERROR: Challenge wasn't given...\n\n"); "PERROR: Challenge wasn't given...\n\n");
else switch (authmethod) switch(hashes[authmethod].base)
{ {
case QTVAM_NONE: default:
case -1: //no auth at all
e = ("QTVSV 1\n" e = ("QTVSV 1\n"
"PERROR: You need to provide a password.\n\n"); "PERROR: You need to provide a password.\n\n");
break; break;
case QTVAM_PLAIN: case 0: //plain text. challenge is not used.
p->hasauthed = !strcmp(qtv_password.string, password); Q_snprintfz(hash, sizeof(hash), "%s", qtv_password.string);
break; break;
#ifdef TCPCONNECT case 15: //fucked encoding(missing some leading 0s)
/*case QTVAM_CCITT:
{
unsigned short ushort_result;
QCRC_Init(&ushort_result);
QCRC_AddBlock(&ushort_result, p->challenge, strlen(p->challenge));
QCRC_AddBlock(&ushort_result, qtv_password.string, strlen(qtv_password.string));
p->hasauthed = (ushort_result == strtoul(password, NULL, 0));
}
break;*/
case QTVAM_MD4:
{
char hash[512];
int md4sum[4];
Q_snprintfz(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
Com_BlockFullChecksum (hash, strlen(hash), (unsigned char*)md4sum);
Q_snprintfz(hash, sizeof(hash), "%X%X%X%X", md4sum[0], md4sum[1], md4sum[2], md4sum[3]);
p->hasauthed = !strcmp(password, hash);
}
break;
#ifdef HAVE_LEGACY //to be disabled at some point.
case QTVAM_SHA1:
{
char hash[512];
int digest[5];
Q_snprintfz(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
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;
#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); Q_snprintfz(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
CalcHash(func, digest, sizeof(digest), hash, strlen(hash)); CalcHash(hashes[authmethod].func, digest, sizeof(digest), hash, strlen(hash));
Base64_EncodeBlock(digest, func->digestsize, hash, sizeof(hash)); Q_snprintfz(hash, sizeof(hash), "%X%X%X%X", ((quint32_t*)digest)[0], ((quint32_t*)digest)[1], ((quint32_t*)digest)[2], ((quint32_t*)digest)[3]);
p->hasauthed = !strcmp(password, hash); break;
case 16:
Q_snprintfz(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
CalcHash(hashes[authmethod].func, digest, sizeof(digest), hash, strlen(hash));
Base16_EncodeBlock(digest, hashes[authmethod].func->digestsize, hash, sizeof(hash));
break;
case 64:
Q_snprintfz(hash, sizeof(hash), "%s%s", p->challenge, qtv_password.string);
CalcHash(hashes[authmethod].func, digest, sizeof(digest), hash, strlen(hash));
Base64_EncodeBlock(digest, hashes[authmethod].func->digestsize, hash, sizeof(hash));
break;
} }
p->hasauthed = !strcmp(password, hash);
if (!p->hasauthed && !e) if (!p->hasauthed && !e)
{ {
if (raw) if (raw)
@ -499,66 +452,31 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
else else
{ {
//no password, and not automagically authed //no password, and not automagically authed
switch (authmethod) switch (hashes[authmethod].base)
{ {
case QTVAM_NONE: case -1:
if (raw) if (raw)
e = ""; e = "";
else else
e = ("QTVSV 1\n" e = ("QTVSV 1\n"
"PERROR: You need to provide a common auth method.\n\n"); "PERROR: You need to provide a common auth method.\n\n");
break; break;
case QTVAM_PLAIN: case 0:
p->hasauthed = !strcmp(qtv_password.string, password); p->hasauthed = !strcmp(qtv_password.string, password);
break; break;
#ifdef TCPCONNECT default:
/*case QTVAM_CCITT:
e = ("QTVSV 1\n"
"AUTH: CCITT\n"
"CHALLENGE: ");
goto hashedpassword;*/
case QTVAM_MD4:
e = ("QTVSV 1\n"
"AUTH: MD4\n"
"CHALLENGE: ");
goto hashedpassword;
/*case QTVAM_MD5:
e = ("QTVSV 1\n"
"AUTH: MD5\n"
"CHALLENGE: ");
goto hashedpassword;*/
case QTVAM_SHA1:
e = ("QTVSV 1\n"
"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]; char tmp[32];
Sys_RandomBytes(tmp, sizeof(tmp)); Sys_RandomBytes(tmp, sizeof(tmp));
tobase64(p->challenge, sizeof(p->challenge), tmp, sizeof(tmp)); tobase64(p->challenge, sizeof(p->challenge), tmp, sizeof(tmp));
} }
VFS_WRITE(clientstream, e, strlen(e)); e = va("QTVSV 1\n"
VFS_WRITE(clientstream, p->challenge, strlen(p->challenge)); "AUTH: %s\n"
e = "\n\n"; "CHALLENGE: %s\n\n",
hashes[authmethod].name, p->challenge);
VFS_WRITE(clientstream, e, strlen(e)); VFS_WRITE(clientstream, e, strlen(e));
return QTV_RETRY; return QTV_RETRY;
#endif
default:
e = ("QTVSV 1\n"
"PERROR: server bug detected.\n\n");
break;
} }
} }

View File

@ -248,10 +248,10 @@ typedef struct
void (*terminate) (unsigned char *digest, void *context); void (*terminate) (unsigned char *digest, void *context);
} hashfunc_t; } hashfunc_t;
extern hashfunc_t hash_sha1; extern hashfunc_t hash_sha1;
/*extern hashfunc_t hash_sha224; /*extern hashfunc_t hash_sha2_224;
extern hashfunc_t hash_sha256; extern hashfunc_t hash_sha2_256;
extern hashfunc_t hash_sha384; extern hashfunc_t hash_sha2_384;
extern hashfunc_t hash_sha512;*/ extern hashfunc_t hash_sha2_512;*/
#define HMAC HMAC_quake //stop conflicts... #define HMAC HMAC_quake //stop conflicts...
size_t CalcHash(hashfunc_t *hash, unsigned char *digest, size_t maxdigestsize, const unsigned char *string, size_t stringlen); size_t CalcHash(hashfunc_t *hash, unsigned char *digest, size_t maxdigestsize, const unsigned char *string, size_t stringlen);
size_t HMAC(hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen); size_t HMAC(hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen);

View File

@ -465,7 +465,7 @@ void Net_SendQTVConnectionRequest(sv_t *qtv, char *authmethod, char *challenge)
snprintf(hash, sizeof(hash), "%s%s", challenge, qtv->connectpassword); snprintf(hash, sizeof(hash), "%s%s", challenge, qtv->connectpassword);
Com_BlockFullChecksum (hash, strlen(hash), (unsigned char*)md4sum); Com_BlockFullChecksum (hash, strlen(hash), (unsigned char*)md4sum);
sprintf(hash, "%X%X%X%X", md4sum[0], md4sum[1], md4sum[2], md4sum[3]); sprintf(hash, "%X%X%X%X", md4sum[0], md4sum[1], md4sum[2], md4sum[3]); //FIXME: bad formatting!
str = hash; Net_QueueUpstream(qtv, strlen(str), str); str = hash; Net_QueueUpstream(qtv, strlen(str), str);
str = "\"\n"; Net_QueueUpstream(qtv, strlen(str), str); str = "\"\n"; Net_QueueUpstream(qtv, strlen(str), str);

View File

@ -1238,11 +1238,11 @@ static int sasl_scramsha1minus_initial(struct sasl_ctx_s *ctx, char *buf, int bu
} }
static int sasl_scramsha256minus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize) static int sasl_scramsha256minus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)
{ {
return sasl_scram_initial(ctx, buf, bufsize, &hash_sha256, false); return sasl_scram_initial(ctx, buf, bufsize, &hash_sha2_256, false);
} }
static int sasl_scramsha512minus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize) static int sasl_scramsha512minus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)
{ {
return sasl_scram_initial(ctx, buf, bufsize, &hash_sha512, false); return sasl_scram_initial(ctx, buf, bufsize, &hash_sha2_512, false);
} }
static int sasl_scramsha1plus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize) static int sasl_scramsha1plus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)
{ {
@ -1250,11 +1250,11 @@ static int sasl_scramsha1plus_initial(struct sasl_ctx_s *ctx, char *buf, int buf
} }
static int sasl_scramsha256plus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize) static int sasl_scramsha256plus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)
{ {
return sasl_scram_initial(ctx, buf, bufsize, &hash_sha256, true); return sasl_scram_initial(ctx, buf, bufsize, &hash_sha2_256, true);
} }
static int sasl_scramsha512plus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize) static int sasl_scramsha512plus_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize)
{ {
return sasl_scram_initial(ctx, buf, bufsize, &hash_sha512, true); return sasl_scram_initial(ctx, buf, bufsize, &hash_sha2_512, true);
} }
static void buf_cat(buf_t *buf, char *data, int len) static void buf_cat(buf_t *buf, char *data, int len)

View File

@ -547,7 +547,7 @@ typedef struct //for plugins that need to read/write files...
F(const char *,GetExtension,(const char *filename, const char *ignoreext)); F(const char *,GetExtension,(const char *filename, const char *ignoreext));
F(void, FileBase, (const char *in, char *out, int outlen)); F(void, FileBase, (const char *in, char *out, int outlen));
F(void, CleanUpPath, (char *str)); F(void, CleanUpPath, (char *str));
F(unsigned int,BlockChecksum,(const void *buffer, int length)); //mostly for pack hashes. F(unsigned int,BlockChecksum,(const void *buffer, size_t length)); //mostly for pack hashes.
F(void*, LoadFile, (const char *fname, size_t *fsize)); //plugfuncs->Free F(void*, LoadFile, (const char *fname, size_t *fsize)); //plugfuncs->Free
//stuff that's useful for networking. //stuff that's useful for networking.