Provide support for webrtc's sctp-over-dtls stuff, providing browser+native connectivity (via broker).

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6171 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2022-01-28 10:48:01 +00:00
parent 43565e4363
commit 86d2f0e0d4
11 changed files with 1555 additions and 200 deletions

View File

@ -1188,7 +1188,7 @@ void CL_CheckForResend (void)
return;
}
else
connectinfo.clogged = false;
connectinfo.clogged = false; //do the prints and everything.
#ifdef HAVE_DTLS
if (connectinfo.numadr>0 && connectinfo.adr[0].prot == NP_DTLS)
@ -1258,6 +1258,9 @@ void CL_CheckForResend (void)
Con_TPrintf ("Connecting to %s...\n", cls.servername);
}
if (connectinfo.clogged)
connectinfo.clogged = false;
if (connectinfo.tries == 0 && connectinfo.nextadr < connectinfo.numadr)
if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername, to))
{
@ -1414,6 +1417,14 @@ void CL_BeginServerReconnect(void)
if (connectinfo.numadr>0)
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[0]);
connectinfo.dtlsupgrade = 0;
#endif
#ifdef SUPPORT_ICE
while (connectinfo.numadr) //remove any ICE addresses. probably we'll end up with no addresses left leaving us free to re-resolve giving us the original(ish) rtc connection.
{
if (connectinfo.adr[connectinfo.numadr-1].type != NA_ICE)
break;
connectinfo.numadr--;
}
#endif
if (*cl_disconnectreason.string)
Cvar_Set(&cl_disconnectreason, "");
@ -1426,6 +1437,13 @@ void CL_BeginServerReconnect(void)
NET_InitClient(false);
}
void CL_Transfer(netadr_t *adr)
{
connectinfo.adr[0] = *adr;
connectinfo.numadr = 1;
connectinfo.istransfer = true;
CL_CheckForResend();
}
void CL_Transfer_f(void)
{
char oldguid[64];
@ -3677,7 +3695,6 @@ void CL_ConnectionlessPacket (void)
Validation_Apply_Ruleset();
Netchan_Setup(NS_CLIENT, &cls.netchan, &net_from, connectinfo.qport);
CL_ParseEstablished();
Con_DPrintf ("CL_EstablishConnection: connected to %s\n", cls.servername);
cls.netchan.isnqprotocol = true;
cls.protocol = CP_NETQUAKE;
@ -3747,11 +3764,14 @@ void CL_ConnectionlessPacket (void)
else if (!strcmp(com_token, "tlsopened"))
{ //server is letting us know that its now listening for a dtls handshake.
#ifdef HAVE_DTLS
dtlscred_t cred;
Con_Printf (S_COLOR_GRAY"dtlsopened\n");
if (!CL_IsPendingServerAddress(&net_from))
return;
if (NET_DTLS_Create(cls.sockets, &net_from, cls.servername))
memset(&cred, 0, sizeof(cred));
cred.peer.name = cls.servername;
if (NET_DTLS_Create(cls.sockets, &net_from, &cred))
{
connectinfo.dtlsupgrade = DTLS_ACTIVE;
connectinfo.numadr = 1; //fixate on this resolved address.
@ -3863,21 +3883,6 @@ client_connect: //fixme: make function
#endif
CL_SendClientCommand(true, "new");
cls.state = ca_connected;
if (cls.netchan.remote_address.type != NA_LOOPBACK)
{
switch(cls.protocol)
{
case CP_QUAKEWORLD: Con_DPrintf("QW "); break;
case CP_NETQUAKE: Con_Printf ("NQ "); break;
case CP_QUAKE2: Con_Printf ("Q2 "); break;
case CP_QUAKE3: Con_Printf ("Q3 "); break;
default: break;
}
if (cls.netchan.remote_address.prot == NP_DTLS || cls.netchan.remote_address.prot == NP_TLS || cls.netchan.remote_address.prot == NP_WSS)
Con_TPrintf ("Connected (^[^2encrypted\\tip\\Any passwords will be sent securely, but may still be logged^]).\n");
else
Con_TPrintf ("Connected (^[^1plain-text\\tip\\"CON_WARNING"Do not type passwords as they can potentially be seen by network sniffers^]).\n");
}
#ifdef QUAKESPYAPI
allowremotecmd = false; // localid required now for remote cmds
#endif
@ -4060,11 +4065,6 @@ void CLNQ_ConnectionlessPacket(void)
cls.protocol = CP_NETQUAKE;
cls.state = ca_connected;
if (cls.netchan.remote_address.prot == NP_DTLS || cls.netchan.remote_address.prot == NP_TLS || cls.netchan.remote_address.prot == NP_WSS)
Con_TPrintf ("Connected (^[^2encrypted\\tip\\Any passwords will be sent securely, but may still be logged^]).\n");
else
Con_TPrintf ("Connected (^[^1plain-text\\tip\\"CON_WARNING"Do not type passwords as they can potentially be seen by network sniffers^]).\n");
total_loading_size = 100;
current_loading_size = 0;
SCR_SetLoadingStage(LS_CLIENT);
@ -4155,7 +4155,7 @@ void CL_ReadPacket(void)
if (net_message.cursize == 1 && net_message.data[0] == A2A_ACK)
Con_TPrintf ("%s: Ack (Pong)\n", NET_AdrToString(adr, sizeof(adr), &net_from));
else
Con_TPrintf ("%s: Runt packet\n", NET_AdrToString(adr, sizeof(adr), &net_from));
Con_TPrintf ("%s: Runt packet (%i bytes)\n", NET_AdrToString(adr, sizeof(adr), &net_from), net_message.cursize);
return;
}

View File

@ -3720,6 +3720,29 @@ void CL_ParseEstablished(void)
cl_dp_csqc_progscrc = 0;
cl_dp_csqc_progssize = 0;
#endif
if (cls.netchan.remote_address.type != NA_LOOPBACK)
{
const char *security;
switch(cls.protocol)
{
case CP_QUAKEWORLD: Con_DPrintf(S_COLOR_GRAY"QW "); break;
case CP_NETQUAKE: Con_Printf (S_COLOR_GRAY"NQ "); break;
case CP_QUAKE2: Con_Printf (S_COLOR_GRAY"Q2 "); break;
case CP_QUAKE3: Con_Printf (S_COLOR_GRAY"Q3 "); break;
default: break;
}
if (
#ifdef SUPPORT_ICE
(cls.netchan.remote_address.type == NA_ICE && cls.netchan.remote_address.port) ||
#endif
cls.netchan.remote_address.prot == NP_DTLS || cls.netchan.remote_address.prot == NP_TLS || cls.netchan.remote_address.prot == NP_WSS)
security = "^["S_COLOR_GREEN"encrypted\\tip\\Any passwords will be sent securely, but will still be readable by the server admin^]";
else
security = "^["S_COLOR_RED"plain-text\\tip\\"CON_WARNING"Do not type passwords as they can potentially be seen by network sniffers^]";
Con_TPrintf ("Connected to ^["S_COLOR_BLUE"%s\\type\\connect %s^] (%s).\n", cls.servername, cls.servername, security);
}
}
#ifdef NQPROT
@ -4376,7 +4399,7 @@ static void CL_ParseSoundlist (qboolean lots)
{
if (cls.demoplayback != DPB_EZTV)
{
if (CL_RemoveClientCommands("soundlist"))
if (CL_RemoveClientCommands("soundlist") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))
Con_DPrintf("Multiple soundlists\n");
// CL_SendClientCommand("soundlist %i %i", cl.servercount, n);
CL_SendClientCommand(true, soundlist_name, cl.servercount, (numsounds&0xff00) + n);
@ -4476,7 +4499,7 @@ static void CL_ParseModellist (qboolean lots)
{
if (cls.demoplayback != DPB_EZTV)
{
if (CL_RemoveClientCommands("modellist"))
if (CL_RemoveClientCommands("modellist") && !(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))
Con_DPrintf("Multiple modellists\n");
// CL_SendClientCommand("modellist %i %i", cl.servercount, n);
CL_SendClientCommand(true, modellist_name, cl.servercount, (nummodels&0xff00) + n);

View File

@ -835,6 +835,7 @@ static qboolean SL_Key (int key, emenu_t *menu)
if (server)
Master_FindRoute(server->adr);
serverpreview = SVPV_ROUTE;
return true;
}
#endif
else if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER || key == K_GP_START) //join

View File

@ -47,6 +47,9 @@ typedef enum {
#ifdef HAVE_WEBSOCKCL
NA_WEBSOCKET,
#endif
#ifdef SUPPORT_ICE
NA_ICE
#endif
} netadrtype_t;
typedef enum {
NP_DGRAM,
@ -80,6 +83,9 @@ typedef struct netadr_s
char channel[12];
} irc;
#endif
#ifdef SUPPORT_ICE
char icename[16];
#endif
#ifdef HAVE_WEBSOCKCL
char websocketurl[64];
#endif
@ -187,12 +193,17 @@ enum certprops_e
size_t NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize);
#ifdef HAVE_DTLS
qboolean NET_DTLS_Create(struct ftenet_connections_s *col, netadr_t *to, const char *hostname);
struct dtlscred_s;
struct dtlsfuncs_s;
qboolean NET_DTLS_Create(struct ftenet_connections_s *col, netadr_t *to, const struct dtlscred_s *cred);
qboolean NET_DTLS_Decode(struct ftenet_connections_s *col);
qboolean NET_DTLS_Disconnect(struct ftenet_connections_s *col, netadr_t *to);
void NET_DTLS_Timeouts(struct ftenet_connections_s *col);
extern cvar_t dtls_psk_hint, dtls_psk_user, dtls_psk_key;
#endif
#ifdef SUPPORT_ICE
neterr_t ICE_SendPacket(struct ftenet_connections_s *col, size_t length, const void *data, netadr_t *to);
#endif
extern cvar_t timeout;
extern cvar_t tls_ignorecertificateerrors; //evil evil evil.
struct ftecrypto_s;

File diff suppressed because it is too large Load Diff

View File

@ -167,7 +167,7 @@ static ssize_t (VARGS *qgnutls_record_recv)(gnutls_session_t session, void *data
static void (VARGS *qgnutls_certificate_set_verify_function)(gnutls_certificate_credentials_t cred, gnutls_certificate_verify_function *func);
static void *(VARGS *qgnutls_session_get_ptr)(gnutls_session_t session);
static void (VARGS *qgnutls_session_set_ptr)(gnutls_session_t session, void *ptr);
int (VARGS *qgnutls_session_channel_binding)(gnutls_session_t session, gnutls_channel_binding_t cbtype, gnutls_datum_t * cb);
static int (VARGS *qgnutls_session_channel_binding)(gnutls_session_t session, gnutls_channel_binding_t cbtype, gnutls_datum_t * cb);
#ifdef GNUTLS_HAVE_SYSTEMTRUST
static int (VARGS *qgnutls_certificate_set_x509_system_trust)(gnutls_certificate_credentials_t cred);
#else
@ -477,6 +477,10 @@ typedef struct
int pullerror; //adding these two because actual networking errors are not getting represented properly, at least with regard to timeouts.
int pusherror;
gnutls_certificate_credentials_t certcred;
hashfunc_t *peerhashfunc;
qbyte peerdigest[DIGEST_MAXSIZE];
qboolean challenging; //not sure this is actually needed, but hey.
void *cbctx;
neterr_t(*cbpush)(void *cbctx, const qbyte *data, size_t datasize);
@ -571,6 +575,8 @@ static qboolean QDECL SSL_CloseFile(vfsfile_t *vfs)
VFS_CLOSE(file->stream);
file->stream = NULL;
}
if (file->certcred)
qgnutls_certificate_free_credentials(file->certcred);
Z_Free(vfs);
return true;
}
@ -754,6 +760,33 @@ static int QDECL SSL_CheckCert(gnutls_session_t session)
return GNUTLS_E_CERTIFICATE_ERROR;
}
static int QDECL SSL_CheckFingerprint(gnutls_session_t session)
{ //actual certificate doesn't matter so long as it matches the hash we expect.
gnutlsfile_t *file = qgnutls_session_get_ptr (session);
unsigned int certcount, j;
const gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(session, &certcount);
if (certlist && certcount)
{
qbyte digest[DIGEST_MAXSIZE];
void *ctx = alloca(file->peerhashfunc->contextsize);
file->peerhashfunc->init(ctx);
for (j = 0; j < certcount; j++)
file->peerhashfunc->process(ctx, certlist[j].data, certlist[j].size);
file->peerhashfunc->terminate(digest, ctx);
{
vfsfile_t *f = FS_OpenVFS("/tmp/cert", "wb", FS_SYSTEM);
VFS_WRITE(f, certlist[0].data, certlist[0].size);
VFS_CLOSE(f);
}
if (!memcmp(digest, file->peerdigest, file->peerhashfunc->digestsize))
return 0;
}
Con_DPrintf(CON_ERROR "%s: rejecting certificate\n", file->certname);
return GNUTLS_E_CERTIFICATE_ERROR;
}
//return 1 to read data.
//-1 for error
//0 for not ready
@ -1352,40 +1385,48 @@ static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboole
qgnutls_server_name_set(newf->session, GNUTLS_NAME_DNS, newf->certname, strlen(newf->certname));
qgnutls_session_set_ptr(newf->session, newf);
#ifdef USE_ANON
//qgnutls_kx_set_priority (newf->session, kx_prio);
qgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred[isserver]);
#else
#ifdef HAVE_DTLS
if (datagram && !isserver)
{ //do psk as needed. we can still do the cert stuff if the server isn't doing psk.
gnutls_psk_client_credentials_t pskcred;
qgnutls_psk_allocate_client_credentials(&pskcred);
qgnutls_psk_set_client_credentials_function(pskcred, GetPSKForServer);
qgnutls_set_default_priority_append (newf->session, "+ECDHE-PSK:+DHE-PSK:+PSK", NULL, 0);
qgnutls_credentials_set(newf->session, GNUTLS_CRD_PSK, pskcred);
}
else if (datagram && isserver && (*dtls_psk_user.string || servercertfail))
{ //offer some arbitrary PSK for dtls clients.
gnutls_psk_server_credentials_t pskcred;
qgnutls_psk_allocate_server_credentials(&pskcred);
qgnutls_psk_set_server_credentials_function(pskcred, GetPSKForUser);
if (*dtls_psk_hint.string)
qgnutls_psk_set_server_credentials_hint(pskcred, dtls_psk_hint.string);
qgnutls_set_default_priority_append (newf->session, ("-KX-ALL:+ECDHE-PSK:+DHE-PSK:+PSK")+(servercertfail?0:8), NULL, 0);
qgnutls_credentials_set(newf->session, GNUTLS_CRD_PSK, pskcred);
}
else
#endif
if (newf->certcred)
{
// Use default priorities for regular tls sessions
qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, newf->certcred);
qgnutls_set_default_priority (newf->session);
}
else
{
#ifdef USE_ANON
//qgnutls_kx_set_priority (newf->session, kx_prio);
qgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred[isserver]);
#else
#ifdef HAVE_DTLS
if (datagram && !isserver)
{ //do psk as needed. we can still do the cert stuff if the server isn't doing psk.
gnutls_psk_client_credentials_t pskcred;
qgnutls_psk_allocate_client_credentials(&pskcred);
qgnutls_psk_set_client_credentials_function(pskcred, GetPSKForServer);
qgnutls_set_default_priority_append (newf->session, "+ECDHE-PSK:+DHE-PSK:+PSK", NULL, 0);
qgnutls_credentials_set(newf->session, GNUTLS_CRD_PSK, pskcred);
}
else if (datagram && isserver && (*dtls_psk_user.string || servercertfail))
{ //offer some arbitrary PSK for dtls clients.
gnutls_psk_server_credentials_t pskcred;
qgnutls_psk_allocate_server_credentials(&pskcred);
qgnutls_psk_set_server_credentials_function(pskcred, GetPSKForUser);
if (*dtls_psk_hint.string)
qgnutls_psk_set_server_credentials_hint(pskcred, dtls_psk_hint.string);
qgnutls_set_default_priority_append (newf->session, ("-KX-ALL:+ECDHE-PSK:+DHE-PSK:+PSK")+(servercertfail?0:8), NULL, 0);
qgnutls_credentials_set(newf->session, GNUTLS_CRD_PSK, pskcred);
}
else
#endif
if (xcred[isserver])
qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, xcred[isserver]);
{
// Use default priorities for regular tls sessions
qgnutls_set_default_priority (newf->session);
}
#endif
if (xcred[isserver])
qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, xcred[isserver]);
}
// tell gnutls how to send/receive data
qgnutls_transport_set_ptr (newf->session, newf);
@ -1542,7 +1583,7 @@ static void GNUDTLS_DestroyContext(void *ctx)
{
SSL_Close(ctx);
}
static void *GNUDTLS_CreateContext(const char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver)
static void *GNUDTLS_CreateContext(const dtlscred_t *credinfo, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver)
{
gnutlsfile_t *newf;
@ -1559,7 +1600,19 @@ static void *GNUDTLS_CreateContext(const char *remotehost, void *cbctx, neterr_t
// Sys_Printf("DTLS_CreateContext: server=%i\n", isserver);
SSL_SetCertificateName(newf, remotehost);
if (credinfo && credinfo->local.cert && credinfo->local.key && credinfo->peer.hash)
{
gnutls_datum_t pub = {credinfo->local.cert, credinfo->local.certsize},
priv = {credinfo->local.key, credinfo->local.keysize};
qgnutls_certificate_allocate_credentials (&newf->certcred);
qgnutls_certificate_set_x509_key_mem(newf->certcred, &pub, &priv, GNUTLS_X509_FMT_DER);
newf->peerhashfunc = credinfo->peer.hash;
memcpy(newf->peerdigest, credinfo->peer.digest, newf->peerhashfunc->digestsize);
qgnutls_certificate_set_verify_function (newf->certcred, SSL_CheckFingerprint);
}
SSL_SetCertificateName(newf, credinfo?credinfo->peer.name:NULL);
if (!SSL_InitConnection(newf, isserver, true))
{
@ -1613,7 +1666,7 @@ static neterr_t GNUDTLS_Received(void *ctx, sizebuf_t *message)
message->data, message->cursize,
&f->prestate);
if (ret < 0)
if (ret == GNUTLS_E_BAD_COOKIE)
{
qgnutls_dtls_cookie_send(&cookie_key,
&cli_addr, sizeof(cli_addr),
@ -1621,6 +1674,8 @@ static neterr_t GNUDTLS_Received(void *ctx, sizebuf_t *message)
(gnutls_transport_ptr_t)f, DTLS_Push);
return NETERR_CLOGGED;
}
else if (ret < 0)
return NETERR_NOROUTE;
f->challenging = false;
qgnutls_dtls_prestate_set(f->session, &f->prestate);
@ -1741,6 +1796,80 @@ static neterr_t GNUDTLS_Timeouts(void *ctx)
return NETERR_SENT;
}
static qboolean GNUDTLS_GenTempCertificate(const char *subject, struct dtlslocalcred_s *qcred)
{
gnutls_datum_t priv = {NULL}, pub = {NULL};
gnutls_x509_privkey_t key;
gnutls_x509_crt_t cert;
char serial[64];
char randomsub[32+1];
const char *errstr;
gnutls_pk_algorithm_t privalgo = GNUTLS_PK_RSA;
int ret;
qgnutls_x509_privkey_init(&key);
ret = qgnutls_x509_privkey_generate(key, privalgo, qgnutls_sec_param_to_pk_bits(privalgo, GNUTLS_SEC_PARAM_HIGH), 0);
if (ret < 0)
Con_Printf(CON_ERROR"gnutls_x509_privkey_generate failed: %i\n", ret);
ret = qgnutls_x509_privkey_export2(key, GNUTLS_X509_FMT_DER, &priv);
if (ret < 0)
Con_Printf(CON_ERROR"gnutls_x509_privkey_export2 failed: %i\n", ret);
//stoopid browsers insisting that serial numbers are different even on throw-away self-signed certs.
//we should probably just go and make our own root ca/master. post it a cert and get a signed one (with sequential serial) back or something.
//we'll probably want something like that for client certs anyway, for stat tracking.
Q_snprintfz(serial, sizeof(serial), "%u", (unsigned)time(NULL));
qgnutls_x509_crt_init(&cert);
qgnutls_x509_crt_set_version(cert, 1);
qgnutls_x509_crt_set_activation_time(cert, time(NULL)-1);
qgnutls_x509_crt_set_expiration_time(cert, time(NULL)+(time_t)10*365*24*60*60);
qgnutls_x509_crt_set_serial(cert, serial, strlen(serial));
// qgnutls_x509_crt_set_key_usage(cert, GNUTLS_KEY_DIGITAL_SIGNATURE);
if (!subject)
{
qbyte tmp[16];
Sys_RandomBytes(tmp, sizeof(tmp));
randomsub[Base16_EncodeBlock(tmp, sizeof(tmp), randomsub, sizeof(randomsub))] = 0;
subject = randomsub;
}
if (qgnutls_x509_crt_set_dn(cert, va("CN=%s", subject), &errstr) < 0)
Con_Printf(CON_ERROR"gnutls_x509_crt_set_dn failed: %s\n", errstr);
if (qgnutls_x509_crt_set_issuer_dn(cert, va("CN=%s", subject), &errstr) < 0)
Con_Printf(CON_ERROR"gnutls_x509_crt_set_issuer_dn failed: %s\n", errstr);
// qgnutls_x509_crt_set_key_usage(cert, GNUTLS_KEY_KEY_ENCIPHERMENT|GNUTLS_KEY_DATA_ENCIPHERMENT|);
qgnutls_x509_crt_set_key(cert, key);
/*sign it with our private key*/
{
gnutls_privkey_t akey;
qgnutls_privkey_init(&akey);
qgnutls_privkey_import_x509(akey, key, GNUTLS_PRIVKEY_IMPORT_COPY);
ret = qgnutls_x509_crt_privkey_sign(cert, cert, akey, GNUTLS_DIG_SHA256, 0);
if (ret < 0)
Con_Printf(CON_ERROR"gnutls_x509_crt_privkey_sign failed: %i\n", ret);
qgnutls_privkey_deinit(akey);
}
ret = qgnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_DER, &pub);
qgnutls_x509_crt_deinit(cert);
qgnutls_x509_privkey_deinit(key);
if (ret < 0)
Con_Printf(CON_ERROR"gnutls_x509_crt_export2 failed: %i\n", ret);
//okay, we have them in memory, make sure the rest of the engine can play with it.
qcred->certsize = pub.size;
memcpy(qcred->cert = Z_Malloc(pub.size), pub.data, pub.size);
qcred->keysize = priv.size;
memcpy(qcred->key = Z_Malloc(priv.size), priv.data, priv.size);
(*qgnutls_free)(priv.data);
(*qgnutls_free)(pub.data);
return true;
}
static const dtlsfuncs_t dtlsfuncs_gnutls =
{
GNUDTLS_CreateContext,
@ -1749,6 +1878,8 @@ static const dtlsfuncs_t dtlsfuncs_gnutls =
GNUDTLS_Transmit,
GNUDTLS_Received,
GNUDTLS_Timeouts,
NULL,
GNUDTLS_GenTempCertificate
};
static const dtlsfuncs_t *GNUDTLS_InitServer(void)
{
@ -1760,6 +1891,8 @@ static const dtlsfuncs_t *GNUDTLS_InitServer(void)
}
static const dtlsfuncs_t *GNUDTLS_InitClient(void)
{
if (!SSL_InitGlobal(false))
return NULL;
return &dtlsfuncs_gnutls;
}
#else

View File

@ -556,6 +556,15 @@ qboolean NET_CompareAdr (netadr_t *a, netadr_t *b)
}
#endif
#ifdef SUPPORT_ICE
if (a->type == NA_ICE)
{
if (!strcmp(a->address.icename, b->address.icename))
return true;
return false;
}
#endif
if (a->type == NA_INVALID && a->prot)
return true; //mneh...
@ -673,6 +682,15 @@ qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b)
}
#endif
#ifdef SUPPORT_ICE
if (a->type == NA_ICE)
{
if (!strcmp(a->address.icename, b->address.icename))
return true;
return false;
}
#endif
if (a->type == NA_INVALID && a->prot)
return true; //mneh...
@ -1007,6 +1025,12 @@ char *NET_AdrToString (char *s, int len, netadr_t *a)
break;
#endif
#ifdef SUPPORT_ICE
case NA_ICE:
snprintf (s, len, "%s[%s]", prot, a->address.icename);
break;
#endif
default:
if (a->prot == NP_RTC_TCP || a->prot == NP_RTC_TLS)
Q_strncpyz(s, prot, len);
@ -1144,6 +1168,12 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a)
return NET_AdrToString(s, len, a);
#endif
#ifdef SUPPORT_ICE
case NA_ICE:
snprintf (s, len, "%s[%s]", prot, a->address.icename);
break;
#endif
default:
if (a->prot == NP_RTC_TCP || a->prot == NP_RTC_TLS)
Q_strncpyz(s, prot, len);
@ -1898,6 +1928,9 @@ void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits)
#endif
#ifdef IRCCONNECT
case NA_IRC:
#endif
#ifdef SUPPORT_ICE
case NA_ICE:
#endif
break;
@ -1916,29 +1949,37 @@ int ParsePartialIP(const char *s, netadr_t *a)
memset (a, 0, sizeof(*a));
//if its ::ffff:a.b.c.d then parse it as ipv4 by just skipping the prefix.
//we ought to leave it as ipv6, but any printing will show it as ipv4 anyway.
if (!strncasecmp(s, "::ffff:", 7) && strchr(s+7, '.') && !strchr(s+7, ':'))
s += 7;
//multiple colons == ipv6
//single colon = ipv4:port
colon = strchr(s, ':');
if (colon && strchr(colon+1, ':'))
{
qbyte *address = a->address.ip6;
unsigned long tmp;
int gapstart = -1; //in bytes...
bits = 0;
//FIXME: check for ::ffff:a.b.c.d
//FIXME: check for xx::xx
if (s[0] == ':' && s[1] == ':' && !s[2])
{
s+=2;
bits = 1;
}
else while(*s)
while(*s)
{
tmp = strtoul(s, &colon, 16);
if (tmp > 0xffff)
return 0; //invalid
*address++ = (tmp>>8)&0xff;
*address++ = (tmp>>0)&0xff;
bits += 16;
if (colon == s)
{
if (bits)
return 0;
}
else
{
if (tmp > 0xffff)
return 0; //invalid
*address++ = (tmp>>8)&0xff;
*address++ = (tmp>>0)&0xff;
bits += 16;
}
if (bits == 128)
{
@ -1948,16 +1989,35 @@ int ParsePartialIP(const char *s, netadr_t *a)
}
//double-colon ends it here. we can't parse xx::xx
//double-colon is a gap (or partial end).
//hopefully the last 64 bits or whatever will be irrelevant anyway, so such addresses won't be common
if (colon[0] == ':' && colon[1] == ':' && !colon[2])
break;
if (*colon == ':')
if (colon[0] == ':' && colon[1] == ':')
{
if (gapstart >= 0)
return 0; //only one gap...
if (!colon[2])
break; //nothing after. its partial.
gapstart = bits/8;
colon+=2;
}
else if (*colon == ':' && bits)
colon++;
else if (*colon)
return 0; //gibberish here...
else
return 0; //don't allow it if it just ended without a double-colon.
break; //end of address... anything more is a partial.
s = colon;
}
if (gapstart >= 0)
{
int tailsize = (bits/8)-gapstart; //bits to move to the end
int gapsize = 16 - gapstart - tailsize;
memmove(a->address.ip6+gapstart+gapsize, a->address.ip6+gapstart, tailsize); //move the bits we found to the end
memset(a->address.ip6+gapstart, 0, gapsize); //and make sure the gap is cleared
bits = 128; //found it all, or something.
}
if (!bits)
bits = 1; //FIXME: return of 0 is an error, but :: is 0-length... lie.
a->type = NA_IPV6;
a->port = 0;
}
@ -3018,7 +3078,7 @@ static neterr_t FTENET_DTLS_DoSendPacket(void *cbctx, const qbyte *data, size_t
struct dtlspeer_s *peer = cbctx;
return NET_SendPacketCol(peer->col, length, data, &peer->addr);
}
qboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to, const char *hostname)
qboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to, const dtlscred_t *cred)
{
extern cvar_t timeout;
struct dtlspeer_s *peer;
@ -3040,7 +3100,7 @@ qboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to, const char *ho
else
peer->funcs = DTLS_InitClient();
if (peer->funcs)
peer->dtlsstate = peer->funcs->CreateContext(hostname, peer, FTENET_DTLS_DoSendPacket, col->islisten);
peer->dtlsstate = peer->funcs->CreateContext(cred, peer, FTENET_DTLS_DoSendPacket, col->islisten);
peer->timeout = realtime+timeout.value;
if (peer->dtlsstate)
@ -3171,6 +3231,7 @@ qboolean NET_DTLS_Decode(ftenet_connections_t *col)
break;
case NETERR_SENT:
//we decoded it properly
net_from.prot = NP_DTLS;
break;
}
net_from.prot = NP_DTLS;
@ -3231,7 +3292,7 @@ static qboolean FTENET_AddToCollection_Ptr(ftenet_connections_t *col, const char
if (col->conn[i])
if (*col->conn[i]->name && !strcmp(col->conn[i]->name, name))
{
if (adr && adr->type != NA_INVALID && col->islisten)
if (adr && (adr->type != NA_INVALID||adr->prot != NP_INVALID) && col->islisten)
if (col->conn[i]->ChangeLocalAddress)
{
if (col->conn[i]->ChangeLocalAddress(col->conn[i], address, adr))
@ -5179,6 +5240,10 @@ qboolean FTENET_TCP_ParseHTTPRequest(ftenet_tcp_connection_t *con, ftenet_tcp_st
{
if (o != st && o->clienttype == TCPC_WEBRTC_HOST && !strcmp(st->webrtc.resource, o->webrtc.resource))
{
net_message_buffer[0] = ICEMSG_NAMEINUSE;
strcpy(net_message_buffer+1, st->webrtc.resource);
FTENET_TCP_WebSocket_Splurge(st, WS_PACKETTYPE_BINARYFRAME, net_message_buffer, strlen(net_message_buffer));
*st->webrtc.resource = 0; //don't trigger shutdown broadcasts to valid clients.
return false; //conflict! can't have two servers listening on the same url
}
@ -7862,6 +7927,11 @@ void NET_ReadPackets (ftenet_connections_t *collection)
while ((p = collection->delayed_packets) && (int)(Sys_Milliseconds()-p->sendtime) > 0)
{
collection->delayed_packets = p->next;
#ifdef SUPPORT_ICE
if (p->dest.type == NA_ICE)
NET_SendPacketCol (collection, p->cursize, p->data, &p->dest);
else
#endif
#ifdef HAVE_DTLS
if (p->dest.prot == NP_DTLS)
FTENET_DTLS_SendPacket(collection, p->cursize, p->data, &p->dest);
@ -8010,6 +8080,10 @@ neterr_t NET_SendPacket (ftenet_connections_t *collection, int length, const voi
}
#endif
#ifdef SUPPORT_ICE
if (to->type == NA_ICE)
return ICE_SendPacket(collection, length, data, to);
#endif
#ifdef HAVE_DTLS
if (to->prot == NP_DTLS)
return FTENET_DTLS_SendPacket(collection, length, data, to);
@ -8031,11 +8105,16 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char
#ifdef HAVE_DTLS
adr->prot = NP_DGRAM;
if (NET_EnsureRoute(collection, routename, host, adr))
if (NET_DTLS_Create(collection, adr, host))
{
dtlscred_t cred;
memset(&cred, 0, sizeof(cred));
cred.peer.name = host;
if (NET_DTLS_Create(collection, adr, &cred))
{
adr->prot = NP_DTLS;
return true;
}
}
adr->prot = NP_DTLS;
#endif
return false;

View File

@ -266,8 +266,9 @@ enum iceproto_e
};
enum icemode_e
{
ICEM_RAW, //not actually interactive beyond a simple handshake.
ICEM_ICE //rfc5245. meant to be able to holepunch, but not implemented properly yet.
ICEM_RAW, //not actually interactive beyond a simple handshake.
ICEM_ICE, //rfc5245. meant to be able to holepunch, but not implemented properly yet.
ICEM_WEBRTC, //IP+UDP+ICE+DTLS+SCTP... no more layers? :o
};
enum icestate_e
{
@ -346,15 +347,33 @@ enum hashvalidation_e
};
struct dtlsfuncs_s;
#ifdef HAVE_DTLS
typedef struct dtlscred_s
{
struct dtlslocalcred_s
{
void *cert;
size_t certsize;
void *key;
size_t keysize;
} local;
struct dtlspeercred_s
{
const char *name; //cert must match this if specified
hashfunc_t *hash; //if set peer's cert MUST match the specified digest (with this hash function)
qbyte digest[DIGEST_MAXSIZE];
} peer;
} dtlscred_t;
typedef struct dtlsfuncs_s
{
void *(*CreateContext)(const char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver); //if remotehost is null then their certificate will not be validated.
void *(*CreateContext)(const dtlscred_t *credinfo, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver); //the certificate to advertise.
qboolean (*CheckConnection)(void *cbctx, void *peeraddr, size_t peeraddrsize, void *indata, size_t insize, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), void (*EstablishTrueContext)(void **cbctx, void *state));
void (*DestroyContext)(void *ctx);
neterr_t (*Transmit)(void *ctx, const qbyte *data, size_t datasize);
neterr_t (*Received)(void *ctx, sizebuf_t *message); //operates in-place...
neterr_t (*Timeouts)(void *ctx);
void (*GetPeerCertificate)(void *ctx);
qboolean (*GenTempCertificate)(const char *subject, struct dtlslocalcred_s *cred);
} dtlsfuncs_t;
const dtlsfuncs_t *DTLS_InitServer(void);
const dtlsfuncs_t *DTLS_InitClient(void);
@ -428,6 +447,7 @@ enum icemsgtype_s
ICEMSG_ACCEPT=5, //go go go (response from offer)
ICEMSG_SERVERINFO=6,//server->broker (for advertising the server properly)
ICEMSG_SERVERUPDATE=7,//broker->browser (for querying available server lists)
ICEMSG_NAMEINUSE=8, //requested resource is unavailable.
};
enum websocketpackettype_e

View File

@ -3144,6 +3144,10 @@ void SV_Voice_UnmuteAll_f(void)
{
host_client->voice_active = true;
}
#else
void SV_Voice_UnmuteAll_f(void)
{ //no-op
}
#endif
qboolean SV_FindRemotePackage(const char *package, char *url, size_t urlsize)
@ -6385,8 +6389,8 @@ ucmd_t ucmds[] =
{"voicetarg", SV_Voice_Target_f},
{"vignore", SV_Voice_Ignore_f}, /*ignore/mute specific player*/
{"muteall", SV_Voice_MuteAll_f}, /*disables*/
{"unmuteall", SV_Voice_UnmuteAll_f}, /*reenables*/
#endif
{"unmuteall", SV_Voice_UnmuteAll_f}, /*reenables*/
{NULL, NULL}
};

View File

@ -774,6 +774,8 @@ mergeInto(LibraryManager.library,
return -1;
if (s.con == 0)
return 0; //not connected yet
if (len == 0)
return 0; //...
s.ws.send(HEAPU8.subarray(data, data+len));
return len;
},
@ -838,60 +840,35 @@ mergeInto(LibraryManager.library,
s.ws.binaryType = 'arraybuffer';
s.ws.onclose = function(event)
{
//console.log("webrtc datachannel closed:")
//console.log(event);
s.con = 0;
s.err = 1;
};
s.ws.onopen = function(event)
{
//console.log("webrtc datachannel opened:");
//console.log(event);
s.con = 1;
};
s.ws.onmessage = function(event)
{
//console.log("webrtc datachannel message:");
//console.log(event);
assert(typeof event.data !== 'string' && event.data.byteLength);
s.inq.push(new Uint8Array(event.data));
};
s.pc.onicecandidate = function(e)
{
//console.log("onicecandidate: ");
//console.log(e);
var desc;
if (1)
desc = JSON.stringify(e.candidate);
else
desc = e.candidate.candidate;
if (desc == null)
return; //no more...
s.callcb(4, desc);
};
s.pc.oniceconnectionstatechange = function(e)
{
//console.log("oniceconnectionstatechange: ");
//console.log(e);
};
s.pc.onaddstream = function(e)
{
//console.log("onaddstream: ");
//console.log(e);
};
s.pc.ondatachannel = function(e)
{
//console.log("ondatachannel: ");
//console.log(e);
s.recvchan = e.channel;
s.recvchan.binaryType = 'arraybuffer';
s.recvchan.onmessage = s.ws.onmessage;
};
s.pc.onnegotiationneeded = function(e)
{
//console.log("onnegotiationneeded: ");
//console.log(e);
s.recvchan = e.channel;
s.recvchan.binaryType = 'arraybuffer';
s.recvchan.onmessage = s.ws.onmessage;
};
if (clientside)
@ -900,8 +877,6 @@ mergeInto(LibraryManager.library,
function(desc)
{
s.pc.setLocalDescription(desc);
// console.log("gotlocaldescription: ");
// console.log(desc);
if (1)
desc = JSON.stringify(desc);
@ -912,8 +887,6 @@ mergeInto(LibraryManager.library,
},
function(event)
{
// console.log("createOffer error:");
// console.log(event);
s.err = 1;
}
);
@ -945,8 +918,6 @@ mergeInto(LibraryManager.library,
function(desc)
{
s.pc.setLocalDescription(desc);
// console.log("gotlocaldescription: ");
// console.log(desc);
if (1)
desc = JSON.stringify(desc);
@ -957,7 +928,6 @@ mergeInto(LibraryManager.library,
},
function(event)
{
// console.log("createAnswer error:" + event.toString());
s.err = 1;
}
);
@ -977,8 +947,6 @@ mergeInto(LibraryManager.library,
desc = JSON.parse(offer);
else
desc = {candidate:offer, sdpMid:null, sdpMLineIndex:0};
//console.log("addIceCandidate:");
//console.log(desc);
s.pc.addIceCandidate(desc);
} catch(err) { console.log(err); }
},

View File

@ -13,7 +13,7 @@ static cvar_t *pdtls_psk_hint, *pdtls_psk_user, *pdtls_psk_key;
#include "openssl/err.h"
#include "openssl/conf.h"
#define assert(c) {if (!(c)) Con_Printf("assert failed: "STRINGIFY(c)"\n");}
#define assert(c) do{if (!(c)) Con_Printf("assert failed: "STRINGIFY(c)"\n");}while(0)
static qboolean OSSL_Init(void);
static int ossl_fte_certctx;
@ -21,6 +21,9 @@ struct fte_certctx_s
{
const char *peername;
qboolean dtls;
hashfunc_t *hash; //if set peer's cert MUST match the specified digest (with this hash function)
qbyte digest[DIGEST_MAXSIZE];
};
static struct
@ -221,6 +224,27 @@ static int OSSL_Verify_Peer(int preverify_ok, X509_STORE_CTX *x509_ctx)
SSL *ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
struct fte_certctx_s *uctx = SSL_get_ex_data(ssl, ossl_fte_certctx);
if (uctx->hash)
{ //our special 'must-match-digest' mode without any other kind of trust.
X509* cert = X509_STORE_CTX_get_current_cert(x509_ctx);
size_t blobsize;
qbyte *blob;
qbyte *end;
qbyte digest[DIGEST_MAXSIZE];
void *hctx = alloca(uctx->hash->contextsize);
blobsize = i2d_X509(cert, NULL);
blob = alloca(blobsize);
end = blob;
i2d_X509(cert, &end);
uctx->hash->init(hctx);
uctx->hash->process(hctx, blob, blobsize);
uctx->hash->terminate(digest, hctx);
//return 1 for success
return !memcmp(digest, uctx->digest, uctx->hash->digestsize);
}
if(preverify_ok == 0)
{
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
@ -279,6 +303,7 @@ static int OSSL_Verify_Peer(int preverify_ok, X509_STORE_CTX *x509_ctx)
}
if (netfuncs->CertLog_ConnectOkay && netfuncs->CertLog_ConnectOkay(uctx->peername, blob, blobsize, probs))
return 1; //ignore the errors...
return 0; //allow it.
}
#endif
}
@ -708,10 +733,11 @@ unsigned int OSSL_CL_Validate_PSK(SSL *ssl, const char *hint, char *identity, un
return 0; //we don't know what to report.
}
static void *OSSL_CreateContext(const char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver)
static void *OSSL_CreateContext(const dtlscred_t *cred, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver)
{ //if remotehost is null then their certificate will not be validated.
ossldtls_t *n;
BIO *sink;
const char *remotehost = cred?cred->peer.name:NULL;
if (!remotehost)
remotehost = "";
@ -724,6 +750,8 @@ static void *OSSL_CreateContext(const char *remotehost, void *cbctx, neterr_t(*p
n->cert.peername = OSSL_SetCertificateName((char*)(n+1), remotehost);
n->cert.dtls = true;
n->cert.hash = cred->peer.hash;
memcpy(n->cert.digest, cred->peer.digest, sizeof(cred->peer.digest));
if (n->ctx)
{
@ -731,11 +759,25 @@ static void *OSSL_CreateContext(const char *remotehost, void *cbctx, neterr_t(*p
SSL_CTX_set_session_cache_mode(n->ctx, SSL_SESS_CACHE_OFF);
SSL_CTX_set_verify(n->ctx, SSL_VERIFY_PEER, OSSL_Verify_Peer);
SSL_CTX_set_verify(n->ctx, SSL_VERIFY_PEER|(cred->peer.hash?SSL_VERIFY_FAIL_IF_NO_PEER_CERT:0), OSSL_Verify_Peer);
SSL_CTX_set_verify_depth(n->ctx, 5);
SSL_CTX_set_options(n->ctx, SSL_OP_NO_COMPRESSION); //compression allows guessing the contents of the stream somehow.
SSL_CTX_set_options(n->ctx, SSL_OP_NO_COMPRESSION| //compression allows guessing the contents of the stream somehow.
SSL_OP_NO_RENEGOTIATION);
if (isserver)
if (cred->local.certsize||cred->local.keysize)
{
X509 *cert = NULL;
EVP_PKEY *key = NULL;
const unsigned char *ffs;
ffs = cred->local.cert;
d2i_X509(&cert, &ffs, cred->local.certsize);
SSL_CTX_use_certificate(n->ctx, cert);
ffs = cred->local.key;
d2i_PrivateKey(EVP_PKEY_RSA, &key, &ffs, cred->local.keysize);
SSL_CTX_use_PrivateKey(n->ctx, key);
}
else if (isserver)
{
if (*pdtls_psk_user->string)
{
@ -821,7 +863,7 @@ qboolean OSSL_CheckConnection(void *cbctx, void *peeraddr, size_t peeraddrsize,
if (!pending)
{
pending = OSSL_CreateContext("localhost", cbctx, push, true);
pending = OSSL_CreateContext(NULL, cbctx, push, true);
SSL_CTX_set_cookie_generate_cb(pending->ctx, OSSL_GenCookie);
SSL_CTX_set_cookie_verify_cb(pending->ctx, OSSL_VerifyCookie);
@ -929,6 +971,50 @@ static neterr_t OSSL_Timeouts(void *ctx)
return OSSL_Received(ctx, NULL);
}
qboolean OSSL_GenTempCertificate(const char *subject, struct dtlslocalcred_s *cred)
{
EVP_PKEY*pkey = EVP_PKEY_new();
RSA *rsa = RSA_new();
BIGNUM *pkexponent = BN_new();
qbyte *ffs;
//The pseudo-random number generator must be seeded prior to calling RSA_generate_key_ex().
BN_set_word(pkexponent, RSA_F4);
RSA_generate_key_ex(rsa, 2048, pkexponent, NULL);
BN_free(pkexponent);
EVP_PKEY_assign_RSA(pkey, rsa);
cred->keysize = i2d_PrivateKey(pkey, NULL);
cred->key = ffs = plugfuncs->Malloc(cred->keysize);
cred->keysize = i2d_PrivateKey(pkey, &ffs);
{
X509 *x509 = X509_new();
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 365*24*60*60); //lots of validity
X509_set_pubkey(x509, pkey);
{
X509_NAME *name = X509_get_subject_name(x509);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (subject?subject:"localhost"), -1, -1, 0);
X509_set_issuer_name(x509, name);
}
X509_sign(x509, pkey, EVP_sha1());
cred->certsize = i2d_X509(x509, NULL);
cred->cert = ffs = plugfuncs->Malloc(cred->certsize);
cred->certsize = i2d_X509(x509, &ffs);
X509_free(x509);
}
EVP_PKEY_free(pkey); //also frees the rsa pointer.
return true;
}
static dtlsfuncs_t ossl_dtlsfuncs =
{
OSSL_CreateContext,
@ -936,7 +1022,9 @@ static dtlsfuncs_t ossl_dtlsfuncs =
OSSL_DestroyContext,
OSSL_Transmit,
OSSL_Received,
OSSL_Timeouts
OSSL_Timeouts,
NULL,
OSSL_GenTempCertificate,
};
static const dtlsfuncs_t *OSSL_InitClient(void)
{