Clients can now request the master to initiate an WebRTC/ICE connection with 'sv_public 1' servers. This is primarily for the browser port. Servers now report their srflx address via the status command (with fp, ready for secure copy+paste).
This commit is contained in:
parent
92be91f416
commit
527233154c
26 changed files with 1598 additions and 570 deletions
|
@ -1026,6 +1026,7 @@ ELSE()
|
|||
engine/common/cvar.c
|
||||
engine/common/cmd.c
|
||||
engine/common/sha1.c #for websockets
|
||||
engine/common/sha2.c #for fingerprints
|
||||
engine/http/httpclient.c #for the pipe stuff
|
||||
engine/common/log.c
|
||||
engine/common/fs.c
|
||||
|
|
|
@ -187,9 +187,6 @@ cvar_t cl_gunanglex = CVAR("cl_gunanglex", "0");
|
|||
cvar_t cl_gunangley = CVAR("cl_gunangley", "0");
|
||||
cvar_t cl_gunanglez = CVAR("cl_gunanglez", "0");
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
extern cvar_t net_enable_dtls;
|
||||
#endif
|
||||
cvar_t cl_proxyaddr = CVAR("cl_proxyaddr", "");
|
||||
cvar_t cl_sendguid = CVARD("cl_sendguid", "", "Send a randomly generated 'globally unique' id to servers, which can be used by servers for score rankings and stuff. Different servers will see different guids. Delete the 'qkey' file in order to appear as a different user.\nIf set to 2, all servers will see the same guid. Be warned that this can show other people the guid that you're using.");
|
||||
cvar_t cl_downloads = CVARAFD("cl_downloads", "1", /*q3*/"cl_allowDownload", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads.");
|
||||
|
@ -614,6 +611,18 @@ static void CL_ConnectAbort(const char *format, ...)
|
|||
connectinfo.numadr = 0;
|
||||
SCR_EndLoadingPlaque();
|
||||
connectinfo.trying = false;
|
||||
|
||||
if (format)
|
||||
{
|
||||
//try and force the menu to show again. this should force the disconnectreason to show.
|
||||
if (!Key_Dest_Has(kdm_console))
|
||||
{
|
||||
#ifdef MENU_DAT
|
||||
if (!MP_Toggle(1))
|
||||
#endif
|
||||
Menu_Prompt(NULL, NULL, reason, NULL, NULL, "Okay", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -679,6 +688,7 @@ static void CL_SendConnectPacket (netadr_t *to)
|
|||
|
||||
t1 = Sys_DoubleTime ();
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
if (connectinfo.peercred.hash && net_enable_dtls.ival>0)
|
||||
{
|
||||
char cert[8192];
|
||||
|
@ -693,6 +703,7 @@ static void CL_SendConnectPacket (netadr_t *to)
|
|||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!to)
|
||||
{
|
||||
|
@ -844,7 +855,7 @@ static void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b)
|
|||
|
||||
if (!ctx->found)
|
||||
{
|
||||
CL_ConnectAbort("Bad server address \"%s\"\n", ctx->servername);
|
||||
CL_ConnectAbort("Unable to resolve server address \"%s\"\n", ctx->servername);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1293,7 +1304,7 @@ void CL_CheckForResend (void)
|
|||
connectinfo.clogged = false;
|
||||
|
||||
if (connectinfo.tries == 0 && connectinfo.nextadr < connectinfo.numadr)
|
||||
if (!NET_EnsureRoute(cls.sockets, "conn", &connectinfo.peercred, to))
|
||||
if (!NET_EnsureRoute(cls.sockets, "conn", &connectinfo.peercred, to, true))
|
||||
{
|
||||
CL_ConnectAbort ("Unable to establish connection to %s\n", cls.servername);
|
||||
return;
|
||||
|
@ -1499,8 +1510,15 @@ static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum c
|
|||
*e=0;
|
||||
if (!strncasecmp(arglist, "fp=", 3))
|
||||
{
|
||||
Base64_DecodeBlock(arglist+3, arglist+strlen(arglist), connectinfo.peercred.digest, sizeof(connectinfo.peercred.digest));
|
||||
size_t l = 8*Base64_DecodeBlock(arglist+3, arglist+strlen(arglist), connectinfo.peercred.digest, sizeof(connectinfo.peercred.digest));
|
||||
if (l <= 160)
|
||||
connectinfo.peercred.hash = &hash_sha1;
|
||||
else if (l <= 256)
|
||||
connectinfo.peercred.hash = &hash_sha2_256;
|
||||
else if (l <= 512)
|
||||
connectinfo.peercred.hash = &hash_sha2_512;
|
||||
else
|
||||
connectinfo.peercred.hash = NULL;
|
||||
}
|
||||
else
|
||||
Con_Printf(CON_WARNING"uri arg not known: \"%s\"\n", arglist);
|
||||
|
@ -3110,7 +3128,7 @@ void CL_Packet_f (void)
|
|||
|
||||
if (!cls.sockets)
|
||||
NET_InitClient(false);
|
||||
if (!NET_EnsureRoute(cls.sockets, "packet", &cred, &adr))
|
||||
if (!NET_EnsureRoute(cls.sockets, "packet", &cred, &adr, true))
|
||||
return;
|
||||
NET_SendPacket (cls.sockets, out-send, send, &adr);
|
||||
|
||||
|
@ -3432,7 +3450,7 @@ void CL_ConnectionlessPacket (void)
|
|||
if (CL_IsPendingServerAddress(&net_from))
|
||||
{
|
||||
struct dtlspeercred_s cred = {cls.servername}; //FIXME
|
||||
if (!NET_EnsureRoute(cls.sockets, "redir", &cred, &adr))
|
||||
if (!NET_EnsureRoute(cls.sockets, "redir", &cred, &adr, true))
|
||||
Con_Printf (CON_ERROR"Unable to redirect to %s\n", data);
|
||||
else
|
||||
{
|
||||
|
@ -3896,7 +3914,7 @@ void CL_ConnectionlessPacket (void)
|
|||
|
||||
memset(&cred, 0, sizeof(cred));
|
||||
cred.peer = connectinfo.peercred;
|
||||
if (NET_DTLS_Create(cls.sockets, &net_from, &cred))
|
||||
if (NET_DTLS_Create(cls.sockets, &net_from, &cred, true))
|
||||
{
|
||||
connectinfo.numadr = 1; //fixate on this resolved address.
|
||||
connectinfo.adr[0] = net_from;
|
||||
|
@ -3942,15 +3960,6 @@ client_connect: //fixme: make function
|
|||
Con_TPrintf ("ignoring connection\n");
|
||||
return;
|
||||
}
|
||||
if (net_from.type != NA_LOOPBACK)
|
||||
{
|
||||
Con_TPrintf (S_COLOR_GRAY"connection\n");
|
||||
|
||||
#ifdef HAVE_SERVER
|
||||
if (sv.state && sv.state != ss_clustermode)
|
||||
SV_UnspawnServer();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cls.state >= ca_connected)
|
||||
{
|
||||
|
@ -3968,6 +3977,15 @@ client_connect: //fixme: make function
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (net_from.type != NA_LOOPBACK)
|
||||
{
|
||||
// Con_TPrintf (S_COLOR_GRAY"connection\n");
|
||||
|
||||
#ifdef HAVE_SERVER
|
||||
if (sv.state && sv.state != ss_clustermode)
|
||||
SV_UnspawnServer();
|
||||
#endif
|
||||
}
|
||||
connectinfo.trying = false;
|
||||
cl.splitclients = 0;
|
||||
cls.protocol = connectinfo.protocol;
|
||||
|
@ -4383,10 +4401,6 @@ void CL_ReadPackets (void)
|
|||
else
|
||||
NET_ReadPackets(cls.sockets);
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
NET_DTLS_Timeouts(cls.sockets);
|
||||
#endif
|
||||
|
||||
//
|
||||
// check timeout
|
||||
//
|
||||
|
|
|
@ -1499,6 +1499,7 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload)
|
|||
if (atstage())
|
||||
{
|
||||
SCR_SetLoadingFile("newmap");
|
||||
|
||||
// if (!cl.worldmodel || cl.worldmodel->type == mod_dummy)
|
||||
// Host_EndGame("No worldmodel was loaded\n");
|
||||
Surf_NewMap (cl.worldmodel);
|
||||
|
@ -3740,7 +3741,7 @@ void CL_ParseEstablished(void)
|
|||
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);
|
||||
Con_TPrintf ("\rConnected to ^["S_COLOR_BLUE"%s\\type\\connect %s^] (%s).\n", cls.servername, cls.servername, security);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ void QDECL joyaxiscallback(cvar_t *var, char *oldvalue)
|
|||
else if (!Q_strcasecmp(end, "right") || !Q_strcasecmp(end, "turnright"))
|
||||
var->ival = 4*sign;
|
||||
else if (!Q_strcasecmp(end, "left") || !Q_strcasecmp(end, "turnleft"))
|
||||
var->ival = 4*sign*1;
|
||||
var->ival = 4*sign*-1;
|
||||
else if (!Q_strcasecmp(end, "up") || !Q_strcasecmp(end, "moveup"))
|
||||
var->ival = 5*sign;
|
||||
else if (!Q_strcasecmp(end, "down") || !Q_strcasecmp(end, "movedown"))
|
||||
|
|
|
@ -780,10 +780,9 @@ void M_Menu_GameOptions_f (void)
|
|||
info->publicgame = MC_AddCombo (menu, 64, 160, y, "Public", publicoptions, bound(0, sv_public.ival+1, 4));y+=8;
|
||||
#if !defined(FTE_TARGET_WEB) && defined(HAVE_DTLS)
|
||||
{
|
||||
extern cvar_t net_enable_dtls;
|
||||
static const char *encoptions[] =
|
||||
{
|
||||
"None",
|
||||
"Disabled",
|
||||
"Accept",
|
||||
"Request",
|
||||
"Require",
|
||||
|
@ -1143,7 +1142,6 @@ void M_Menu_Network_f (void)
|
|||
NULL
|
||||
};
|
||||
#ifdef HAVE_DTLS
|
||||
extern cvar_t net_enable_dtls;
|
||||
static const char *dtlsopts[] = {
|
||||
"Disabled",
|
||||
"Accept",
|
||||
|
|
|
@ -352,7 +352,7 @@ static void SV_Master_SingleHeartbeat(net_masterlist_t *master)
|
|||
//Note that Darkplaces clients are supposed to be able to use the qw protocol, so it should be okay to heartbeat as Darkplaces-Quake here even when not doing any nq protocols.
|
||||
//either way, custom protocols tend to require ftemaster/dpmaster so we want to heartbeat regardless.
|
||||
#if defined(NQPROT) && !defined(QUAKETC)
|
||||
if (sv_listen_dp.value || sv_listen_nq.value || strcasecmp(com_protocolname.string, "FTE-Quake"))
|
||||
// if (sv_listen_dp.value || sv_listen_nq.value || strcasecmp(com_protocolname.string, "FTE-Quake"))
|
||||
#endif
|
||||
{
|
||||
//darkplaces here refers to the master server protocol, rather than the game protocol
|
||||
|
@ -375,7 +375,7 @@ static void SV_Master_SingleHeartbeat(net_masterlist_t *master)
|
|||
if (sv_reportheartbeats.ival != 2 || !master->announced)
|
||||
{
|
||||
COM_Parse(master->cv.string);
|
||||
Con_TPrintf ("Sending heartbeat to %s (%s)\n", NET_AdrToString (adr, sizeof(adr), na), com_token);
|
||||
Con_TPrintf (S_COLOR_GRAY"Sending heartbeat to %s (%s)\n", NET_AdrToString (adr, sizeof(adr), na), com_token);
|
||||
}
|
||||
master->announced = true;
|
||||
}
|
||||
|
@ -414,7 +414,7 @@ static void SV_Master_SingleHeartbeat(net_masterlist_t *master)
|
|||
struct thr_res
|
||||
{
|
||||
qboolean success;
|
||||
netadr_t na[8];
|
||||
netadr_t na[MAX_MASTER_ADDRESSES];
|
||||
char str[1]; //trailing
|
||||
};
|
||||
static void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
|
||||
|
@ -499,7 +499,7 @@ static void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
|
|||
if (NET_AddrIsReliable(na))
|
||||
{
|
||||
struct dtlspeercred_s cred = {master->cv.string};
|
||||
NET_EnsureRoute(svs.sockets, master->cv.name, &cred, na);
|
||||
NET_EnsureRoute(svs.sockets, master->cv.name, &cred, na, true);
|
||||
}
|
||||
|
||||
//q2+qw masters are given a ping to verify that they're still up
|
||||
|
@ -527,6 +527,39 @@ static void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
|
|||
}
|
||||
Z_Free(work);
|
||||
}
|
||||
|
||||
#if defined(SUPPORT_ICE)
|
||||
struct stunheader_s
|
||||
{
|
||||
unsigned short msgtype;
|
||||
unsigned short msglen;
|
||||
unsigned int magiccookie;
|
||||
unsigned int transactid[3];
|
||||
};
|
||||
static void SV_Master_Worker_Resolved_Broker(void *ctx, void *data, size_t a, size_t b)
|
||||
{
|
||||
struct thr_res *work = data;
|
||||
if (svs.sockets && work->na[0].type != NA_INVALID) //something resolved...
|
||||
{
|
||||
struct stunheader_s msg = {htons(1), htons(sizeof(msg)-20), BigLong(0x2112a442), {42,42,42}};
|
||||
|
||||
//randomize the transaction id to avoid poisoning.
|
||||
if (!Sys_RandomBytes((qbyte*)msg.transactid, sizeof(msg.transactid)))
|
||||
{ //FIXME: not really random enough to avoid hacks. oh well.
|
||||
msg.transactid[0] = rand();
|
||||
msg.transactid[1] = rand();
|
||||
msg.transactid[2] = rand();
|
||||
}
|
||||
|
||||
svs.sockets->srflx_tid[0] = msg.transactid[0];
|
||||
svs.sockets->srflx_tid[1] = msg.transactid[1];
|
||||
svs.sockets->srflx_tid[2] = msg.transactid[2];
|
||||
|
||||
NET_SendPacket(svs.sockets, sizeof(msg), &msg, &work->na[0]);
|
||||
}
|
||||
Z_Free(work);
|
||||
}
|
||||
#endif
|
||||
//worker thread
|
||||
static void SV_Master_Worker_Resolve(void *ctx, void *data, size_t a, size_t b)
|
||||
{
|
||||
|
@ -540,12 +573,18 @@ static void SV_Master_Worker_Resolve(void *ctx, void *data, size_t a, size_t b)
|
|||
{
|
||||
str = COM_ParseOut(str, token, sizeof(token));
|
||||
if (*token)
|
||||
found += NET_StringToAdr2(token, 0, &work->na[found], MAX_MASTER_ADDRESSES-found, NULL);
|
||||
found += NET_StringToAdr2(token, 0, &work->na[found], countof(work->na)-found, NULL);
|
||||
if (first && found)
|
||||
break; //if we found one by name, don't try any fallback ip addresses.
|
||||
first = false;
|
||||
}
|
||||
work->success = !!found;
|
||||
|
||||
#if defined(SUPPORT_ICE)
|
||||
if (a==~(size_t)0)
|
||||
COM_AddWork(WG_MAIN, SV_Master_Worker_Resolved_Broker, NULL, work, a, b);
|
||||
else
|
||||
#endif
|
||||
COM_AddWork(WG_MAIN, SV_Master_Worker_Resolved, NULL, work, a, b);
|
||||
}
|
||||
|
||||
|
@ -560,7 +599,7 @@ let it know we are alive, and log information
|
|||
void SV_Master_Heartbeat (void)
|
||||
{
|
||||
int i;
|
||||
int interval = bound(90, sv_heartbeat_interval.ival, 600);
|
||||
int interval = bound(85, sv_heartbeat_interval.ival, 600);
|
||||
|
||||
if (sv_public.ival<=0 || SSV_IsSubServer())
|
||||
return;
|
||||
|
@ -605,6 +644,18 @@ void SV_Master_Heartbeat (void)
|
|||
else
|
||||
SV_Master_SingleHeartbeat(&net_masterlist[i]);
|
||||
}
|
||||
|
||||
#if defined(SUPPORT_ICE)
|
||||
if (*net_ice_broker.string)
|
||||
{
|
||||
const char *s = net_ice_broker.string;
|
||||
struct thr_res *work = Z_Malloc(sizeof(*work) + strlen(s));
|
||||
if (!strncmp(s, "tls://", 6) || !strncmp(s, "tcp://", 6))
|
||||
s+=6; //ignore weird prefixes here
|
||||
strcpy(work->str, s);
|
||||
COM_AddWork(WG_MAIN, SV_Master_Worker_Resolve, NULL, work, ~(size_t)0, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_LEGACY
|
||||
|
@ -2521,26 +2572,88 @@ void SListOptionChanged(serverinfo_t *newserver)
|
|||
}
|
||||
}
|
||||
|
||||
static qboolean MasterInfo_ReadProtocol(serverinfo_t *info, const char *infostring)
|
||||
{
|
||||
char *token = Info_ValueForKey(infostring, "protocol");
|
||||
if (*token)
|
||||
{
|
||||
//read the protocol number
|
||||
info->protocol = strtoul(token, &token, 0);
|
||||
|
||||
//and try to figure out which filter it should be under.
|
||||
info->special &= ~SS_PROTOCOLMASK;
|
||||
if (*token)
|
||||
{
|
||||
while (*token)
|
||||
{
|
||||
if (*token == 'w')
|
||||
info->special |= SS_QUAKEWORLD;
|
||||
else if (*token == 'n' || *token == 'd')
|
||||
info->special |= SS_NETQUAKE;
|
||||
else if (*token == 'x')
|
||||
info->special |= SS_QEPROT;
|
||||
else
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else switch(info->protocol)
|
||||
{
|
||||
case PROTOCOL_VERSION_QW: info->special |= SS_QUAKEWORLD; break;
|
||||
#ifdef NQPROT
|
||||
case PROTOCOL_VERSION_NQ: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_H2: info->special |= SS_NETQUAKE; break; //erk
|
||||
case PROTOCOL_VERSION_NEHD: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_FITZ: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_RMQ: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_DP5: info->special |= SS_NETQUAKE; break; //dp actually says 3... but hey, that's dp being WEIRD.
|
||||
case PROTOCOL_VERSION_DP6: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_DP7: info->special |= SS_NETQUAKE; break;
|
||||
case NQ_NETCHAN_VERSION_QEX:info->special |= SS_QEPROT; break;
|
||||
case NQ_NETCHAN_VERSION:
|
||||
#endif
|
||||
default:
|
||||
if ((info->special&SS_PROTOCOLMASK) == SS_UNKNOWN)
|
||||
{ //guesses...
|
||||
if (PROTOCOL_VERSION_Q2 >= info->protocol && info->protocol >= PROTOCOL_VERSION_Q2_MIN)
|
||||
info->special |= SS_QUAKE2; //q2 has a range!
|
||||
else if (info->protocol > 60)
|
||||
info->special |= SS_QUAKE3;
|
||||
else if (!strcmp(Info_ValueForKey(infostring, "gamename"), "DarkPlaces-Quake") || *Info_ValueForKey(infostring, "nqprotocol"))
|
||||
info->special |= SS_NETQUAKE;
|
||||
else
|
||||
info->special |= SS_QUAKEWORLD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
info->protocol = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef WEBCLIENT
|
||||
static void MasterInfo_ProcessHTTPInfo(serverinfo_t *srv, const char *info)
|
||||
{
|
||||
char adrbuf[MAX_ADR_SIZE];
|
||||
if (info && (!(srv->status & SRVSTATUS_ALIVE) || srv->ping == PING_UNKNOWN))
|
||||
{
|
||||
if (srv->adr.prot == NP_RTC_TLS || srv->adr.prot == NP_RTC_TCP)
|
||||
if (srv->adr.prot != NP_DGRAM)
|
||||
{
|
||||
srv->sends = 0; //no point pinging it, it won't work.
|
||||
srv->ping = PING_UNKNOWN;
|
||||
srv->status |= SRVSTATUS_ALIVE; //or at least wouldn't have been reported this time around.
|
||||
}
|
||||
else
|
||||
srv->sends = 1; //no point pinging it, it won't work.
|
||||
|
||||
Q_strncpyz(srv->name, Info_ValueForKey(info, "hostname"), sizeof(srv->name));
|
||||
Q_strncpyz(srv->gamedir, Info_ValueForKey(info, "modname"), sizeof(srv->gamedir));
|
||||
Q_strncpyz(srv->map, Info_ValueForKey(info, "mapname"), sizeof(srv->map));
|
||||
srv->players = atoi(Info_ValueForKey(info, "clients"));
|
||||
srv->maxplayers = atoi(Info_ValueForKey(info, "maxclients"));
|
||||
|
||||
if (!MasterInfo_ReadProtocol(srv, info))
|
||||
srv->special = (srv->special&~SS_PROTOCOLMASK)|SS_QUAKEWORLD; //assume its an older fteqw server.
|
||||
|
||||
srv->numbots = 0;
|
||||
srv->numhumans = srv->players - srv->numbots;
|
||||
srv->freeslots = srv->maxplayers - srv->players;
|
||||
|
@ -2564,7 +2677,7 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
|
|||
char *el;
|
||||
serverinfo_t *info;
|
||||
char linebuffer[2048];
|
||||
char *brokerid;
|
||||
const char *brokerid;
|
||||
char *infostring;
|
||||
netadr_t brokeradr;
|
||||
|
||||
|
@ -2611,12 +2724,19 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
|
|||
if (*s == '#') //hash is a comment, apparently.
|
||||
continue;
|
||||
|
||||
for (infostring = s; *infostring && *infostring != ' '; )
|
||||
for (infostring = s; *infostring && *infostring != ' ' && *infostring != '\t'; )
|
||||
infostring++;
|
||||
if (*infostring == ' ')
|
||||
*infostring++ = 0;
|
||||
if (*infostring == ' ' || *infostring == '\t')
|
||||
{
|
||||
*infostring++ = 0; //null terminate the address
|
||||
while(*infostring == ' ' || *infostring == '\t')
|
||||
infostring++; //skip over any whitespace...
|
||||
if (*infostring != '\\')
|
||||
infostring = NULL; //err... no. not an info string. probably a comment.
|
||||
}
|
||||
else
|
||||
infostring = NULL;
|
||||
|
||||
if (!strncmp(s, "ice:///", 7) || !strncmp(s, "ices:///", 8) || !strncmp(s, "rtc:///", 7) || !strncmp(s, "rtcs:///", 8))
|
||||
{
|
||||
brokerid = s+((s[4]==':')?7:6);
|
||||
|
@ -2624,10 +2744,14 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
|
|||
if (!*brokerid)
|
||||
continue; //invalid...
|
||||
}
|
||||
else if (*s == '/')
|
||||
{
|
||||
brokerid = s;
|
||||
adr = brokeradr;
|
||||
}
|
||||
else
|
||||
{
|
||||
brokerid = "";
|
||||
if (!NET_StringToAdr(s, 80, &adr))
|
||||
if (!NET_StringToAdr2(s, 80, &adr, 1, &brokerid))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2648,8 +2772,8 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
|
|||
info->special = 0;
|
||||
if (protocoltype == MP_QUAKEWORLD)
|
||||
info->special |= SS_QUAKEWORLD;
|
||||
else if (protocoltype == MP_DPMASTER)
|
||||
info->special |= SS_GETINFO;
|
||||
else if (protocoltype == MP_DPMASTER) //actually ftemaster... so assume fteqw servers not ftenq ones unless otherwise indicated.
|
||||
info->special |= SS_QUAKEWORLD|SS_GETINFO;
|
||||
#if defined(Q2CLIENT) || defined(Q2SERVER)
|
||||
else if (protocoltype == MP_QUAKE2)
|
||||
info->special |= SS_QUAKE2;
|
||||
|
@ -3249,53 +3373,10 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea
|
|||
else if (!strncmp(DISTRIBUTION, Info_ValueForKey(msg, "*version"), strlen(DISTRIBUTION)))
|
||||
info->special |= SS_FTESERVER;
|
||||
|
||||
info->protocol = strtoul(Info_ValueForKey(msg, "protocol"), &token, 0);
|
||||
if (info->protocol)
|
||||
{
|
||||
switch(info->protocol)
|
||||
{
|
||||
case PROTOCOL_VERSION_QW: info->special |= SS_QUAKEWORLD; break;
|
||||
#ifdef NQPROT
|
||||
case PROTOCOL_VERSION_NQ: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_H2: info->special |= SS_NETQUAKE; break; //erk
|
||||
case PROTOCOL_VERSION_NEHD: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_FITZ: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_RMQ: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_DP5: info->special |= SS_NETQUAKE; break; //dp actually says 3... but hey, that's dp being WEIRD.
|
||||
case PROTOCOL_VERSION_DP6: info->special |= SS_NETQUAKE; break;
|
||||
case PROTOCOL_VERSION_DP7: info->special |= SS_NETQUAKE; break;
|
||||
case NQ_NETCHAN_VERSION_QEX:info->special |= SS_QEPROT; break;
|
||||
case NQ_NETCHAN_VERSION:
|
||||
#endif
|
||||
default:
|
||||
while (*token)
|
||||
{
|
||||
if (*token == 'w')
|
||||
info->special |= SS_QUAKEWORLD;
|
||||
else if (*token == 'n' || *token == 'd')
|
||||
info->special |= SS_NETQUAKE;
|
||||
else if (*token == 'x')
|
||||
info->special |= SS_QEPROT;
|
||||
else
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if ((info->special&SS_PROTOCOLMASK) == SS_UNKNOWN)
|
||||
{ //guesses...
|
||||
if (PROTOCOL_VERSION_Q2 >= info->protocol && info->protocol >= PROTOCOL_VERSION_Q2_MIN)
|
||||
info->special |= SS_QUAKE2; //q2 has a range!
|
||||
else if (info->protocol > 60)
|
||||
info->special |= SS_QUAKE3;
|
||||
else if (!strcmp(Info_ValueForKey(msg, "gamename"), "DarkPlaces-Quake") || *Info_ValueForKey(msg, "nqprotocol"))
|
||||
info->special |= SS_NETQUAKE;
|
||||
else
|
||||
info->special |= SS_QUAKEWORLD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!MasterInfo_ReadProtocol(info, msg))
|
||||
{ //try and guess.
|
||||
#ifdef Q2CLIENT
|
||||
else if (prototype == MP_QUAKE2)
|
||||
if (prototype == MP_QUAKE2)
|
||||
info->special |= SS_QUAKE2;
|
||||
#endif
|
||||
#ifdef Q3CLIENT
|
||||
|
@ -3308,6 +3389,7 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea
|
|||
#endif
|
||||
else
|
||||
info->special |= SS_QUAKEWORLD;
|
||||
}
|
||||
if (favorite) //was specifically named, not retrieved from a master.
|
||||
info->special |= SS_FAVORITE;
|
||||
|
||||
|
@ -3786,6 +3868,8 @@ static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const ch
|
|||
const char *url;
|
||||
struct dl_download *dl;
|
||||
COM_Parse(com_protocolname.string);
|
||||
if (*net_ice_broker.string)
|
||||
{
|
||||
if (!strncmp(net_ice_broker.string, "tls://", 6))
|
||||
url = va("https://%s/raw/%s", net_ice_broker.string+6, com_token);
|
||||
else if (!strncmp(net_ice_broker.string, "tcp://", 6))
|
||||
|
@ -3797,6 +3881,7 @@ static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const ch
|
|||
if (dl)
|
||||
dl->isquery = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if POLLTOTALSOCKETS>0
|
||||
if (masternum >= countof(net_masterlist))
|
||||
|
|
|
@ -213,7 +213,7 @@ void Sys_Printf (char *fmt, ...)
|
|||
if (w >= 0xe000 && w < 0xe100)
|
||||
{
|
||||
/*not all quake chars are ascii compatible, so map those control chars to safe ones so we don't mess up anyone's xterm*/
|
||||
if ((w & 0x7f) > 0x20)
|
||||
if ((w & 0x7f) >= 0x20)
|
||||
putc(w&0x7f, out);
|
||||
else if (w & 0x80)
|
||||
{
|
||||
|
|
|
@ -429,9 +429,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
//FIXME: HAVE_WINSSPI does not work as a server.
|
||||
//FIXME: advertising dtls without a valid certificate will probably bug out if a client tries to auto-upgrade.
|
||||
//FIXME: we don't cache server certs
|
||||
#ifndef MASTERONLY
|
||||
#define HAVE_DTLS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(USE_SQLITE) || defined(USE_MYSQL)
|
||||
|
|
|
@ -796,7 +796,6 @@ static void CertLog_Add_Prompted(void *vctx, promptbutton_t button)
|
|||
}
|
||||
qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems)
|
||||
{ //this is specifically for dtls certs.
|
||||
extern cvar_t net_enable_dtls;
|
||||
struct certlog_s *l;
|
||||
qboolean trusted = (net_enable_dtls.ival >= 2);
|
||||
char digest[DIGEST_MAXSIZE];
|
||||
|
|
|
@ -152,17 +152,17 @@ int NET_LocalAddressForRemote(struct ftenet_connections_s *collection, netadr_
|
|||
void NET_PrintAddresses(struct ftenet_connections_s *collection);
|
||||
qboolean NET_AddressSmellsFunny(netadr_t *a);
|
||||
struct dtlspeercred_s;
|
||||
qboolean NET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, const struct dtlspeercred_s *peerinfo, netadr_t *adr);
|
||||
qboolean NET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, const struct dtlspeercred_s *peerinfo, netadr_t *adr, qboolean outgoing);
|
||||
void NET_TerminateRoute(struct ftenet_connections_s *collection, netadr_t *adr);
|
||||
void NET_PrintConnectionsStatus(struct ftenet_connections_s *collection);
|
||||
|
||||
enum addressscope_e
|
||||
{
|
||||
ASCOPE_PROCESS=0,
|
||||
ASCOPE_HOST=1,
|
||||
ASCOPE_LINK=2,
|
||||
ASCOPE_LAN=3,
|
||||
ASCOPE_NET=4
|
||||
ASCOPE_PROCESS=0, //unusable
|
||||
ASCOPE_HOST=1, //unroutable
|
||||
ASCOPE_LINK=2, //unpredictable
|
||||
ASCOPE_LAN=3, //private
|
||||
ASCOPE_NET=4 //aka hopefully globally routable
|
||||
};
|
||||
enum addressscope_e NET_ClassifyAddress(netadr_t *adr, const char **outdesc);
|
||||
|
||||
|
@ -175,6 +175,7 @@ char *NET_AdrToString (char *s, int len, netadr_t *a);
|
|||
char *NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a, size_t sizeofa);
|
||||
char *NET_BaseAdrToString (char *s, int len, netadr_t *a);
|
||||
size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhint, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addrcount);
|
||||
qboolean NET_StringToAdr_NoDNS(const char *address, int port, netadr_t *out);
|
||||
#define NET_StringToSockaddr(s,p,a,f,z) (NET_StringToSockaddr2(s,p,NA_INVALID,a,f,z,1)>0)
|
||||
size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t addrcount, const char **pathstart);
|
||||
#define NET_StringToAdr(s,p,a) NET_StringToAdr2(s,p,a,1,NULL)
|
||||
|
@ -203,11 +204,11 @@ int NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a,
|
|||
#ifdef HAVE_DTLS
|
||||
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_Create(struct ftenet_connections_s *col, netadr_t *to, const struct dtlscred_s *cred, qboolean outgoing);
|
||||
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;
|
||||
extern cvar_t net_enable_dtls;
|
||||
#endif
|
||||
#ifdef SUPPORT_ICE
|
||||
neterr_t ICE_SendPacket(size_t length, const void *data, netadr_t *to);
|
||||
|
|
|
@ -1,3 +1,43 @@
|
|||
/*
|
||||
Interactive Connectivity Establishment (rfc 5245)
|
||||
find out your peer's potential ports.
|
||||
spam your peer with stun packets.
|
||||
see what sticks.
|
||||
the 'controller' assigns some final candidate pair to ensure that both peers send+receive from a single connection.
|
||||
if no candidates are available, try using stun to find public nat addresses.
|
||||
|
||||
in fte, a 'pair' is actually in terms of each local socket and remote address. hopefully that won't cause too much weirdness.
|
||||
this does limit which interfaces we can send packets from, which may cause issues with local TURN relays(does not result in extra prflx candidates) and VPNs(may need to be set up as the default route), and prevents us from being able to report reladdr in candidate offers (again mostly only of use with TURN)
|
||||
lan connections should resolve to a host interface anyway
|
||||
|
||||
stun test packets must contain all sorts of info. username+integrity+fingerprint for validation. priority+usecandidate+icecontrol(ing) to decree the priority of any new remote candidates, whether its finished, and just who decides whether its finished.
|
||||
peers don't like it when those are missing.
|
||||
|
||||
host candidates - addresses that are directly known (but are probably unroutable private things)
|
||||
server reflexive candidates - addresses that we found from some public stun server (useful for NATs that use a single public port for each unique private port)
|
||||
peer reflexive candidates - addresses that our peer finds out about as we spam them
|
||||
relayed candidates - some sort of socks5 or something proxy.
|
||||
|
||||
|
||||
Note: Even after the ICE connection becomes active, you should continue to collect local candidates and transmit them to the peer out of band.
|
||||
this allows the connection to pick a new route if some router dies (like a relay kicking us).
|
||||
FIXME: the client currently disconnects from the broker. the server tracks players via ip rather than ICE.
|
||||
|
||||
tcp rtp framing should generally be done with a 16-bit network-endian length prefix followed by the data.
|
||||
|
||||
NOTE: we do NOT distinguish between media-level and session-level attributes, as such we can only handle ONE media stream per session. we also don't support rtcp.
|
||||
*/
|
||||
/*
|
||||
webrtc
|
||||
basically just sctp-over-dtls-over-ice or srtp(negotiated via dtls)-over-ice.
|
||||
the sctp part is pure bloat+pain for us, but as its required for browser compat we have to support it anyway - but we only use it where we must.
|
||||
we don't do any srtp stuff at all.
|
||||
*/
|
||||
/*
|
||||
broker
|
||||
ftemaster provides a broker service
|
||||
*/
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "netinc.h"
|
||||
|
||||
|
@ -77,36 +117,6 @@ static cvar_t net_ice_debug = CVARFD("net_ice_debug", "0", CVAR_NOTFROMSERVER, "
|
|||
#define ASCOPE_TURN_REQUIRESCOPE ASCOPE_LAN //don't report loopback/link-local addresses to turn relays.
|
||||
#endif
|
||||
|
||||
/*
|
||||
Interactive Connectivity Establishment (rfc 5245)
|
||||
find out your peer's potential ports.
|
||||
spam your peer with stun packets.
|
||||
see what sticks.
|
||||
the 'controller' assigns some final candidate pair to ensure that both peers send+receive from a single connection.
|
||||
if no candidates are available, try using stun to find public nat addresses.
|
||||
|
||||
in fte, a 'pair' is actually in terms of each local socket and remote address. hopefully that won't cause too much weirdness.
|
||||
this does limit which interfaces we can send packets from, which may cause issues with local TURN relays(does not result in extra prflx candidates) and VPNs(may need to be set up as the default route), and prevents us from being able to report reladdr in candidate offers (again mostly only of use with TURN)
|
||||
lan connections should resolve to a host interface anyway
|
||||
|
||||
stun test packets must contain all sorts of info. username+integrity+fingerprint for validation. priority+usecandidate+icecontrol(ing) to decree the priority of any new remote candidates, whether its finished, and just who decides whether its finished.
|
||||
peers don't like it when those are missing.
|
||||
|
||||
host candidates - addresses that are directly known (but are probably unroutable private things)
|
||||
server reflexive candidates - addresses that we found from some public stun server (useful for NATs that use a single public port for each unique private port)
|
||||
peer reflexive candidates - addresses that our peer finds out about as we spam them
|
||||
relayed candidates - some sort of socks5 or something proxy.
|
||||
|
||||
|
||||
Note: Even after the ICE connection becomes active, you should continue to collect local candidates and transmit them to the peer out of band.
|
||||
this allows the connection to pick a new route if some router dies (like a relay kicking us).
|
||||
FIXME: the client currently disconnects from the broker. the server tracks players via ip rather than ICE.
|
||||
|
||||
tcp rtp framing should generally be done with a 16-bit network-endian length prefix followed by the data.
|
||||
|
||||
NOTE: we do NOT distinguish between media-level and session-level attributes, as such we can only handle ONE media stream per session. we also don't support rtcp.
|
||||
*/
|
||||
|
||||
struct icecandidate_s
|
||||
{
|
||||
struct icecandinfo_s info;
|
||||
|
@ -214,10 +224,22 @@ struct icestate_s
|
|||
int id;
|
||||
char *name;
|
||||
} codecslot[34]; //96-127. don't really need to care about other ones.
|
||||
|
||||
struct
|
||||
{ //this block is for our inbound udp broker reliability, ensuring we get candidate info to where its needed...
|
||||
char *text;
|
||||
unsigned int inseq;
|
||||
unsigned int outseq;
|
||||
} u;
|
||||
};
|
||||
|
||||
typedef struct sctp_s
|
||||
{
|
||||
char *friendlyname; //for printing/debugging.
|
||||
struct icestate_s *icestate; //for forwarding over ice connections...
|
||||
const struct dtlsfuncs_s *dtlsfuncs; //for forwarding over dtls connects (ice-lite)
|
||||
void *dtlsstate;
|
||||
|
||||
quint16_t myport, peerport;
|
||||
qboolean peerhasfwdtsn;
|
||||
double nextreinit;
|
||||
|
@ -271,12 +293,11 @@ static const struct
|
|||
{"sha-384", &hash_sha2_384},
|
||||
{"sha-512", &hash_sha2_512},
|
||||
};
|
||||
extern cvar_t net_enable_dtls;
|
||||
static neterr_t SCTP_Transmit(sctp_t *sctp, struct icestate_s *peer, const void *data, size_t length);
|
||||
#endif
|
||||
static neterr_t ICE_Transmit(void *cbctx, const qbyte *data, size_t datasize);
|
||||
static neterr_t TURN_Encapsulate(struct icestate_s *ice, netadr_t *to, const qbyte *data, size_t datasize);
|
||||
static void TURN_AuthorisePeer(struct icestate_s *con, struct iceserver_s *srv, int peer);
|
||||
static neterr_t SCTP_Transmit(sctp_t *sctp, const void *data, size_t length);
|
||||
|
||||
static struct icestate_s *icelist;
|
||||
|
||||
|
@ -684,7 +705,8 @@ static struct icestate_s *QDECL ICE_Create(void *module, const char *conname, co
|
|||
|
||||
con = Z_Malloc(sizeof(*con));
|
||||
con->conname = Z_StrDup(conname);
|
||||
con->friendlyname = peername?Z_StrDup(peername):Z_StrDupf("%i", icenum++);
|
||||
icenum++;
|
||||
con->friendlyname = peername?Z_StrDup(peername):Z_StrDupf("%i", icenum);
|
||||
con->proto = proto;
|
||||
con->rpwd = Z_StrDup("");
|
||||
con->rufrag = Z_StrDup("");
|
||||
|
@ -739,6 +761,7 @@ static struct icestate_s *QDECL ICE_Create(void *module, const char *conname, co
|
|||
con->qadr.type = NA_ICE;
|
||||
con->qadr.prot = NP_DGRAM;
|
||||
Q_strncpyz(con->qadr.address.icename, con->friendlyname, sizeof(con->qadr.address.icename));
|
||||
con->qadr.port = icenum;
|
||||
|
||||
con->mode = mode;
|
||||
|
||||
|
@ -2155,6 +2178,8 @@ static qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const ch
|
|||
if (!con->sctp && (!con->sctpoptional || !con->peersctpoptional) && con->mysctpport && con->peersctpport)
|
||||
{
|
||||
con->sctp = Z_Malloc(sizeof(*con->sctp));
|
||||
con->sctp->icestate = con;
|
||||
con->sctp->friendlyname = con->friendlyname;
|
||||
con->sctp->myport = htons(con->mysctpport);
|
||||
con->sctp->peerport = htons(con->peersctpport);
|
||||
con->sctp->o.tsn = rand() ^ (rand()<<16);
|
||||
|
@ -2580,6 +2605,16 @@ static qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *va
|
|||
}
|
||||
// Q_strncatz(value, va("c=IN %s %s\n", sender.type==NA_IPV6?"IP6":"IP4", NET_BaseAdrToString(tmpstr, sizeof(tmpstr), &sender)), valuelen);
|
||||
Q_strncatz(value, "c=IN IP4 0.0.0.0\n", valuelen);
|
||||
|
||||
for (can = con->lc; can; can = can->next)
|
||||
{
|
||||
char canline[256];
|
||||
can->dirty = false; //doesn't matter now.
|
||||
ICE_CandidateToSDP(can, canline, sizeof(canline));
|
||||
Q_strncatz(value, canline, valuelen);
|
||||
Q_strncatz(value, "\n", valuelen);
|
||||
}
|
||||
|
||||
Q_strncatz(value, va("a=ice-pwd:%s\n", con->lpwd), valuelen);
|
||||
Q_strncatz(value, va("a=ice-ufrag:%s\n", con->lufrag), valuelen);
|
||||
|
||||
|
@ -3065,7 +3100,7 @@ void ICE_Tick(void)
|
|||
{
|
||||
#ifdef HAVE_DTLS
|
||||
if (con->sctp)
|
||||
SCTP_Transmit(con->sctp, con, NULL,0); //try to keep it ticking...
|
||||
SCTP_Transmit(con->sctp, NULL,0); //try to keep it ticking...
|
||||
if (con->dtlsstate)
|
||||
con->dtlsfuncs->Timeouts(con->dtlsstate);
|
||||
#endif
|
||||
|
@ -3125,7 +3160,8 @@ icefuncs_t iceapi =
|
|||
ICE_AddRCandidateInfo,
|
||||
ICE_Close,
|
||||
ICE_CloseModule,
|
||||
ICE_GetLCandidateSDP
|
||||
ICE_GetLCandidateSDP,
|
||||
ICE_Find
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -3208,9 +3244,12 @@ struct sctp_chunk_fwdtsn_s
|
|||
} streams[];*/
|
||||
};
|
||||
|
||||
static neterr_t SCTP_PeerSendPacket(struct icestate_s *peer, int length, const void *data)
|
||||
static neterr_t SCTP_PeerSendPacket(sctp_t *sctp, int length, const void *data)
|
||||
{ //sends to the dtls layer (which will send to the generic ice dispatcher that'll send to the dgram stuff... layers on layers.
|
||||
if (peer)
|
||||
struct icestate_s *peer = sctp->icestate;
|
||||
if (sctp->dtlsstate)
|
||||
return sctp->dtlsfuncs->Transmit(sctp->dtlsstate, data, length);
|
||||
else if (peer)
|
||||
{
|
||||
if (peer->dtlsstate)
|
||||
return peer->dtlsfuncs->Transmit(peer->dtlsstate, data, length);
|
||||
|
@ -3244,7 +3283,7 @@ static quint32_t SCTP_Checksum(const struct sctp_header_s *h, size_t size)
|
|||
return ~crc;
|
||||
}
|
||||
|
||||
static neterr_t SCTP_Transmit(sctp_t *sctp, struct icestate_s *peer, const void *data, size_t length)
|
||||
neterr_t SCTP_Transmit(sctp_t *sctp, const void *data, size_t length)
|
||||
{
|
||||
qbyte pkt[65536];
|
||||
size_t pktlen = 0;
|
||||
|
@ -3298,7 +3337,7 @@ static neterr_t SCTP_Transmit(sctp_t *sctp, struct icestate_s *peer, const void
|
|||
pktlen += sizeof(*init) + sizeof(*ftsn);
|
||||
|
||||
h->crc = SCTP_Checksum(h, pktlen);
|
||||
return SCTP_PeerSendPacket(peer, pktlen, h);
|
||||
return SCTP_PeerSendPacket(sctp, pktlen, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3313,7 +3352,7 @@ static neterr_t SCTP_Transmit(sctp_t *sctp, struct icestate_s *peer, const void
|
|||
pktlen += sizeof(*cookie) + sctp->cookiesize;
|
||||
|
||||
h->crc = SCTP_Checksum(h, pktlen);
|
||||
return SCTP_PeerSendPacket(peer, pktlen, h);
|
||||
return SCTP_PeerSendPacket(sctp, pktlen, h);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3385,7 +3424,7 @@ static neterr_t SCTP_Transmit(sctp_t *sctp, struct icestate_s *peer, const void
|
|||
if (pktlen + sizeof(*d) + length >= 500 && length && pktlen != sizeof(*h))
|
||||
{ //probably going to result in fragmentation issues. send separate packets.
|
||||
h->crc = SCTP_Checksum(h, pktlen);
|
||||
SCTP_PeerSendPacket(peer, pktlen, h);
|
||||
SCTP_PeerSendPacket(sctp, pktlen, h);
|
||||
|
||||
//reset to the header
|
||||
pktlen = sizeof(*h);
|
||||
|
@ -3412,10 +3451,10 @@ static neterr_t SCTP_Transmit(sctp_t *sctp, struct icestate_s *peer, const void
|
|||
return NETERR_SENT; //nothing to send...
|
||||
|
||||
h->crc = SCTP_Checksum(h, pktlen);
|
||||
return SCTP_PeerSendPacket(peer, pktlen, h);
|
||||
return SCTP_PeerSendPacket(sctp, pktlen, h);
|
||||
}
|
||||
|
||||
static void SCTP_DecodeDCEP(sctp_t *sctp, struct icestate_s *peer, qbyte *resp)
|
||||
static void SCTP_DecodeDCEP(sctp_t *sctp, qbyte *resp)
|
||||
{ //send an ack...
|
||||
size_t pktlen = 0;
|
||||
struct sctp_header_s *h = (void*)resp;
|
||||
|
@ -3440,7 +3479,7 @@ static void SCTP_DecodeDCEP(sctp_t *sctp, struct icestate_s *peer, qbyte *resp)
|
|||
|
||||
sctp->qstreamid = sctp->i.r.sid;
|
||||
if (net_ice_debug.ival >= 1)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: New SCTP Channel: \"%s\" (%s)\n", peer->friendlyname, label, prot);
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: New SCTP Channel: \"%s\" (%s)\n", sctp->friendlyname, label, prot);
|
||||
|
||||
h->dstport = sctp->peerport;
|
||||
h->srcport = sctp->myport;
|
||||
|
@ -3460,7 +3499,7 @@ static void SCTP_DecodeDCEP(sctp_t *sctp, struct icestate_s *peer, qbyte *resp)
|
|||
pktlen += sizeof(*d) + length;
|
||||
|
||||
h->crc = SCTP_Checksum(h, pktlen);
|
||||
SCTP_PeerSendPacket(peer, pktlen, h);
|
||||
SCTP_PeerSendPacket(sctp, pktlen, h);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3469,7 +3508,7 @@ struct sctp_errorcause_s
|
|||
quint16_t cause;
|
||||
quint16_t length;
|
||||
};
|
||||
static void SCTP_ErrorChunk(struct icestate_s *peer, const char *errortype, struct sctp_errorcause_s *s, size_t totallen)
|
||||
static void SCTP_ErrorChunk(sctp_t *sctp, const char *errortype, struct sctp_errorcause_s *s, size_t totallen)
|
||||
{
|
||||
quint16_t cc, cl;
|
||||
while(totallen > 0)
|
||||
|
@ -3483,20 +3522,20 @@ static void SCTP_ErrorChunk(struct icestate_s *peer, const char *errortype, stru
|
|||
|
||||
if (net_ice_debug.ival >= 1) switch(cc)
|
||||
{
|
||||
case 1: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Invalid Stream Identifier\n", peer->friendlyname, errortype); break;
|
||||
case 2: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Missing Mandatory Parameter\n", peer->friendlyname, errortype); break;
|
||||
case 3: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Stale Cookie Error\n", peer->friendlyname, errortype); break;
|
||||
case 4: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Out of Resource\n", peer->friendlyname, errortype); break;
|
||||
case 5: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Unresolvable Address\n", peer->friendlyname, errortype); break;
|
||||
case 6: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Unrecognized Chunk Type\n", peer->friendlyname, errortype); break;
|
||||
case 7: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Invalid Mandatory Parameter\n", peer->friendlyname, errortype); break;
|
||||
case 8: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Unrecognized Parameters\n", peer->friendlyname, errortype); break;
|
||||
case 9: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: No User Data\n", peer->friendlyname, errortype); break;
|
||||
case 10: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Cookie Received While Shutting Down\n", peer->friendlyname, errortype); break;
|
||||
case 11: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Restart of an Association with New Addresses\n", peer->friendlyname, errortype); break;
|
||||
case 12: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: User Initiated Abort\n", peer->friendlyname, errortype); break;
|
||||
case 13: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Protocol Violation [%s]\n", peer->friendlyname, errortype, (char*)(s+1)); break;
|
||||
default: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Unknown Reason\n", peer->friendlyname, errortype); break;
|
||||
case 1: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Invalid Stream Identifier\n", sctp->friendlyname, errortype); break;
|
||||
case 2: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Missing Mandatory Parameter\n", sctp->friendlyname, errortype); break;
|
||||
case 3: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Stale Cookie Error\n", sctp->friendlyname, errortype); break;
|
||||
case 4: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Out of Resource\n", sctp->friendlyname, errortype); break;
|
||||
case 5: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Unresolvable Address\n", sctp->friendlyname, errortype); break;
|
||||
case 6: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Unrecognized Chunk Type\n", sctp->friendlyname, errortype); break;
|
||||
case 7: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Invalid Mandatory Parameter\n", sctp->friendlyname, errortype); break;
|
||||
case 8: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Unrecognized Parameters\n", sctp->friendlyname, errortype); break;
|
||||
case 9: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: No User Data\n", sctp->friendlyname, errortype); break;
|
||||
case 10: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Cookie Received While Shutting Down\n", sctp->friendlyname, errortype); break;
|
||||
case 11: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Restart of an Association with New Addresses\n", sctp->friendlyname, errortype); break;
|
||||
case 12: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: User Initiated Abort\n", sctp->friendlyname, errortype); break;
|
||||
case 13: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Protocol Violation [%s]\n", sctp->friendlyname, errortype, (char*)(s+1)); break;
|
||||
default: Con_Printf(S_COLOR_GRAY"[%s]: SCTP %s: Unknown Reason\n", sctp->friendlyname, errortype); break;
|
||||
}
|
||||
|
||||
totallen -= cl;
|
||||
|
@ -3505,7 +3544,7 @@ static void SCTP_ErrorChunk(struct icestate_s *peer, const char *errortype, stru
|
|||
}
|
||||
}
|
||||
|
||||
static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connections_t *col)
|
||||
void SCTP_Decode(sctp_t *sctp, ftenet_connections_t *col)
|
||||
{
|
||||
qbyte resp[4096];
|
||||
|
||||
|
@ -3527,7 +3566,7 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
if (net_message.cursize&3)
|
||||
{
|
||||
if (net_ice_debug.ival >= 2)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: packet not padded\n", peer->friendlyname);
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: packet not padded\n", sctp->friendlyname);
|
||||
return; //mimic chrome, despite it being pointless.
|
||||
}
|
||||
|
||||
|
@ -3558,17 +3597,17 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
if (adv >= SCTP_RCVSIZE)
|
||||
{
|
||||
if (net_ice_debug.ival >= 1)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Future Packet\n", peer->friendlyname);/*too far in the future. we can't track such things*/
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Future Packet\n", sctp->friendlyname);/*too far in the future. we can't track such things*/
|
||||
}
|
||||
else if (adv <= 0)
|
||||
{
|
||||
if (net_ice_debug.ival >= 2)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: PreCumulative\n", peer->friendlyname);/*already acked this*/
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: PreCumulative\n", sctp->friendlyname);/*already acked this*/
|
||||
}
|
||||
else if (sctp->i.received[(tsn>>3)%sizeof(sctp->i.received)] & 1<<(tsn&7))
|
||||
{
|
||||
if (net_ice_debug.ival >= 2)
|
||||
Con_DPrintf(S_COLOR_GRAY"[%s]: SCTP: Dupe\n", peer->friendlyname);/*already processed it. FIXME: Make a list for the next SACK*/
|
||||
Con_DPrintf(S_COLOR_GRAY"[%s]: SCTP: Dupe\n", sctp->friendlyname);/*already processed it. FIXME: Make a list for the next SACK*/
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3600,7 +3639,7 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
if (sctp->i.r.size + dlen > sizeof(sctp->i.r.buf))
|
||||
{
|
||||
if (net_ice_debug.ival >= 2)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Oversized\n", peer->friendlyname);
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Oversized\n", sctp->friendlyname);
|
||||
sctp->i.r.toobig = true; //reassembled packet was too large, just corrupt it.
|
||||
}
|
||||
else
|
||||
|
@ -3629,7 +3668,7 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
}
|
||||
}
|
||||
else if (sctp->i.r.ppid == BigLong(SCTP_PPID_DCEP))
|
||||
SCTP_DecodeDCEP(sctp, peer, resp);
|
||||
SCTP_DecodeDCEP(sctp, resp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3681,7 +3720,7 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
break;
|
||||
default:
|
||||
if (net_ice_debug.ival >= 2)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Found unknown init parameter %i||%#x\n", peer->friendlyname, ptype, ptype);
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Found unknown init parameter %i||%#x\n", sctp->friendlyname, ptype, ptype);
|
||||
break;
|
||||
}
|
||||
p = (void*)((qbyte*)p + ((plen+3)&~3));
|
||||
|
@ -3691,7 +3730,7 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
{
|
||||
sctp->nextreinit = 0;
|
||||
if (sctp->cookie)
|
||||
SCTP_Transmit(sctp, peer, NULL, 0); //make sure we send acks occasionally even if we have nothing else to say.
|
||||
SCTP_Transmit(sctp, NULL, 0); //make sure we send acks occasionally even if we have nothing else to say.
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3731,7 +3770,7 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
|
||||
//complete. calc the proper crc and send it off.
|
||||
rh->crc = SCTP_Checksum(rh, end-resp);
|
||||
SCTP_PeerSendPacket(peer, end-resp, rh);
|
||||
SCTP_PeerSendPacket(sctp, end-resp, rh);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -3765,24 +3804,26 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
|
||||
//complete. calc the proper crc and send it off.
|
||||
pongh->crc = SCTP_Checksum(pongh, sizeof(*pongh) + clen);
|
||||
SCTP_PeerSendPacket(peer, sizeof(*pongh) + clen, pongh);
|
||||
SCTP_PeerSendPacket(sctp, sizeof(*pongh) + clen, pongh);
|
||||
Z_Free(pongh);
|
||||
}
|
||||
break;
|
||||
// case SCTP_TYPE_PONG: //we don't send pings
|
||||
case SCTP_TYPE_ABORT:
|
||||
SCTP_ErrorChunk(peer, "Abort", (struct sctp_errorcause_s*)(c+1), clen-sizeof(*c));
|
||||
ICE_Set(peer, "state", STRINGIFY(ICE_FAILED));
|
||||
SCTP_ErrorChunk(sctp, "Abort", (struct sctp_errorcause_s*)(c+1), clen-sizeof(*c));
|
||||
if (sctp->icestate)
|
||||
ICE_Set(sctp->icestate, "state", STRINGIFY(ICE_FAILED));
|
||||
break;
|
||||
case SCTP_TYPE_SHUTDOWN: //FIXME. we should send an ack...
|
||||
ICE_Set(peer, "state", STRINGIFY(ICE_FAILED));
|
||||
if (sctp->icestate)
|
||||
ICE_Set(sctp->icestate, "state", STRINGIFY(ICE_FAILED));
|
||||
if (net_ice_debug.ival >= 1)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Shutdown\n", peer->friendlyname);
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Shutdown\n", sctp->friendlyname);
|
||||
break;
|
||||
// case SCTP_TYPE_SHUTDOWNACK: //we don't send shutdowns, cos we're lame like that...
|
||||
case SCTP_TYPE_ERROR:
|
||||
//not fatal...
|
||||
SCTP_ErrorChunk(peer, "Error", (struct sctp_errorcause_s*)(c+1), clen-sizeof(*c));
|
||||
SCTP_ErrorChunk(sctp, "Error", (struct sctp_errorcause_s*)(c+1), clen-sizeof(*c));
|
||||
break;
|
||||
case SCTP_TYPE_COOKIEECHO:
|
||||
if (clen >= sizeof(struct sctp_chunk_s))
|
||||
|
@ -3801,7 +3842,7 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
|
||||
//complete. calc the proper crc and send it off.
|
||||
rh->crc = SCTP_Checksum(rh, end-resp);
|
||||
SCTP_PeerSendPacket(peer, end-resp, rh);
|
||||
SCTP_PeerSendPacket(sctp, end-resp, rh);
|
||||
|
||||
sctp->o.writable = true; //channel SHOULD now be open for data.
|
||||
}
|
||||
|
@ -3833,19 +3874,183 @@ static void SCTP_Decode(sctp_t *sctp, struct icestate_s *peer, ftenet_connection
|
|||
safedefault:
|
||||
//no idea what this chunk is, just ignore it...
|
||||
if (net_ice_debug.ival >= 1)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Unsupported chunk %i\n", peer->friendlyname, c->type);
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Unsupported chunk %i\n", sctp->friendlyname, c->type);
|
||||
break;
|
||||
}
|
||||
c = (struct sctp_chunk_s*)((qbyte*)c + ((clen+3)&~3)); //next chunk is 4-byte aligned.
|
||||
}
|
||||
|
||||
if (sctp->i.ackneeded >= 5)
|
||||
SCTP_Transmit(sctp, peer, NULL, 0); //make sure we send acks occasionally even if we have nothing else to say.
|
||||
SCTP_Transmit(sctp, NULL, 0); //make sure we send acks occasionally even if we have nothing else to say.
|
||||
|
||||
//we already made sense of it all.
|
||||
net_message.cursize = 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
qboolean SCTP_Handshake(const dtlsfuncs_t *dtlsfuncs, void *dtlsstate, sctp_t **out)
|
||||
{
|
||||
const int myport = ICELITE_SCTP_PORT;
|
||||
struct cookiedata_s
|
||||
{
|
||||
qbyte peerhasfwdtsn;
|
||||
int overifycode;
|
||||
int iverifycode;
|
||||
int ictsn;
|
||||
int otsn;
|
||||
// int checkcode;
|
||||
};
|
||||
//if this is an sctp init packet, send a cookie.
|
||||
//if its an initack then create the new state.
|
||||
qbyte resp[4096];
|
||||
|
||||
qbyte *msg = net_message.data;
|
||||
qbyte *msgend = msg+net_message.cursize;
|
||||
struct sctp_header_s *h = (struct sctp_header_s*)msg;
|
||||
struct sctp_chunk_s *c = (struct sctp_chunk_s*)(h+1);
|
||||
quint16_t clen;
|
||||
if (net_message.cursize&3)
|
||||
return false;
|
||||
if ((qbyte*)c+1 > msgend)
|
||||
return false; //runt
|
||||
if (h->dstport != htons(myport))
|
||||
return false; //not for us...
|
||||
clen = BigShort(c->length);
|
||||
if ((qbyte*)c + ((clen+3)&~3) == msgend) //don't allow multiple chucks
|
||||
switch(c->type)
|
||||
{
|
||||
default:
|
||||
return false; //not the right kind of packet.
|
||||
case SCTP_TYPE_COOKIEECHO:
|
||||
if (clen == sizeof(struct sctp_chunk_s)+sizeof(struct cookiedata_s))
|
||||
{
|
||||
struct cookiedata_s *cookie = (struct cookiedata_s *)(c+1);
|
||||
sctp_t *sctp;
|
||||
struct sctp_header_s *rh = (void*)resp;
|
||||
struct sctp_chunk_s *rack = (void*)(rh+1);
|
||||
qbyte *end = (void*)(rack+1);
|
||||
if (h->verifycode == cookie->iverifycode)
|
||||
if (h->crc == SCTP_Checksum(h, net_message.cursize)) //make sure the crc matches.
|
||||
{ //looks okay.
|
||||
NET_AdrToString(resp, sizeof(resp), &net_from);
|
||||
|
||||
*out = sctp = Z_Malloc(sizeof(*sctp) + strlen(resp));
|
||||
sctp->friendlyname = strcpy((char*)(sctp+1), resp);
|
||||
sctp->icestate = NULL;
|
||||
sctp->dtlsfuncs = dtlsfuncs;
|
||||
sctp->dtlsstate = dtlsstate;
|
||||
|
||||
sctp->myport = h->dstport;
|
||||
sctp->peerport = h->srcport;
|
||||
sctp->o.tsn = cookie->otsn;
|
||||
sctp->i.ctsn = cookie->ictsn;
|
||||
sctp->o.verifycode = cookie->overifycode;
|
||||
sctp->i.verifycode = cookie->iverifycode;
|
||||
sctp->peerhasfwdtsn = cookie->peerhasfwdtsn;
|
||||
sctp->o.writable = true; //channel SHOULD now be open for data (once it gets the ack anyway).
|
||||
|
||||
//let our peer know too.
|
||||
rh->srcport = sctp->myport;
|
||||
rh->dstport = sctp->peerport;
|
||||
rh->verifycode = sctp->o.verifycode;
|
||||
rh->crc = 0;
|
||||
rack->type = SCTP_TYPE_COOKIEACK;
|
||||
rack->flags = 0;
|
||||
rack->length = BigShort(sizeof(*rack));
|
||||
|
||||
//complete. calc the proper crc and send it off.
|
||||
rh->crc = SCTP_Checksum(rh, end-resp);
|
||||
SCTP_PeerSendPacket(sctp, end-resp, rh);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SCTP_TYPE_INIT:
|
||||
if (h->verifycode == 0) //this must be 0 for inits.
|
||||
if (clen >= sizeof(struct sctp_chunk_init_s))
|
||||
if (h->crc == SCTP_Checksum(h, net_message.cursize)) //make sure the crc matches.
|
||||
{
|
||||
struct sctp_chunk_init_s *init = (void*)c;
|
||||
struct {
|
||||
quint16_t ptype;
|
||||
quint16_t plen;
|
||||
} *p = (void*)(init+1);
|
||||
|
||||
struct cookiedata_s cookie = {0};
|
||||
|
||||
cookie.otsn = rand() ^ (rand()<<16);
|
||||
Sys_RandomBytes((void*)&cookie.iverifycode, sizeof(cookie.iverifycode));
|
||||
cookie.ictsn = BigLong(init->tsn)-1;
|
||||
cookie.overifycode = init->verifycode;
|
||||
(void)BigLong(init->arwc);
|
||||
(void)BigShort(init->numoutstreams);
|
||||
(void)BigShort(init->numinstreams);
|
||||
|
||||
while ((qbyte*)p+sizeof(*p) <= (qbyte*)c+clen)
|
||||
{
|
||||
unsigned short ptype = BigShort(p->ptype);
|
||||
unsigned short plen = BigShort(p->plen);
|
||||
switch(ptype)
|
||||
{
|
||||
case 32776: //ASCONF
|
||||
break;
|
||||
case 49152:
|
||||
cookie.peerhasfwdtsn = true;
|
||||
break;
|
||||
default:
|
||||
if (net_ice_debug.ival >= 2)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: SCTP: Found unknown init parameter %i||%#x\n", NET_AdrToString(resp,sizeof(resp), &net_from), ptype, ptype);
|
||||
break;
|
||||
}
|
||||
p = (void*)((qbyte*)p + ((plen+3)&~3));
|
||||
}
|
||||
|
||||
{
|
||||
struct sctp_header_s *rh = (void*)resp;
|
||||
struct sctp_chunk_init_s *rinit = (void*)(rh+1);
|
||||
struct {
|
||||
quint16_t ptype;
|
||||
quint16_t plen;
|
||||
struct cookiedata_s cookie;
|
||||
} *rinitcookie = (void*)(rinit+1);
|
||||
struct {
|
||||
quint16_t ptype;
|
||||
quint16_t plen;
|
||||
} *rftsn = (void*)(rinitcookie+1);
|
||||
qbyte *end = cookie.peerhasfwdtsn?(void*)(rftsn+1):(void*)(rinitcookie+1);
|
||||
|
||||
rh->srcport = h->dstport;
|
||||
rh->dstport = h->srcport;
|
||||
rh->verifycode = init->verifycode;
|
||||
rh->crc = 0;
|
||||
*rinit = *init;
|
||||
rinit->chunk.type = SCTP_TYPE_INITACK;
|
||||
rinit->chunk.flags = 0;
|
||||
rinit->chunk.length = BigShort(end-(qbyte*)rinit);
|
||||
rinit->verifycode = cookie.iverifycode;
|
||||
rinit->arwc = BigLong(65536);
|
||||
rinit->numoutstreams = init->numoutstreams;
|
||||
rinit->numinstreams = init->numinstreams;
|
||||
rinit->tsn = BigLong(cookie.otsn);
|
||||
rinitcookie->ptype = BigShort(7);
|
||||
rinitcookie->plen = BigShort(sizeof(*rinitcookie));
|
||||
memcpy(&rinitcookie->cookie, &cookie, sizeof(rinitcookie->cookie)); //frankly the contents of the cookie are irrelevant to anything. we've already verified the peer's ice pwd/ufrag stuff as well as their dtls certs etc.
|
||||
rftsn->ptype = BigShort(49152);
|
||||
rftsn->plen = BigShort(sizeof(*rftsn));
|
||||
|
||||
//complete. calc the proper crc and send it off.
|
||||
rh->crc = SCTP_Checksum(rh, end-resp);
|
||||
dtlsfuncs->Transmit(dtlsstate, resp, end-resp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
//========================================
|
||||
#endif
|
||||
|
||||
|
@ -4031,9 +4236,24 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
if (con->state == ICE_CONNECTING)
|
||||
ICE_Set(con, "state", STRINGIFY(ICE_CONNECTED));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//only accept actual responses, not spoofed stuff.
|
||||
if (stun->magiccookie == BigLong(0x2112a442)
|
||||
&& stun->transactid[0]==col->srflx_tid[0]
|
||||
&& stun->transactid[1]==col->srflx_tid[1]
|
||||
&& stun->transactid[2]==col->srflx_tid[2]
|
||||
&& !NET_CompareAdr(&col->srflx[adr.type!=NA_IP], &adr))
|
||||
{
|
||||
if (col->srflx[adr.type!=NA_IP].type==NA_INVALID)
|
||||
Con_Printf(S_COLOR_GRAY"Public address reported as %s\n", NET_AdrToString(errmsg, sizeof(errmsg), &adr));
|
||||
else
|
||||
Con_Printf(CON_ERROR"Server reflexive address changed to %s\n", NET_AdrToString(errmsg, sizeof(errmsg), &adr));
|
||||
col->srflx[adr.type!=NA_IP] = adr;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -4465,6 +4685,7 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
unsigned int tielow = 0;
|
||||
qboolean usecandidate = false;
|
||||
unsigned int priority = 0;
|
||||
char *lpwd = NULL;
|
||||
#endif
|
||||
char *integritypos = NULL;
|
||||
int error = 0;
|
||||
|
@ -4645,6 +4866,7 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
if (con->state == ICE_CONNECTING)
|
||||
ICE_Set(con, "state", STRINGIFY(ICE_CONNECTED));
|
||||
}
|
||||
lpwd = con->lpwd;
|
||||
}
|
||||
}//otherwise its just an ip check
|
||||
else
|
||||
|
@ -4736,13 +4958,13 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
MSG_WriteChar(&buf, 0);
|
||||
|
||||
#ifdef SUPPORT_ICE
|
||||
if (con)
|
||||
if (lpwd)
|
||||
{
|
||||
//message integrity is a bit annoying
|
||||
data[2] = ((buf.cursize+4+sizeof(integrity)-20)>>8)&0xff; //hashed header length is up to the end of the hmac attribute
|
||||
data[3] = ((buf.cursize+4+sizeof(integrity)-20)>>0)&0xff;
|
||||
//but the hash is to the start of the attribute's header
|
||||
CalcHMAC(&hash_sha1, integrity, sizeof(integrity), data, buf.cursize, con->lpwd, strlen(con->lpwd));
|
||||
CalcHMAC(&hash_sha1, integrity, sizeof(integrity), data, buf.cursize, lpwd, strlen(lpwd));
|
||||
MSG_WriteShort(&buf, BigShort(STUNATTR_MESSAGEINTEGRITIY));
|
||||
MSG_WriteShort(&buf, BigShort(sizeof(integrity))); //sha1 key length
|
||||
SZ_Write(&buf, integrity, sizeof(integrity)); //integrity data
|
||||
|
@ -4760,10 +4982,11 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
data[3] = ((buf.cursize-20)>>0)&0xff;
|
||||
|
||||
#ifdef SUPPORT_ICE
|
||||
if (con)
|
||||
TURN_Encapsulate(con, &net_from, data, buf.cursize);
|
||||
#else
|
||||
NET_SendPacket(col, buf.cursize, data, &net_from);
|
||||
else
|
||||
#endif
|
||||
NET_SendPacket(col, buf.cursize, data, &net_from);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -4803,7 +5026,7 @@ qboolean ICE_WasStun(ftenet_connections_t *col)
|
|||
net_from = con->qadr;
|
||||
#ifdef HAVE_DTLS
|
||||
if (con->sctp)
|
||||
SCTP_Decode(con->sctp, con, col);
|
||||
SCTP_Decode(con->sctp, col);
|
||||
else
|
||||
#endif
|
||||
if (net_message.cursize)
|
||||
|
@ -4862,9 +5085,9 @@ neterr_t ICE_SendPacket(size_t length, const void *data, netadr_t *to)
|
|||
return NETERR_DISCONNECTED;
|
||||
#ifdef HAVE_DTLS
|
||||
if (con->sctp)
|
||||
return SCTP_Transmit(con->sctp, con, data, length);
|
||||
return SCTP_Transmit(con->sctp, data, length);
|
||||
if (con->dtlsstate)
|
||||
return SCTP_PeerSendPacket(con, length, data);
|
||||
return con->dtlsfuncs->Transmit(con->dtlsstate, data, length);
|
||||
#endif
|
||||
if (con->chosenpeer.type != NA_INVALID)
|
||||
return ICE_Transmit(con, data, length);
|
||||
|
@ -5047,7 +5270,7 @@ static void FTENET_ICE_SendOffer(ftenet_ice_connection_t *b, int cl, struct ices
|
|||
ice->blockcandidates = false;
|
||||
}
|
||||
}
|
||||
static void FTENET_ICE_Establish(ftenet_ice_connection_t *b, int cl, struct icestate_s **ret)
|
||||
static void FTENET_ICE_Establish(ftenet_ice_connection_t *b, const char *peeraddr, int cl, struct icestate_s **ret)
|
||||
{ //sends offer
|
||||
struct icestate_s *ice;
|
||||
qboolean usewebrtc;
|
||||
|
@ -5062,7 +5285,7 @@ static void FTENET_ICE_Establish(ftenet_ice_connection_t *b, int cl, struct ices
|
|||
else
|
||||
usewebrtc = net_ice_usewebrtc.ival;
|
||||
#endif
|
||||
ice = *ret = iceapi.Create(b, NULL, b->generic.islisten?NULL:va("/%s", b->gamename), usewebrtc?ICEM_WEBRTC:ICEM_ICE, b->generic.islisten?ICEP_QWSERVER:ICEP_QWCLIENT, !b->generic.islisten);
|
||||
ice = *ret = iceapi.Create(b, NULL, b->generic.islisten?((peeraddr&&*peeraddr)?va("%s:%i", peeraddr,cl):NULL):va("/%s", b->gamename), usewebrtc?ICEM_WEBRTC:ICEM_ICE, b->generic.islisten?ICEP_QWSERVER:ICEP_QWCLIENT, !b->generic.islisten);
|
||||
if (!*ret)
|
||||
return; //some kind of error?!?
|
||||
|
||||
|
@ -5259,12 +5482,12 @@ handleerror:
|
|||
{
|
||||
b->error = true;
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Broker lost connection: %s\n", b->ice?b->ice->friendlyname:"?", *data?data:"<NO REASON>");
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Broker host lost connection: %s\n", b->ice?b->ice->friendlyname:"?", *data?data:"<NO REASON>");
|
||||
}
|
||||
else if (cl >= 0 && cl < b->numclients)
|
||||
{
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Broker lost connection: %s\n", b->clients[cl].ice?b->clients[cl].ice->friendlyname:"?", *data?data:"<NO REASON>");
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: Broker client lost connection: %s\n", b->clients[cl].ice?b->clients[cl].ice->friendlyname:"?", *data?data:"<NO REASON>");
|
||||
if (b->clients[cl].ice)
|
||||
iceapi.Close(b->clients[cl].ice, false);
|
||||
b->clients[cl].ice = NULL;
|
||||
|
@ -5291,7 +5514,7 @@ handleerror:
|
|||
}
|
||||
if (cl >= 0 && cl < b->numclients)
|
||||
{
|
||||
FTENET_ICE_Establish(b, cl, &b->clients[cl].ice);
|
||||
FTENET_ICE_Establish(b, (len>3)?data:NULL, cl, &b->clients[cl].ice);
|
||||
|
||||
if (net_ice_debug.ival)
|
||||
Con_Printf(S_COLOR_GRAY"[%s]: New client spotted...\n", b->clients[cl].ice?b->clients[cl].ice->friendlyname:"?");
|
||||
|
@ -5302,7 +5525,7 @@ handleerror:
|
|||
else
|
||||
{
|
||||
// Con_DPrintf("Server found: %s\n", data);
|
||||
FTENET_ICE_Establish(b, cl, &b->ice);
|
||||
FTENET_ICE_Establish(b, (len>3)?data:NULL, cl, &b->ice);
|
||||
b->serverid = cl;
|
||||
|
||||
if (net_ice_debug.ival)
|
||||
|
@ -5526,4 +5749,134 @@ void ICE_Init(void)
|
|||
Cvar_Register(&net_ice_debug, "networking");
|
||||
Cmd_AddCommand("net_ice_show", ICE_Show_f);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_SERVER
|
||||
void SVC_ICE_Offer(void)
|
||||
{ //handles an 'ice_offer' udp message from a broker
|
||||
extern cvar_t net_ice_servers;
|
||||
struct icestate_s *ice;
|
||||
static float throttletimer;
|
||||
char *sdp, *s;
|
||||
char buf[1400];
|
||||
int sz;
|
||||
char *clientaddr = Cmd_Argv(1); //so we can do ip bans on the client's srflx address
|
||||
char *brokerid = Cmd_Argv(2); //specific id to identify the pairing on the broker.
|
||||
netadr_t adr;
|
||||
if (!sv.state)
|
||||
return; //err..?
|
||||
if (net_from.prot != NP_DTLS && net_from.prot != NP_WSS && net_from.prot != NP_TLS)
|
||||
{ //a) dtls provides a challenge.
|
||||
//b) this contains the caller's ips. We'll be pinging them anyway, but hey. also it'll be too late at this point but it keeps the other side honest.
|
||||
Con_ThrottlePrintf(&throttletimer, 0, CON_WARNING"%s: ice handshake via %s was unencrypted\n", NET_AdrToString (buf, sizeof(buf), &net_from));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NET_StringToAdr_NoDNS(clientaddr, 0, &adr)) //no dns-resolution denial-of-service attacks please.
|
||||
{
|
||||
Con_ThrottlePrintf(&throttletimer, 0, CON_WARNING"%s: ice handshake specifies bad client address: %s\n", NET_AdrToString (buf, sizeof(buf), &net_from), clientaddr);
|
||||
return;
|
||||
}
|
||||
if (SV_BannedReason(&adr)!=NULL)
|
||||
{
|
||||
Con_ThrottlePrintf(&throttletimer, 0, CON_WARNING"%s: ice handshake for %s - banned\n", NET_AdrToString (buf, sizeof(buf), &net_from), clientaddr);
|
||||
return;
|
||||
}
|
||||
|
||||
ice = iceapi.Create(NULL, brokerid, clientaddr, ICEM_WEBRTC, ICEP_QWSERVER, false);
|
||||
if (!ice)
|
||||
return; //some kind of error?!?
|
||||
//use the sender as a stun server. FIXME: put server's address in the request instead.
|
||||
iceapi.Set(ice, "server", va("stun:%s", NET_AdrToString (buf, sizeof(buf), &net_from))); //the sender should be able to act as a stun server for use. should probably just pass who its sending to and call it a srflx anyway, tbh.
|
||||
|
||||
s = net_ice_servers.string;
|
||||
while((s=COM_Parse(s)))
|
||||
iceapi.Set(ice, "server", com_token);
|
||||
|
||||
sdp = MSG_ReadString();
|
||||
if (!strncmp(sdp, "{\"type\":\"offer\",\"sdp\":\"", 23))
|
||||
{ //browsers are poo
|
||||
sdp += 22;
|
||||
COM_ParseCString(sdp, buf, sizeof(buf), NULL);
|
||||
sdp = buf;
|
||||
}
|
||||
|
||||
if (iceapi.Set(ice, "sdpoffer", sdp))
|
||||
{
|
||||
iceapi.Set(ice, "state", STRINGIFY(ICE_CONNECTING)); //skip gathering, just trickle.
|
||||
|
||||
Q_snprintfz(buf, sizeof(buf), "\xff\xff\xff\xff""ice_answer %s", brokerid);
|
||||
sz = strlen(buf)+1;
|
||||
if (iceapi.Get(ice, "sdpanswer", buf+sz, sizeof(buf)-sz))
|
||||
{
|
||||
sz += strlen(buf+sz);
|
||||
|
||||
NET_SendPacket(svs.sockets, sz, buf, &net_from);
|
||||
}
|
||||
}
|
||||
|
||||
//and because we won't have access to its broker, disconnect it from any persistent state to let it time out.
|
||||
iceapi.Close(ice, false);
|
||||
}
|
||||
void SVC_ICE_Candidate(void)
|
||||
{
|
||||
struct icestate_s *ice;
|
||||
char *sdp;
|
||||
char buf[1400];
|
||||
char *brokerid = Cmd_Argv(1); //specific id to identify the pairing on the broker.
|
||||
unsigned int seq = atoi(Cmd_Argv(2)); //their seq, to ack and prevent dupes
|
||||
unsigned int ack = atoi(Cmd_Argv(3)); //so we don't resend endlessly... *cough*
|
||||
if (net_from.prot != NP_DTLS && net_from.prot != NP_WSS && net_from.prot != NP_TLS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ice = iceapi.Find(NULL, brokerid);
|
||||
if (!ice)
|
||||
return; //bad state. lost packet?
|
||||
|
||||
//parse the inbound candidates
|
||||
for(;;)
|
||||
{
|
||||
sdp = MSG_ReadStringLine();
|
||||
if (msg_badread || !*sdp)
|
||||
break;
|
||||
if (seq++ < ice->u.inseq)
|
||||
continue;
|
||||
ice->u.inseq++;
|
||||
if (!strncmp(sdp, "{\"candidate\":\"", 14))
|
||||
{ //dewebify
|
||||
sdp += 13;
|
||||
buf[0]='a';
|
||||
buf[1]='=';
|
||||
COM_ParseCString(sdp, buf+2, sizeof(buf)-2, NULL);
|
||||
sdp = buf;
|
||||
}
|
||||
iceapi.Set(ice, "sdp", sdp);
|
||||
}
|
||||
|
||||
while (ack > ice->u.outseq)
|
||||
{ //drop an outgoing candidate line
|
||||
char *nl = strchr(ice->u.text, '\n');
|
||||
if (nl)
|
||||
{
|
||||
nl++;
|
||||
memmove(ice->u.text, nl, strlen(nl)+1); //chop it away.
|
||||
ice->u.outseq++;
|
||||
continue;
|
||||
}
|
||||
//wut?
|
||||
if (ack > ice->u.outseq)
|
||||
ice->u.outseq = ack; //a gap? oh noes!
|
||||
break;
|
||||
}
|
||||
|
||||
//check for new candidates to include
|
||||
while (iceapi.GetLCandidateSDP(ice, buf, sizeof(buf)))
|
||||
Z_StrCat(&ice->u.text, buf);
|
||||
|
||||
Q_snprintfz(buf, sizeof(buf), "\xff\xff\xff\xff""ice_scand %s %u %u\n%s", brokerid, ice->u.outseq, ice->u.inseq, ice->u.text?ice->u.text:"");
|
||||
NET_SendPacket(svs.sockets, strlen(buf), buf, &net_from);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -292,8 +292,8 @@ typedef struct
|
|||
void (QDECL *AddRCandidateInfo)(struct icestate_s *con, struct icecandinfo_s *cand); //stuff that came from the peer.
|
||||
void (QDECL *Close)(struct icestate_s *con, qboolean force); //bye then.
|
||||
void (QDECL *CloseModule)(void *module); //closes all unclosed connections, with warning.
|
||||
// struct icestate_s *(QDECL *Find)(void *module, const char *conname);
|
||||
qboolean (QDECL *GetLCandidateSDP)(struct icestate_s *con, char *out, size_t valuesize); //retrieves candidates that need reporting to the peer.
|
||||
struct icestate_s *(QDECL *Find)(void *module, const char *conname);
|
||||
} icefuncs_t;
|
||||
extern icefuncs_t iceapi;
|
||||
extern cvar_t net_ice_broker;
|
||||
|
@ -435,6 +435,9 @@ typedef struct ftenet_connections_s
|
|||
size_t cursize;
|
||||
qbyte data[1];
|
||||
} *delayed_packets;
|
||||
|
||||
netadr_t srflx[2]; //ipv4, ipv6
|
||||
unsigned int srflx_tid[3]; //to verify the above.
|
||||
} ftenet_connections_t;
|
||||
|
||||
void ICE_Tick(void);
|
||||
|
|
|
@ -254,6 +254,16 @@ char *Z_StrDupf(const char *format, ...)
|
|||
|
||||
return string;
|
||||
}
|
||||
void Z_StrCatLen(char **ptr, const char *append, size_t newlen)
|
||||
{
|
||||
size_t oldlen = *ptr?strlen(*ptr):0;
|
||||
char *newptr = BZ_Malloc(oldlen+newlen+1);
|
||||
memcpy(newptr, *ptr, oldlen);
|
||||
memcpy(newptr+oldlen, append, newlen);
|
||||
newptr[oldlen+newlen] = 0;
|
||||
BZ_Free(*ptr);
|
||||
*ptr = newptr;
|
||||
}
|
||||
void Z_StrCat(char **ptr, const char *append)
|
||||
{
|
||||
size_t oldlen = *ptr?strlen(*ptr):0;
|
||||
|
|
|
@ -151,6 +151,7 @@ void QDECL ZG_FreeGroup(zonegroup_t *ctx);
|
|||
|
||||
char *Z_StrDupf(const char *format, ...);
|
||||
void Z_StrCat(char **ptr, const char *append);
|
||||
void Z_StrCatLen(char **ptr, const char *append, size_t newlen); //still doesn't allow nulls, but src doesn't need null termination.
|
||||
|
||||
/*
|
||||
void *Hunk_Alloc (int size); // returns 0 filled memory
|
||||
|
|
|
@ -305,7 +305,7 @@ const char *HTTP_RunClient (HTTP_active_connections_t *cl)
|
|||
char *content;
|
||||
char *msg, *nl;
|
||||
char buf2[2560]; //short lived temp buffer.
|
||||
char resource[2560];
|
||||
char resource[2560], *args;
|
||||
char host[256];
|
||||
char mode[80];
|
||||
qboolean hostspecified;
|
||||
|
@ -389,6 +389,10 @@ const char *HTTP_RunClient (HTTP_active_connections_t *cl)
|
|||
}
|
||||
}
|
||||
|
||||
args = strchr(resource, '?');
|
||||
if (args)
|
||||
*args++=0;
|
||||
|
||||
if (!strcmp(resource, "/"))
|
||||
strcpy(resource, "/index.html");
|
||||
|
||||
|
|
|
@ -6462,18 +6462,31 @@ char *PF_infokey_Internal (int entnum, const char *key)
|
|||
sprintf(ov, "%d", SV_CalcPing (&svs.clients[entnum-1], true));
|
||||
else if (!strcmp(key, "guid"))
|
||||
sprintf(ov, "%s", pl->guid);
|
||||
else if (!strcmp(key, "*cert_sha1"))
|
||||
{
|
||||
char buf[8192];
|
||||
char digest[DIGEST_MAXSIZE];
|
||||
int certsize = NET_GetConnectionCertificate(svs.sockets, &controller->netchan.remote_address, QCERT_PEERCERTIFICATE, buf, sizeof(buf));
|
||||
if (certsize <= 0)
|
||||
value = "";
|
||||
else
|
||||
Base64_EncodeBlockURI(digest,CalcHash(&hash_sha1, digest, sizeof(digest), buf, certsize), ov, sizeof(ov));
|
||||
}
|
||||
else if (!strcmp(key, "*cert_dn"))
|
||||
NET_GetConnectionCertificate(svs.sockets, &controller->netchan.remote_address, QCERT_PEERSUBJECT, ov, sizeof(ov));
|
||||
else if (!strncmp(key, "*cert_", 6))
|
||||
{
|
||||
static struct
|
||||
{
|
||||
const char *name;
|
||||
hashfunc_t *func;
|
||||
} funcs[] = {{"sha1",&hash_sha1}, {"sha2_256", &hash_sha2_256}, {"sha2_512", &hash_sha2_512}};
|
||||
int i;
|
||||
char buf[8192];
|
||||
char digest[DIGEST_MAXSIZE];
|
||||
int certsize;
|
||||
*ov = 0;
|
||||
for (i = 0; i < countof(funcs); i++)
|
||||
{
|
||||
if (!strcmp(key+6, funcs[i].name))
|
||||
{
|
||||
certsize = NET_GetConnectionCertificate(svs.sockets, &controller->netchan.remote_address, QCERT_PEERCERTIFICATE, buf, sizeof(buf));
|
||||
if (certsize > 0)
|
||||
Base64_EncodeBlockURI(digest,CalcHash(&hash_sha1, digest, sizeof(digest), buf, certsize), ov, sizeof(ov));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp(key, "challenge"))
|
||||
sprintf(ov, "%u", pl->challenge);
|
||||
else if (!strcmp(key, "*userid"))
|
||||
|
|
|
@ -697,6 +697,7 @@ typedef struct client_s
|
|||
qboolean qex; //qex sends strange clc inputs and needs workarounds for its prediction. it also favours fitzquake's protocol but violates parts of it.
|
||||
|
||||
unsigned int lastruncmd; //for non-qw physics. timestamp they were last run, so switching between physics modes isn't a (significant) cheat
|
||||
unsigned int hoverms; //purely for sv_showpredloss to avoid excessive spam
|
||||
//speed cheat testing
|
||||
#define NEWSPEEDCHEATPROT
|
||||
float msecs;
|
||||
|
@ -1385,10 +1386,12 @@ void SV_SendClientPrespawnInfo(client_t *client);
|
|||
void SV_ClientProtocolExtensionsChanged(client_t *client);
|
||||
|
||||
//sv_master.c
|
||||
float SVM_Think(int port);
|
||||
float SVM_Think(void);
|
||||
vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname, const char **mimetype, const char *query);
|
||||
void SVM_AddBrokerGame(const char *brokerid, const char *info);
|
||||
void SVM_RemoveBrokerGame(const char *brokerid);
|
||||
qboolean SVM_FixupServerAddress(netadr_t *adr, struct dtlspeercred_s *cred);
|
||||
void FTENET_TCP_ICEResponse(struct ftenet_connections_s *col, int type, const char *cid, const char *sdp);
|
||||
|
||||
|
||||
//
|
||||
|
|
|
@ -2160,9 +2160,6 @@ static void SV_Status_f (void)
|
|||
#ifdef QWOVERQ3
|
||||
extern cvar_t sv_listen_q3;
|
||||
#endif
|
||||
#ifdef HAVE_DTLS
|
||||
extern cvar_t net_enable_dtls;
|
||||
#endif
|
||||
|
||||
#ifndef SERVERONLY
|
||||
if (!sv.state && cls.state >= ca_connected && !cls.demoplayback && cls.protocol == CP_NETQUAKE)
|
||||
|
@ -2210,9 +2207,6 @@ static void SV_Status_f (void)
|
|||
s = "private";
|
||||
Con_TPrintf("public : %s\n", s);
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
Con_TPrintf("fingerprint : "S_COLOR_GRAY"%s\n", InfoBuf_ValueForKey(&svs.info, "*fp"));
|
||||
#endif
|
||||
switch(svs.gametype)
|
||||
{
|
||||
#ifdef Q3SERVER
|
||||
|
|
|
@ -140,7 +140,6 @@ cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0");
|
|||
#endif
|
||||
cvar_t sv_reconnectlimit = CVARD("sv_reconnectlimit", "0", "Blocks dupe connection within the specified length of time .");
|
||||
cvar_t sv_use_dns = CVARD("sv_use_dns", "", "Performs a reverse-dns lookup in order to report more info about where clients are connecting from.");
|
||||
extern cvar_t net_enable_dtls;
|
||||
cvar_t sv_reportheartbeats = CVARD("sv_reportheartbeats", "2", "Print a notice each time a heartbeat is sent to a master server. When set to 2, the message will be displayed once.");
|
||||
cvar_t sv_heartbeat_interval = CVARD("sv_heartbeat_interval", "110", "Interval between heartbeats. Low values are abusive, high values may cause NAT/ghost issues.");
|
||||
cvar_t sv_heartbeat_checks = CVARD("sv_heartbeat_checks", "1", "Report when sv_public 1 fails due to PROBABLE router/NAT issues.");
|
||||
|
@ -162,7 +161,6 @@ cvar_t sv_pupglow = CVARFD("sv_pupglow", "", CVAR_SERVERINFO, "Instructs clie
|
|||
|
||||
#ifdef SV_MASTER
|
||||
cvar_t sv_master = CVAR("sv_master", "0");
|
||||
cvar_t sv_masterport = CVAR("sv_masterport", "0");
|
||||
#endif
|
||||
|
||||
cvar_t sv_reliable_sound = CVARFD("sv_reliable_sound", "0", 0, "Causes all sounds to be sent reliably, so they will not be missed due to packetloss. However, this will cause them to be delayed somewhat, and slightly bursty. This can be overriden using the 'rsnd' userinfo setting (either forced on or forced off). Note: this does not affect sounds attached to particle effects.");
|
||||
|
@ -3113,9 +3111,12 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
|
|||
newcl->netchan.mtu = info->mtu;
|
||||
newcl->netchan.message.maxsize = sizeof(newcl->netchan.message_buf);
|
||||
|
||||
#ifdef HAVE_ICE
|
||||
if (info->adr.type == NA_ICE)
|
||||
newcl->netchan.mtu -= 48+12; //dtls+sctp overhead
|
||||
else if (info->adr.prot == NP_DTLS || info->adr.prot == NP_TLS)
|
||||
else
|
||||
#endif
|
||||
if (info->adr.prot == NP_DTLS || info->adr.prot == NP_TLS)
|
||||
newcl->netchan.mtu -= 48; //dtls overhead
|
||||
}
|
||||
else
|
||||
|
@ -4042,9 +4043,14 @@ void SVC_ACK (void)
|
|||
}
|
||||
}
|
||||
}
|
||||
Con_TPrintf ("A2A_ACK from %s\n", NET_AdrToString (adr, sizeof(adr), &net_from));
|
||||
Con_TPrintf (S_COLOR_GRAY"A2A_ACK from %s\n", NET_AdrToString (adr, sizeof(adr), &net_from));
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_ICE
|
||||
void SVC_ICE_Offer(void);
|
||||
void SVC_ICE_Candidate(void);
|
||||
#endif
|
||||
|
||||
//returns false to block replies
|
||||
//this is to mitigate wasted bandwidth if we're used as a udp amplification
|
||||
qboolean SVC_ThrottleInfo (void)
|
||||
|
@ -4083,7 +4089,7 @@ static struct attacker_s
|
|||
} *dosattacker;
|
||||
static size_t dosattacker_count;
|
||||
static size_t dosattacker_max;
|
||||
#define dosattacker_limit 10 //if we get X packets
|
||||
#define dosattacker_limit 15 //if we get X packets
|
||||
#define dosattacker_period 30 //within Y secs
|
||||
#define dosattacker_blocktime (60*60*24) //block them for Z secs (24 hours).
|
||||
static qboolean SV_DetectAmplificationDDOS (void)
|
||||
|
@ -4286,8 +4292,10 @@ qboolean SV_ConnectionlessPacket (void)
|
|||
else
|
||||
{
|
||||
//NET_DTLS_Disconnect(svs.sockets, &net_from);
|
||||
if (NET_DTLS_Create(svs.sockets, &net_from, NULL))
|
||||
if (NET_DTLS_Create(svs.sockets, &net_from, NULL, false))
|
||||
Netchan_OutOfBandPrint(NS_SERVER, &net_from, "dtlsopened");
|
||||
else
|
||||
SV_RejectMessage (SCP_QUAKEWORLD, "DTLS driver failure.\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -4332,6 +4340,13 @@ qboolean SV_ConnectionlessPacket (void)
|
|||
}
|
||||
else if (!strcmp(c, "realip") || !strcmp(c, "ip"))
|
||||
SVC_RealIP ();
|
||||
|
||||
#ifdef SUPPORT_ICE
|
||||
else if (!strcmp(c, "ice_offer"))
|
||||
SVC_ICE_Offer();
|
||||
else if (!strcmp(c, "ice_ccand"))
|
||||
SVC_ICE_Candidate();
|
||||
#endif
|
||||
/*
|
||||
else if (!strcmp(c,"lastscores"))
|
||||
{
|
||||
|
@ -5077,10 +5092,6 @@ qboolean SV_ReadPackets (float *delay)
|
|||
|
||||
NET_ReadPackets(svs.sockets);
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
NET_DTLS_Timeouts(svs.sockets);
|
||||
#endif
|
||||
|
||||
if (inboundsequence == oldinboundsequence)
|
||||
return false; //nothing new.
|
||||
oldinboundsequence = inboundsequence;
|
||||
|
@ -5513,12 +5524,7 @@ float SV_Frame (void)
|
|||
|
||||
#ifdef SV_MASTER
|
||||
if (sv_master.ival)
|
||||
{
|
||||
if (sv_masterport.ival)
|
||||
SVM_Think(sv_masterport.ival);
|
||||
else
|
||||
SVM_Think(PORT_QWMASTER);
|
||||
}
|
||||
SVM_Think();
|
||||
#endif
|
||||
|
||||
#ifdef PLUGINS
|
||||
|
@ -5882,7 +5888,6 @@ void SV_InitLocal (void)
|
|||
Cvar_Register (&sv_banproxies, cvargroup_serverpermissions);
|
||||
#ifdef SV_MASTER
|
||||
Cvar_Register (&sv_master, cvargroup_servercontrol);
|
||||
Cvar_Register (&sv_masterport, cvargroup_servercontrol);
|
||||
#endif
|
||||
|
||||
Cvar_Register (&filterban, cvargroup_servercontrol);
|
||||
|
|
|
@ -45,6 +45,7 @@ typedef struct svm_server_s {
|
|||
unsigned int bots; //non-human players
|
||||
unsigned int clients; //human players
|
||||
unsigned int maxclients; //limit of bots+clients, but not necessarily spectators.
|
||||
int secure:1;
|
||||
int needpass:1;
|
||||
int coop:1;
|
||||
char hostname[64]; //just for our own listings.
|
||||
|
@ -189,6 +190,28 @@ static svm_server_t *SVM_GetServer(netadr_t *adr)
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
qboolean SVM_FixupServerAddress(netadr_t *adr, struct dtlspeercred_s *cred)
|
||||
{ //if we get a request to send an ice offer over udp, make sure we respond from the socket they heartbeated from, so their (possible) nat won't block us.
|
||||
//also make sure the fingerprint stuff is okay.
|
||||
svm_server_t *sv = SVM_GetServer(adr);
|
||||
char *fp;
|
||||
size_t b;
|
||||
if (!sv)
|
||||
return false;
|
||||
*adr = sv->adr; //fix it up (mostly the connum so it follows the proper 'return' route)
|
||||
fp = Info_ValueForKey(sv->rules, "*fp");
|
||||
b = Base64_DecodeBlock(fp, NULL, cred->digest, sizeof(cred->digest));
|
||||
if (b <= 20)
|
||||
cred->hash = &hash_sha1;
|
||||
else if (b <= 256/8)
|
||||
cred->hash = &hash_sha2_256;
|
||||
else if (b <= 512/8)
|
||||
cred->hash = &hash_sha2_512;
|
||||
else
|
||||
return false; //just no.
|
||||
memset(cred->digest+b, 0, cred->hash->digestsize-b); //make sure its -terminated, in case the provided size was wrong
|
||||
return true;
|
||||
}
|
||||
|
||||
static svm_game_t *SVM_FindGame(const char *game, int create)
|
||||
{
|
||||
|
@ -579,7 +602,7 @@ vfsfile_t *SVM_Generate_Gamelist(const char **mimetype, const char *query)
|
|||
if (game->numservers || !sv_hideinactivegames.ival) //only show active servers
|
||||
{
|
||||
QuakeCharsToHTML(tmpbuf, sizeof(tmpbuf), game->name, true);
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"game/%s%s%s\">%s</a></td><td>%u player%s", game->name, query?"?":"", query?query:"", tmpbuf, clients, clients==1?"":"s");
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"/game/%s%s%s\">%s</a></td><td>%u player%s", game->name, query?"?":"", query?query:"", tmpbuf, clients, clients==1?"":"s");
|
||||
if (bots)
|
||||
VFS_PRINTF(f, ", %u bot%s", bots, bots==1?"":"s");
|
||||
if (specs)
|
||||
|
@ -634,12 +657,12 @@ static int QDECL SVM_SortServerRule(const void *r1, const void *r2)
|
|||
vfsfile_t *SVM_Generate_Serverinfo(const char **mimetype, const char *serveraddr, const char *query)
|
||||
{
|
||||
vfsfile_t *f = VFSPIPE_Open(1, false);
|
||||
char tmpbuf[256];
|
||||
char tmpbuf[512];
|
||||
char hostname[1024];
|
||||
svm_server_t *server;
|
||||
netadr_t adr[64];
|
||||
size_t count, u;
|
||||
const char *url;
|
||||
const char *url, *fp;
|
||||
|
||||
VFS_PRINTF(f, "%s", master_css);
|
||||
VFS_PRINTF(f, "<h1>Single Server Info</h1>\n");
|
||||
|
@ -656,10 +679,17 @@ vfsfile_t *SVM_Generate_Serverinfo(const char **mimetype, const char *serveraddr
|
|||
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
||||
|
||||
url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
||||
fp = Info_ValueForKey(server->rules, "*fp");
|
||||
if (*fp)
|
||||
fp = va("?fp=%s", Info_ValueForKey(server->rules, "*fp"));
|
||||
if (server->game->scheme && !server->brokerid)
|
||||
url = va("<a href=\"%s://%s\">%s</a>", server->game->scheme, url, url);
|
||||
url = va("<a href=\"%s://%s%s\">%s</a>", server->game->scheme, url,fp, url);
|
||||
|
||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", server->game?server->game->name:"Unknown", url, (server->needpass&1)?"🔒":"", hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"/game/%s%s%s\">%s</a></td><td>%s</td><td>%s%s%s%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n",
|
||||
server->game?server->game->name:"Unknown", query?"?":"", query?query:"", server->game?server->game->name:"Unknown", //game column
|
||||
url, //address column
|
||||
server->secure?"🛡":"🚫", (server->needpass&1)?"🔒":"", (server->coop&1)?"🚸":"", hostname, //hostname column
|
||||
server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
VFS_PRINTF(f, "<br/>\n");
|
||||
|
||||
|
@ -749,7 +779,7 @@ vfsfile_t *SVM_Generate_Serverlist(const char **mimetype, const char *masteraddr
|
|||
infourl = url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
||||
}
|
||||
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"/server/%s\">%s</a></td><td>%s%s%s</td><td>%s</td><td>%s</td><td>%u", infourl, url, (server->needpass&1)?"🔒":"", (server->coop&1)?"🚸":"", hostname, server->gamedir, server->mapname, server->clients);
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"/server/%s\">%s</a></td><td>%s%s%s%s</td><td>%s</td><td>%s</td><td>%u", infourl, url, server->secure?"🛡":"🚫", (server->needpass&1)?"🔒":"", (server->coop&1)?"🚸":"", hostname, server->gamedir, server->mapname, server->clients);
|
||||
if (server->bots)
|
||||
VFS_PRINTF(f, "+%ub", server->bots);
|
||||
VFS_PRINTF(f, "/%u", server->maxclients);
|
||||
|
@ -786,6 +816,10 @@ vfsfile_t *SVM_Generate_Rawlist(const char **mimetype, const char *masteraddr, c
|
|||
svm_game_t *game;
|
||||
svm_server_t *server;
|
||||
vfsfile_t *f = VFSPIPE_Open(1, false);
|
||||
char *fp;
|
||||
char *prot;
|
||||
|
||||
masteraddr = ""; //client should work this out based on where it got the list from.
|
||||
|
||||
COM_StripExtension(gamename, tmpbuf, sizeof(tmpbuf));
|
||||
game = SVM_FindGame(tmpbuf, false);
|
||||
|
@ -794,8 +828,13 @@ vfsfile_t *SVM_Generate_Rawlist(const char **mimetype, const char *masteraddr, c
|
|||
VFS_PRINTF(f, "#Server list for \"%s\"\n", tmpbuf);
|
||||
for (server = (game?game->firstserver:NULL); server; server = server->next)
|
||||
{
|
||||
prot = Info_ValueForKey(server->rules, "protocol");
|
||||
if (!*prot)
|
||||
prot = va("%i", server->protover);
|
||||
if (server->brokerid)
|
||||
VFS_PRINTF(f, "rtc://%s/%s \\maxclients\\%u\\clients\\%u\\bots\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s\\needpass\\%i\n", masteraddr, server->brokerid, server->maxclients, server->clients, server->bots, *server->hostname?server->hostname:"unnamed", *server->gamedir?server->gamedir:"-", *server->mapname?server->mapname:"-", server->needpass);
|
||||
VFS_PRINTF(f, "rtc://%s/%s \\protocol\\%s\\maxclients\\%u\\clients\\%u\\bots\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s\\needpass\\%i\n", masteraddr, server->brokerid, prot, server->maxclients, server->clients, server->bots, *server->hostname?server->hostname:"unnamed", *server->gamedir?server->gamedir:"-", *server->mapname?server->mapname:"-", server->needpass?1:0);
|
||||
else if ((fp = Info_ValueForKey(server->rules, "*fp")))
|
||||
VFS_PRINTF(f, "rtc://%s/udp/%s \\protocol\\%s\\maxclients\\%u\\clients\\%u\\bots\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s\\needpass\\%i\\*fp\\%s\n", masteraddr, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr), prot, server->maxclients, server->clients, server->bots, *server->hostname?server->hostname:"unnamed", *server->gamedir?server->gamedir:"-", *server->mapname?server->mapname:"-", server->needpass?1:0, fp);
|
||||
else
|
||||
VFS_PRINTF(f, "%s\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr));
|
||||
}
|
||||
|
@ -1112,6 +1151,13 @@ static void SVM_ProcessUDPPacket(void)
|
|||
*(int*)&net_from.address.ip6[12]=0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
if (*(int *)net_message.data != -1)
|
||||
if (NET_DTLS_Decode(svm_sockets))
|
||||
if (!net_message.cursize)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (NET_WasSpecialPacket(svm_sockets))
|
||||
{
|
||||
svm.total.stun++;
|
||||
|
@ -1258,7 +1304,6 @@ static void SVM_ProcessUDPPacket(void)
|
|||
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
||||
if (!strcmp(chal, ourchallenge))
|
||||
{
|
||||
|
||||
bots = atoi(Info_ValueForKey(s, "bots"));
|
||||
clients = atoi(Info_ValueForKey(s, "clients"));
|
||||
clients = max(0, clients-bots);
|
||||
|
@ -1272,11 +1317,13 @@ static void SVM_ProcessUDPPacket(void)
|
|||
if (srv)
|
||||
{
|
||||
Q_strncpyz(srv->rules, s, sizeof(srv->rules));
|
||||
Info_RemoveKey(srv->rules, "challenge"); //prevent poisoning
|
||||
if (developer.ival)
|
||||
Info_Print(s, "\t");
|
||||
if (game)
|
||||
srv->protover = atoi(Info_ValueForKey(s, "protocol"));
|
||||
srv->maxclients = atoi(Info_ValueForKey(s, "sv_maxclients"));
|
||||
srv->secure = !!*Info_ValueForKey(s, "*fp");
|
||||
srv->needpass = atoi(Info_ValueForKey(s, "needpass"));
|
||||
srv->coop = atoi(Info_ValueForKey(s, "coop"));
|
||||
if (!srv->coop)
|
||||
|
@ -1453,6 +1500,7 @@ static void SVM_ProcessUDPPacket(void)
|
|||
Info_Print(s, "\t");
|
||||
srv->protover = 3;//atoi(Info_ValueForKey(s, "protocol"));
|
||||
srv->maxclients = atoi(Info_ValueForKey(s, "maxclients"));
|
||||
srv->secure = !!*Info_ValueForKey(s, "*fp");
|
||||
srv->needpass = atoi(Info_ValueForKey(s, "needpass"));
|
||||
srv->coop = atoi(Info_ValueForKey(s, "coop"));
|
||||
if (!srv->coop)
|
||||
|
@ -1500,6 +1548,14 @@ static void SVM_ProcessUDPPacket(void)
|
|||
//this isn't actually useful. we can't use it because we can't protect against spoofed denial-of-service attacks.
|
||||
//we could only use this by sending it a few pings to see if it is actually still responding. which is unreliable (especially if we're getting spammed by packet floods).
|
||||
}
|
||||
else if (!strcmp(com_token, "ice_answer"))
|
||||
{ //one of our ws clients sent an ice offer over udp. this is the reply... hopefully.
|
||||
FTENET_TCP_ICEResponse(svm_sockets, ICEMSG_OFFER, s, MSG_ReadString());
|
||||
}
|
||||
else if (!strcmp(com_token, "ice_scand"))
|
||||
{ //a send or ack...
|
||||
FTENET_TCP_ICEResponse(svm_sockets, ICEMSG_CANDIDATE, s, MSG_ReadString());
|
||||
}
|
||||
else
|
||||
svm.total.junk++;
|
||||
}
|
||||
|
@ -1577,14 +1633,123 @@ float SVM_RequerySlaves(void)
|
|||
return 4; //nothing happening.
|
||||
}
|
||||
|
||||
float SVM_Think(int port)
|
||||
|
||||
static void SVM_RegisterAlias(svm_game_t *game, char *aliasname)
|
||||
{
|
||||
const char *a;
|
||||
size_t l;
|
||||
svm_game_t *aliasgame;
|
||||
if (!game)
|
||||
return;
|
||||
|
||||
//make sure we never have dupes. they confuse EVERYTHING.
|
||||
aliasgame = SVM_FindGame(aliasname, false);
|
||||
if (aliasgame == game)
|
||||
return; //already in there somehow.
|
||||
if (aliasgame)
|
||||
{
|
||||
Con_Printf("game alias of %s is already registered\n", aliasname);
|
||||
return;
|
||||
}
|
||||
game->persistent = true; //don't forget us!
|
||||
|
||||
if (!*aliasname)
|
||||
return;
|
||||
|
||||
a = game->aliases;
|
||||
if (a) for (; *a; a+=strlen(a)+1);
|
||||
l = a-game->aliases;
|
||||
game->aliases = BZ_Realloc(game->aliases, l+strlen(aliasname)+2);
|
||||
memcpy(game->aliases+l, aliasname, strlen(aliasname)+1);
|
||||
l += strlen(aliasname)+1;
|
||||
game->aliases[l] = 0;
|
||||
}
|
||||
static void SVM_GameAlias_f(void)
|
||||
{
|
||||
svm_game_t *game = SVM_FindGame(Cmd_Argv(1), 2);
|
||||
if (!game)
|
||||
{
|
||||
Con_Printf("Unable to register game %s\n", Cmd_Argv(1));
|
||||
return;
|
||||
}
|
||||
SVM_RegisterAlias(game, Cmd_Argv(2));
|
||||
}
|
||||
static void SVM_Register(void)
|
||||
{
|
||||
size_t u;
|
||||
|
||||
svm_sockets = FTENET_CreateCollection(true, SVM_ProcessUDPPacket);
|
||||
Hash_InitTable(&svm.serverhash, 1024, Z_Malloc(Hash_BytesForBuckets(1024)));
|
||||
|
||||
Cmd_AddCommand ("gamealias", SVM_GameAlias_f);
|
||||
|
||||
Cvar_Register(&sv_masterport, "server control variables");
|
||||
Cvar_Register(&sv_masterport_tcp, "server control variables");
|
||||
Cvar_Register(&sv_heartbeattimeout, "server control variables");
|
||||
Cvar_Register(&sv_maxgames, "server control variables");
|
||||
Cvar_Register(&sv_maxservers, "server control variables");
|
||||
Cvar_Register(&sv_hideinactivegames, "server control variables");
|
||||
Cvar_Register(&sv_sortlist, "server control variables");
|
||||
Cvar_Register(&sv_hostname, "server control variables");
|
||||
Cvar_Register(&sv_slaverequery, "server control variables");
|
||||
for (u = 0; u < countof(sv_masterslave); u++)
|
||||
Cvar_Register(&sv_masterslave[u].var, "server control variables");
|
||||
}
|
||||
static qboolean SVM_FoundManifest(void *usr, ftemanifest_t *man)
|
||||
{
|
||||
svm_game_t *game;
|
||||
const char *g;
|
||||
if (man->protocolname)
|
||||
{ //FIXME: we ought to do this for each manifest we could find.
|
||||
g = man->protocolname;
|
||||
|
||||
#if 1
|
||||
game = SVM_FindGame(man->formalname, 2);
|
||||
#else
|
||||
g = COM_Parse(g);
|
||||
game = SVM_FindGame(com_token, 2);
|
||||
#endif
|
||||
if (!game)
|
||||
return false;
|
||||
if (man->schemes && !game->scheme)
|
||||
{
|
||||
COM_Parse(man->schemes);
|
||||
game->scheme = Z_StrDup(com_token);
|
||||
}
|
||||
while (*g)
|
||||
{
|
||||
g = COM_Parse(g);
|
||||
SVM_RegisterAlias(game, com_token);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
static void SVM_Begin(void)
|
||||
{ //called once filesystem etc stuff is started.
|
||||
SVM_FoundManifest(NULL, fs_manifest);
|
||||
FS_EnumerateKnownGames(SVM_FoundManifest, NULL);
|
||||
|
||||
Cvar_ForceCallback(&sv_masterport);
|
||||
Cvar_ForceCallback(&sv_masterport_tcp);
|
||||
}
|
||||
|
||||
float SVM_Think(void)
|
||||
{
|
||||
#ifndef MASTERONLY
|
||||
if (!svm_sockets)
|
||||
{
|
||||
SVM_Register();
|
||||
SVM_Begin();
|
||||
}
|
||||
#endif
|
||||
|
||||
NET_ReadPackets (svm_sockets);
|
||||
SVM_RemoveOldServers();
|
||||
return SVM_RequerySlaves();
|
||||
}
|
||||
#else
|
||||
float SVM_Think(int port){return 4;}
|
||||
float SVM_Think(void){return 4;}
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -1631,81 +1796,9 @@ static void SVM_Status_f(void)
|
|||
|
||||
}
|
||||
|
||||
static void SVM_RegisterAlias(svm_game_t *game, char *aliasname)
|
||||
{
|
||||
const char *a;
|
||||
size_t l;
|
||||
svm_game_t *aliasgame;
|
||||
if (!game)
|
||||
return;
|
||||
|
||||
//make sure we never have dupes. they confuse EVERYTHING.
|
||||
aliasgame = SVM_FindGame(aliasname, false);
|
||||
if (aliasgame == game)
|
||||
return; //already in there somehow.
|
||||
if (aliasgame)
|
||||
{
|
||||
Con_Printf("game alias of %s is already registered\n", aliasname);
|
||||
return;
|
||||
}
|
||||
game->persistent = true; //don't forget us!
|
||||
|
||||
if (!*aliasname)
|
||||
return;
|
||||
|
||||
a = game->aliases;
|
||||
if (a) for (; *a; a+=strlen(a)+1);
|
||||
l = a-game->aliases;
|
||||
game->aliases = BZ_Realloc(game->aliases, l+strlen(aliasname)+2);
|
||||
memcpy(game->aliases+l, aliasname, strlen(aliasname)+1);
|
||||
l += strlen(aliasname)+1;
|
||||
game->aliases[l] = 0;
|
||||
}
|
||||
static qboolean SVM_FoundManifest(void *usr, ftemanifest_t *man)
|
||||
{
|
||||
svm_game_t *game;
|
||||
const char *g;
|
||||
if (man->protocolname)
|
||||
{ //FIXME: we ought to do this for each manifest we could find.
|
||||
g = man->protocolname;
|
||||
|
||||
#if 1
|
||||
game = SVM_FindGame(man->formalname, 2);
|
||||
#else
|
||||
g = COM_Parse(g);
|
||||
game = SVM_FindGame(com_token, 2);
|
||||
#endif
|
||||
if (!game)
|
||||
return false;
|
||||
if (man->schemes && !game->scheme)
|
||||
{
|
||||
COM_Parse(man->schemes);
|
||||
game->scheme = Z_StrDup(com_token);
|
||||
}
|
||||
while (*g)
|
||||
{
|
||||
g = COM_Parse(g);
|
||||
SVM_RegisterAlias(game, com_token);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void SVM_GameAlias_f(void)
|
||||
{
|
||||
svm_game_t *game = SVM_FindGame(Cmd_Argv(1), 2);
|
||||
if (!game)
|
||||
{
|
||||
Con_Printf("Unable to register game %s\n", Cmd_Argv(1));
|
||||
return;
|
||||
}
|
||||
SVM_RegisterAlias(game, Cmd_Argv(2));
|
||||
}
|
||||
void SV_Init (struct quakeparms_s *parms)
|
||||
{
|
||||
int manarg;
|
||||
size_t u;
|
||||
|
||||
COM_InitArgv (parms->argc, parms->argv);
|
||||
|
||||
|
@ -1731,22 +1824,8 @@ void SV_Init (struct quakeparms_s *parms)
|
|||
|
||||
Cmd_AddCommand ("quit", SV_Quit_f);
|
||||
Cmd_AddCommand ("status", SVM_Status_f);
|
||||
Cmd_AddCommand ("gamealias", SVM_GameAlias_f);
|
||||
|
||||
svm_sockets = FTENET_CreateCollection(true, SVM_ProcessUDPPacket);
|
||||
Hash_InitTable(&svm.serverhash, 1024, Z_Malloc(Hash_BytesForBuckets(1024)));
|
||||
|
||||
Cvar_Register(&sv_masterport, "server control variables");
|
||||
Cvar_Register(&sv_masterport_tcp, "server control variables");
|
||||
Cvar_Register(&sv_heartbeattimeout, "server control variables");
|
||||
Cvar_Register(&sv_maxgames, "server control variables");
|
||||
Cvar_Register(&sv_maxservers, "server control variables");
|
||||
Cvar_Register(&sv_hideinactivegames, "server control variables");
|
||||
Cvar_Register(&sv_sortlist, "server control variables");
|
||||
Cvar_Register(&sv_hostname, "server control variables");
|
||||
Cvar_Register(&sv_slaverequery, "server control variables");
|
||||
for (u = 0; u < countof(sv_masterslave); u++)
|
||||
Cvar_Register(&sv_masterslave[u].var, "server control variables");
|
||||
SVM_Register();
|
||||
|
||||
Cvar_ParseWatches();
|
||||
host_initialized = true;
|
||||
|
@ -1760,11 +1839,7 @@ void SV_Init (struct quakeparms_s *parms)
|
|||
Cmd_StuffCmds();
|
||||
Cbuf_Execute ();
|
||||
|
||||
Cvar_ForceCallback(&sv_masterport);
|
||||
Cvar_ForceCallback(&sv_masterport_tcp);
|
||||
|
||||
SVM_FoundManifest(NULL, fs_manifest);
|
||||
FS_EnumerateKnownGames(SVM_FoundManifest, NULL);
|
||||
SVM_Begin();
|
||||
|
||||
Con_Printf ("Exe: %s\n", version_string());
|
||||
|
||||
|
@ -1785,7 +1860,7 @@ float SV_Frame (void)
|
|||
}
|
||||
Cbuf_Execute ();
|
||||
|
||||
sleeptime = SVM_Think(sv_masterport.ival);
|
||||
sleeptime = SVM_Think();
|
||||
|
||||
//record lots of info over multiple frames, for smoother stats info.
|
||||
svm.total.timestamp = realtime;
|
||||
|
|
|
@ -468,7 +468,7 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
|
|||
{
|
||||
char tmp[32];
|
||||
Sys_RandomBytes(tmp, sizeof(tmp));
|
||||
tobase64(p->challenge, sizeof(p->challenge), tmp, sizeof(tmp));
|
||||
Base64_EncodeBlock(tmp, sizeof(tmp), p->challenge, sizeof(p->challenge));
|
||||
}
|
||||
|
||||
e = va("QTVSV 1\n"
|
||||
|
|
|
@ -3499,8 +3499,7 @@ void SV_SendClientMessages (void)
|
|||
stepmsec = 13;
|
||||
cmd.msec = stepmsec;
|
||||
|
||||
if (sv_showpredloss.ival)
|
||||
Con_Printf("%s: forcing %g msecs (anti-hover)\n", c->name, cmd.msec);
|
||||
c->hoverms += cmd.msec;
|
||||
VectorCopy(c->lastcmd.angles, cmd.angles);
|
||||
cmd.buttons = c->lastcmd.buttons;
|
||||
SV_RunCmd (&cmd, true);
|
||||
|
|
|
@ -7240,6 +7240,12 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse)
|
|||
#ifdef NEWSPEEDCHEATPROT
|
||||
if (ucmd->msec && host_client->msecs > 500)
|
||||
host_client->msecs = 500;
|
||||
if (host_client->hoverms)
|
||||
{
|
||||
if (sv_showpredloss.ival)
|
||||
Con_Printf("%s: forcing %g msecs (anti-hover)\n", host_client->name, cmd.msec);
|
||||
host_client->hoverms = 0;
|
||||
}
|
||||
if (ucmd->msec > host_client->msecs)
|
||||
{ //they're over their timeslice allocation
|
||||
//if they're not taking the piss then be prepared to truncate the frame. this should hide clockskew without allowing full-on speedcheats.
|
||||
|
|
|
@ -794,6 +794,8 @@ mergeInto(LibraryManager.library,
|
|||
return -1;
|
||||
if (s.con == 0)
|
||||
return 0; //not connected yet
|
||||
if (s.err != 0)
|
||||
return -1;
|
||||
if (len == 0)
|
||||
return 0; //...
|
||||
s.ws.send(HEAPU8.subarray(data, data+len));
|
||||
|
@ -888,6 +890,29 @@ mergeInto(LibraryManager.library,
|
|||
s.recvchan.binaryType = 'arraybuffer';
|
||||
s.recvchan.onmessage = s.ws.onmessage;
|
||||
};
|
||||
s.pc.onconnectionstatechange = function(e)
|
||||
{
|
||||
//console.log(s.pc.connectionState);
|
||||
//console.log(e);
|
||||
switch (s.pc.connectionState)
|
||||
{
|
||||
//case "new":
|
||||
//case "checking":
|
||||
//case "connected":
|
||||
case "disconnected":
|
||||
s.err = 1;
|
||||
break;
|
||||
case "closed":
|
||||
s.con = 0;
|
||||
s.err = 1;
|
||||
break;
|
||||
case "failed":
|
||||
s.err = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if (clientside)
|
||||
{
|
||||
|
|
|
@ -49,12 +49,50 @@ qboolean Sys_RandomBytes(qbyte *string, int len)
|
|||
void Sys_Printf (char *fmt, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char buf[1024];
|
||||
char text[2048];
|
||||
conchar_t ctext[2048], *e, *c;
|
||||
unsigned int len = 0;
|
||||
unsigned int w, codeflags;
|
||||
|
||||
va_start (argptr,fmt);
|
||||
vsnprintf (buf, sizeof(buf), fmt, argptr);
|
||||
emscriptenfte_print(buf);
|
||||
vsnprintf (text, sizeof(text), fmt, argptr);
|
||||
va_end (argptr);
|
||||
|
||||
//make sense of any markup
|
||||
e = COM_ParseFunString(CON_WHITEMASK, text, ctext, sizeof(ctext), false);
|
||||
|
||||
//convert to utf-8 for the js to make sense of
|
||||
for (c = ctext; c < e; )
|
||||
{
|
||||
c = Font_Decode(c, &codeflags, &w);
|
||||
if (codeflags & CON_HIDDEN)
|
||||
continue;
|
||||
|
||||
//dequake it as required, so its only codepoints the browser will understand. should probably deal with linefeeds specially.
|
||||
if (w >= 0xe000 && w < 0xe100)
|
||||
{ //quake-encoded mess
|
||||
if ((w & 0x7f) >= 0x20)
|
||||
w &= 0x7f; //regular (discoloured) ascii
|
||||
else if (w & 0x80)
|
||||
{ //c1 glyphs
|
||||
static char tab[32] = "---#@.@@@@ # >.." "[]0123456789.---";
|
||||
w = tab[w&31];
|
||||
}
|
||||
else
|
||||
{ //c0 glyphs
|
||||
static char tab[32] = ".####.#### # >.." "[]0123456789.---";
|
||||
w = tab[w&31];
|
||||
}
|
||||
}
|
||||
else if (w < ' ' && w != '\t' && w != '\r' && w != '\n')
|
||||
w = '?'; //c0 chars are awkward
|
||||
|
||||
len += utf8_encode(text+len, w, sizeof(text)-1-len);
|
||||
}
|
||||
text[len] = 0;
|
||||
|
||||
//now throw it at the browser's console.log.
|
||||
emscriptenfte_print(text);
|
||||
}
|
||||
|
||||
#if 1
|
||||
|
|
Loading…
Reference in a new issue