Add TURN support to our ICE implementation, along with a few fixups. Must be configured with 'net_ice_servers turn:foo?user=foo?auth=bar'. space-separated list, can also accept additional 'stun:' servers.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6285 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2022-07-28 02:16:31 +00:00
parent 91ea3ed518
commit 3f85c9793c
14 changed files with 1947 additions and 347 deletions

View file

@ -1257,7 +1257,11 @@ void CL_CheckForResend (void)
q3->cl.SendAuthPacket(cls.sockets, to);
#endif
if (connectinfo.istransfer || connectinfo.numadr>1)
if ((connectinfo.istransfer || connectinfo.numadr>1) && to->prot != NP_RTC_TCP && to->prot != NP_RTC_TLS
#ifdef SUPPORT_ICE
&& to->type != NA_ICE
#endif
)
Con_TPrintf ("Connecting to %s" S_COLOR_GRAY "(%s)" S_COLOR_WHITE "...\n", cls.servername, NET_AdrToString(data, sizeof(data), to));
else
Con_TPrintf ("Connecting to %s...\n", cls.servername);
@ -1368,6 +1372,8 @@ void CL_CheckForResend (void)
Con_TPrintf ("No route to host, giving up\n");
connectinfo.trying = false;
SCR_EndLoadingPlaque();
NET_CloseClient();
}
}
}
@ -3619,7 +3625,7 @@ void CL_ConnectionlessPacket (void)
NET_SendPacket (cls.sockets, strlen(pkt), pkt, &net_from);
return;
}
if (connectinfo.dtlsupgrade == DTLS_REQUIRE)
if (connectinfo.dtlsupgrade == DTLS_REQUIRE && !NET_IsEncrypted(&net_from))
{
Cvar_Set(&cl_disconnectreason, va("Server does not support/allow dtls. not connecting\n"));
connectinfo.trying = false;

View file

@ -3733,11 +3733,7 @@ void CL_ParseEstablished(void)
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)
if (NET_IsEncrypted(&cls.netchan.remote_address))
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^]";

View file

@ -1085,6 +1085,14 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info)
Cbuf_AddText(va("\nssv \"%s\"\n", c), RESTRICT_LOCAL);
return;
}
#endif
#ifdef SUPPORT_ICE
c = Info_ValueForKey(info, "ice");
if (*c && !strchr(c, ';') && !strchr(c, '\n'))
{
Cbuf_AddText(va("\nnet_ice_show \"%s\"\n", c), RESTRICT_LOCAL);
return;
}
#endif
c = Info_ValueForKey(info, "impulse");
if (*c && !strchr(c, ';') && !strchr(c, '\n'))

View file

@ -665,6 +665,7 @@ static void CertLog_Update(const char *hostname, const void *cert, size_t certsi
Z_Free(l);
}
l = Z_Malloc(sizeof(*l) + certsize + strlen(hostname));
l->trusted = trusted;
l->certsize = certsize;
l->hostname = l->cert + l->certsize;
memcpy(l->cert, cert, certsize);

View file

@ -114,6 +114,7 @@ struct sockaddr_qstorage
extern netadr_t net_local_cl_ipadr;
extern netadr_t net_from; // address of who sent the packet
extern struct ftenet_generic_connection_s *net_from_connection;
extern sizebuf_t net_message;
//#define MAX_UDP_PACKET (MAX_MSGLEN*2) // one more than msg + header
#define MAX_UDP_PACKET 8192 // one more than msg + header
@ -164,6 +165,7 @@ enum addressscope_e
enum addressscope_e NET_ClassifyAddress(netadr_t *adr, const char **outdesc);
qboolean NET_AddrIsReliable(netadr_t *adr); //hints that the protocol is reliable. if so, we don't need to wait for acks
qboolean NET_IsEncrypted(netadr_t *adr);
qboolean NET_CompareAdr (netadr_t *a, netadr_t *b);
qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b);
void NET_AdrToStringResolve (netadr_t *adr, void (*resolved)(void *ctx, void *data, size_t a, size_t b), void *ctx, size_t a, size_t b);
@ -203,6 +205,8 @@ 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);
qboolean ICE_IsEncrypted(netadr_t *to);
void ICE_Init(void);
#endif
extern cvar_t timeout;
extern cvar_t tls_ignorecertificateerrors; //evil evil evil.

File diff suppressed because it is too large Load diff

View file

@ -71,6 +71,7 @@ extern ftemanifest_t *fs_manifest;
netadr_t net_local_cl_ipadr; //still used to match local ui requests (quake/gamespy), and to generate ip reports for q3 servers (which is probably pointless).
struct ftenet_generic_connection_s *net_from_connection;
netadr_t net_from;
sizebuf_t net_message;
@ -150,7 +151,6 @@ cvar_t net_enable_websockets = CVARD("net_enable_websockets", "1", "If enabled,
#endif
#endif
#endif
extern cvar_t net_ice_exchangeprivateips, net_ice_usewebrtc;
#if defined(HAVE_DTLS)
#if defined(HAVE_SERVER)
static void QDECL NET_Enable_DTLS_Changed(struct cvar_s *var, char *oldvalue)
@ -1802,16 +1802,12 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num
if (path == s && fs_manifest->rtcbroker && *fs_manifest->rtcbroker)
{
s = fs_manifest->rtcbroker;
if (!strncmp(s, "tls://", 6))
{
s += 6;
prot = NP_RTC_TLS;
}
if (!strncmp(s, "tls://", 6) || !strncmp(s, "wss://", 6))
s+=6, prot=NP_RTC_TLS;
else if (!strncmp(s, "tcp://", 6))
{
s+=6;
prot = NP_RTC_TCP;
}
s+=6, prot=NP_RTC_TCP;
else if (!strncmp(s, "ws://", 5))
s+=5, prot=NP_RTC_TCP;
else
prot = NP_RTC_TLS; //best-practise by default.
if (pathstart)
@ -2135,6 +2131,17 @@ qboolean NET_StringToAdrMasked (const char *s, qboolean allowdns, netadr_t *a, n
return true;
}
qboolean NET_IsEncrypted(netadr_t *adr)
{
#ifdef SUPPORT_ICE
if (adr->type == NA_ICE && ICE_IsEncrypted(adr))
return true;
#endif
if (adr->prot == NP_DTLS || adr->prot == NP_TLS || adr->prot == NP_WSS)
return true;
return false;
}
// NET_CompareAdrMasked: given 3 addresses, 2 to compare with a complimentary mask,
// returns true or false if they match
//WARNING: a is typically an ipv6 address, even if its an ipv4-mapped address.
@ -2722,7 +2729,7 @@ ftenet_connections_t *FTENET_CreateCollection(qboolean listen, void(*ReadPacket)
static ftenet_generic_connection_t *FTENET_Loop_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr);
#endif
#ifdef HAVE_PACKET
static ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr);
ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr);
#endif
#ifdef TCPCONNECT
static ftenet_generic_connection_t *FTENET_TCP_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr);
@ -3949,6 +3956,7 @@ static void FTENET_Datagram_Polled(epollctx_t *ctx, unsigned int events)
while (FTENET_Datagram_GetPacket(con))
{
net_from.connum = con->connum;
net_from_connection = con;
con->owner->ReadGamePacket();
}
}
@ -5288,6 +5296,7 @@ qboolean FTENET_TCP_ParseHTTPRequest(ftenet_tcp_connection_t *con, ftenet_tcp_st
net_message.currentbit = 0;
net_from = st->remoteaddr;
net_from.connum = con->generic.connum;
net_from_connection = &con->generic;
MSG_WriteLong(&net_message, LongSwap(NETFLAG_CTL | (strlen(NQ_NETCHAN_GAMENAME)+7)));
MSG_WriteByte(&net_message, CCREQ_CONNECT);
MSG_WriteString(&net_message, NQ_NETCHAN_GAMENAME);
@ -5670,6 +5679,7 @@ static enum{
net_message.currentbit = 0;
net_from = st->remoteaddr;
net_from.connum = con->generic.connum;
net_from_connection = &con->generic;
con->generic.owner->ReadGamePacket();
return FTETCP_RETRY;
@ -5915,6 +5925,7 @@ static enum{
net_message.currentbit = 0;
net_from = st->remoteaddr;
net_from.connum = con->generic.connum;
net_from_connection = &con->generic;
con->generic.owner->ReadGamePacket();
return FTETCP_RETRY;
}
@ -7087,6 +7098,9 @@ struct ftenet_generic_connection_s *FTENET_IRCConnect_EstablishConnection(qboole
#endif
#ifdef FTE_TARGET_WEB
cvar_t net_ice_servers = CVAR("net_ice_servers", "");
cvar_t net_ice_relayonly = CVAR("net_ice_relayonly", "0");
#include "web/ftejslib.h"
typedef struct
@ -7197,9 +7211,109 @@ static void FTENET_WebRTC_Callback(void *ctxp, int ctxi, int/*enum icemsgtype_s*
*o++ = (ctxi>>8)&0xff;
memcpy(o, data, dl);
o+=dl;
//Con_Printf("To Broker: %i %i\n", evtype, ctxi);
//Con_Printf("To Broker: %i %i %s\n", evtype, ctxi, data);
emscriptenfte_ws_send(wcs->brokersock, net_message_buffer, o-net_message_buffer);
}
static int FTENET_WebRTC_Create(qboolean initiator, ftenet_websocket_connection_t *wsc, int clid)
{
char config[4096], tmp[256];
qboolean first = true;
const char *servers;
*config = 0;
Q_strncatz(config, "{\"iceServers\":[", sizeof(config));
{
/*
rtc://broker/id
rtc:///id
/id
*/
char *c;
int i;
const char *brokeraddress = wsc->remoteadr.address.websocketurl;
char *pre[] = { "wss://", "ices://", "rtcs://", "tls://",
"ws://", "ice://", "rtc://", "tcp://"};
//try and clean up the prefix, if specified
for (i = countof(pre); i --> 0; )
{
if (!strncmp(brokeraddress, pre[i], strlen(pre[i])))
{
brokeraddress += strlen(pre[i]);
break;
}
}
if (*brokeraddress == '/')
{
brokeraddress = fs_manifest->rtcbroker;
for (i = countof(pre); i --> 0; )
{
if (!strncmp(brokeraddress, pre[i], strlen(pre[i])))
{
brokeraddress += strlen(pre[i]);
break;
}
}
}
Q_strncpyz(com_token, brokeraddress, sizeof(com_token));
c = strchr(com_token, '/');
if (c) *c = 0;
first = false;
Q_strncatz(config, va("\n{\"urls\":[\"stun:%s\"]}", COM_QuotedString(com_token, tmp,sizeof(tmp), true)), sizeof(config));
}
for(servers = net_ice_servers.string; (servers=COM_Parse(servers)); )
{
//we don't do the ?foo stuff properly (RFCs say only ?transport= and only for stun)
char *s = strchr(com_token, '?'), *next;
const char *transport = NULL;
const char *user = NULL;
const char *auth = NULL;
for (;s;s=next)
{
*s++ = 0;
next = strchr(s, '?');
if (next)
*next = 0;
if (!strncmp(s, "transport=", 10))
transport = s+10;
else if (!strncmp(s, "user=", 5))
user = s+5;
else if (!strncmp(s, "auth=", 5))
auth = s+5;
else if (!strncmp(s, "fam=", 4))
;
}
if (!strncmp(com_token, "turn:", 5) || !strncmp(com_token, "turns:", 6))
if (!user || !auth)
continue;
if (first)
first = false;
else
Q_strncatz(config, ",", sizeof(config));
if (transport)
Q_strncatz(config, va("\n{\"urls\":[\"%s?transport=%s\"]", COM_QuotedString(com_token, tmp,sizeof(tmp), true), transport), sizeof(config));
else
Q_strncatz(config, va("\n{\"urls\":[\"%s\"]", COM_QuotedString(com_token, tmp,sizeof(tmp), true)), sizeof(config));
if (user)
Q_strncatz(config, va(",\"username\":\"%s\"", COM_QuotedString(user, tmp,sizeof(tmp), true)), sizeof(config));
if (auth)
Q_strncatz(config, va(",\"credential\":\"%s\"", COM_QuotedString(auth, tmp,sizeof(tmp), true)), sizeof(config));
Q_strncatz(config, "}", sizeof(config));
}
Q_strncatz(config, va("]"
// ",\"bundlePolicy\":\"max-bundle\""
",\"iceTransportPolicy\":\"%s\""
"}",net_ice_relayonly.ival?"relay":"all"), sizeof(config));
return emscriptenfte_rtc_create(initiator, wsc, clid, FTENET_WebRTC_Callback, config);
}
static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
{
ftenet_websocket_connection_t *wsc = (void*)gcon;
@ -7301,14 +7415,14 @@ static qboolean FTENET_WebRTC_GetPacket(ftenet_generic_connection_t *gcon)
memcpy(&wsc->clients[cl].remoteadr, &wsc->remoteadr, sizeof(netadr_t));
Q_strncatz(wsc->clients[cl].remoteadr.address.websocketurl, id, sizeof(wsc->clients[cl].remoteadr.address.websocketurl));
wsc->clients[cl].remoteadr.port = htons(cl+1);
wsc->clients[cl].datasock = emscriptenfte_rtc_create(false, wsc, cl, FTENET_WebRTC_Callback);
wsc->clients[cl].datasock = FTENET_WebRTC_Create(false, wsc, cl);
}
}
else
{
if (wsc->datasock != INVALID_SOCKET)
emscriptenfte_ws_close(wsc->datasock);
wsc->datasock = emscriptenfte_rtc_create(true, wsc, cl, FTENET_WebRTC_Callback);
wsc->datasock = FTENET_WebRTC_Create(true, wsc, cl);
}
break;
case ICEMSG_OFFER: //we received an offer from a client
@ -7956,6 +8070,7 @@ void NET_ReadPackets (ftenet_connections_t *collection)
collection->bytesin += net_message.cursize;
collection->packetsin += 1;
net_from.connum = c+1;
net_from_connection = collection->conn[c];
collection->ReadGamePacket();
}
}
@ -8013,9 +8128,10 @@ static neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length,
if (to->connum)
{
if (collection->conn[to->connum-1])
i = to->connum-1;
if (i < MAX_CONNECTIONS && collection->conn[i])
{
err = collection->conn[to->connum-1]->SendPacket(collection->conn[to->connum-1], length, data, to);
err = collection->conn[i]->SendPacket(collection->conn[i], length, data, to);
if (err != NETERR_NOROUTE)
{
/*if (err == NETERR_DISCONNECTED)
@ -8030,6 +8146,7 @@ static neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length,
return err;
}
}
return NETERR_NOROUTE;
}
for (i = 0; i < MAX_CONNECTIONS; i++)
@ -8942,8 +9059,7 @@ void NET_Init (void)
#endif
#ifdef SUPPORT_ICE
Cvar_Register(&net_ice_exchangeprivateips, "networking");
Cvar_Register(&net_ice_usewebrtc, "networking");
ICE_Init();
#endif
#if defined(HAVE_CLIENT)||defined(HAVE_SERVER)
@ -9098,6 +9214,8 @@ cvar_t sv_port_rtc = CVARCD("sv_port_rtc", "/", SV_PortRTC_Callback, "This spec
void SVNET_RegisterCvars(void)
{
#ifdef FTE_TARGET_WEB
Cvar_Register (&net_ice_relayonly, "networking");
Cvar_Register (&net_ice_servers, "networking");
Cvar_Register (&sv_port_rtc, "networking");
// sv_port_rtc.restriction = RESTRICT_MAX;
#endif

View file

@ -281,7 +281,7 @@ struct icestate_s;
#define ICE_API_CURRENT "Internet Connectivity Establishment 0.0"
typedef struct
{
struct icestate_s *(QDECL *ICE_Create)(void *module, const char *conname, const char *peername, enum icemode_e mode, enum iceproto_e proto); //doesn't start pinging anything.
struct icestate_s *(QDECL *ICE_Create)(void *module, const char *conname, const char *peername, enum icemode_e mode, enum iceproto_e proto, qboolean initiator); //doesn't start pinging anything.
qboolean (QDECL *ICE_Set)(struct icestate_s *con, const char *prop, const char *value);
qboolean (QDECL *ICE_Get)(struct icestate_s *con, const char *prop, char *value, size_t valuesize);
struct icecandinfo_s *(QDECL *ICE_GetLCandidateInfo)(struct icestate_s *con); //retrieves candidates that need reporting to the peer.

View file

@ -4820,7 +4820,7 @@ dominping:
// packet is not from a known client
if (sv_showconnectionlessmessages.ival)
Con_Printf (S_COLOR_GRAY "%s:sequenced packet without connection\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); //hack: com_token cos we need some random temp buffer.
Con_Printf (S_COLOR_GRAY "%s:sequenced packet without connection (%i bytes)\n", NET_AdrToString (com_token, sizeof(com_token), &net_from), net_message.cursize); //hack: com_token cos we need some random temp buffer.
}
/*

View file

@ -26,7 +26,7 @@ int emscriptenfte_ws_cansend(int sockid, int extra, int maxpending); //returns f
int emscriptenfte_ws_send(int sockid, const void *data, int len); //send data to the peer. queues data. never dropped.
int emscriptenfte_ws_recv(int sockid, void *data, int len); //receive data from the peer.
int emscriptenfte_rtc_create(int clientside, void *ctxp, int ctxi, void(*cb)(void *ctxp, int ctxi, int type, const char *data)); //open a webrtc connection to a specific broker url
int emscriptenfte_rtc_create(int clientside, void *ctxp, int ctxi, void(*cb)(void *ctxp, int ctxi, int type, const char *data), const char *json_config); //open a webrtc connection to a specific broker url
void emscriptenfte_rtc_offer(int sock, const char *offer, const char *sdptype);//sets the remote sdp.
void emscriptenfte_rtc_candidate(int sock, const char *offer); //adds a remote candidate.

View file

@ -816,14 +816,12 @@ mergeInto(LibraryManager.library,
},
emscriptenfte_rtc_create__deps: ['emscriptenfte_handle_alloc'],
emscriptenfte_rtc_create : function(clientside, ctxp, ctxi, callback)
emscriptenfte_rtc_create : function(clientside, ctxp, ctxi, callback, pcconfig)
{
var pcconfig = {
iceServers:
[{
url: 'stun:stun.l.google.com:19302'
}]
};
try {
pcconfig = JSON.parse(UTF8ToString(pcconfig));
} catch(err) {pcconfig = {};}
var dcconfig = {ordered: false, maxRetransmits: 0, reliable:false};
var s = {pc:null, ws:null, inq:[], err:0, con:0, isclient:clientside, callcb:
@ -914,6 +912,7 @@ mergeInto(LibraryManager.library,
},
emscriptenfte_rtc_offer : function(sockid, offer, offertype)
{
var desc;
var s = FTEH.h[sockid];
offer = UTF8ToString(offer);
offertype = UTF8ToString(offertype);
@ -927,29 +926,35 @@ mergeInto(LibraryManager.library,
else
desc = {sdp:offer, type:offertype};
s.pc.setRemoteDescription(desc);
s.pc.setRemoteDescription(desc).then(() =>
{
if (!s.isclient)
{ //server must give a response.
s.pc.createAnswer().then(
function(desc)
{
s.pc.setLocalDescription(desc);
if (1)
desc = JSON.stringify(desc);
else
desc = desc.sdp;
s.callcb(3, desc);
},
function(event)
{
s.err = 1;
}
);
}
}, err =>
{
console.log(desc);
console.log(err);
});
} catch(err) { console.log(err); }
if (!s.isclient)
{ //server must give a response.
s.pc.createAnswer().then(
function(desc)
{
s.pc.setLocalDescription(desc);
if (1)
desc = JSON.stringify(desc);
else
desc = desc.sdp;
s.callcb(3, desc);
},
function(event)
{
s.err = 1;
}
);
}
},
emscriptenfte_rtc_candidate : function(sockid, offer)
{

View file

@ -38,7 +38,7 @@ typedef unsigned char *POINTER;
typedef unsigned short int UINT2;
/* UINT4 defines a four byte word */
typedef unsigned long int UINT4;
typedef unsigned int UINT4;

View file

@ -1312,11 +1312,11 @@ static struct ircice_s *IRC_ICE_Create(ircclient_t *irc, const char *sender, enu
return NULL;
if (!creator && type == ICEP_QWSERVER)
ice = piceapi->ICE_Create(NULL, NULL, sender, ICEM_ICE, ICEP_QWCLIENT);
ice = piceapi->ICE_Create(NULL, NULL, sender, ICEM_ICE, ICEP_QWCLIENT, creator);
else if (!creator && type == ICEP_QWCLIENT)
ice = piceapi->ICE_Create(NULL, NULL, sender, ICEM_ICE, ICEP_QWSERVER);
ice = piceapi->ICE_Create(NULL, NULL, sender, ICEM_ICE, ICEP_QWSERVER, creator);
else
ice = piceapi->ICE_Create(NULL, NULL, sender, ICEM_ICE, type);
ice = piceapi->ICE_Create(NULL, NULL, sender, ICEM_ICE, type, creator);
if (!ice)
return NULL;

View file

@ -22,7 +22,7 @@ static struct c2c_s *JCL_JingleAddContentToSession(jclient_t *jcl, struct c2c_s
}
if (piceapi)
ice = piceapi->ICE_Create(NULL, sid, with, method, mediatype);
ice = piceapi->ICE_Create(NULL, sid, with, method, mediatype, creator);
if (ice)
{
piceapi->ICE_Get(ice, "sid", generatedname, sizeof(generatedname));