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:
parent
43565e4363
commit
86d2f0e0d4
11 changed files with 1555 additions and 200 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
|
|
@ -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); }
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue