Skip to content

Commit 01756e7

Browse files
committed
Bluewing Server update to build 33
This is mostly bugfixes to build 32, but a couple improvements in there. Fixed incorrect client flag address being read - causing server to half-handshake and not respond to any clients for entirety of hosting, dependent on server's memory address... (Build 32 is considered unstable due to this) Fixed already-connected clients not being disconnected on unhost. Fixed wrong websocket server (secure vs not) being passed to relayserver's disconnect handler and connect handler. Fixed loading server cert files with one combined PFX being disallowed. Added lw_ws_cert_expiry_time() and lw_server_cert_expiry_time(). Due to PFX password functions using WCHAR only, made the lw_char_to_wchar available in non-Unicode Windows builds as well. We target XP+, so no real reason to support non-Unicode... may be removed in future when Fusion 3.0 is out, since this repo is tied to MMF2Exts repo. Added openssl headers from OpenSSL 1.1.1s. Unix build should use OS libraries anyway. No longer defines ENABLE_SSL by default. It made SSL libraries required even when not in use. Moved relayserver's websocket hosting and unhosting to their own functions so they can properly work with the regular socket server and unhost both websocket servers in one call. New functions are relayserver::host_websocket() and relayserver::unhost_websocket(). Fixed some elements not being removed from event queue (bug from liblacewing days). Moved port number types from long to int. Changed Windows system store cert loading (load_sys_cert) parameters from "store, location, common" to "common, location, store". Improved expiry time logging to console. Removed or debug-only some tracing from event queue. Some formatting clean-up.
1 parent 02fba0b commit 01756e7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+24718
-217
lines changed

Lacewing/Lacewing.h

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -487,10 +487,11 @@ typedef lw_i8 lw_bool;
487487
lw_import void lw_server_host_filter (lw_server, lw_filter);
488488
lw_import void lw_server_unhost (lw_server);
489489
lw_import lw_bool lw_server_hosting (lw_server);
490-
lw_import long lw_server_port (lw_server);
491-
lw_import lw_bool lw_server_load_cert_file (lw_server, const char * filename_certchain, const char* filename_privkey, const char * passphrase);
492-
lw_import lw_bool lw_server_load_sys_cert (lw_server, const char * store_name, const char * common_name, const char * location);
490+
lw_import int lw_server_port (lw_server);
491+
lw_import lw_bool lw_server_load_cert_file (lw_server, const char * filename_certchain, const char * filename_privkey, const char * passphrase);
492+
lw_import lw_bool lw_server_load_sys_cert (lw_server, const char * common_name, const char * location, const char * store_name);
493493
lw_import lw_bool lw_server_cert_loaded (lw_server);
494+
lw_import time_t lw_server_cert_expiry_time(lw_server);
494495
lw_import lw_bool lw_server_can_npn (lw_server);
495496
lw_import void lw_server_add_npn (lw_server, const char * protocol);
496497
lw_import const char * lw_server_client_npn (lw_server_client);
@@ -560,11 +561,12 @@ typedef lw_i8 lw_bool;
560561
lw_import void lw_ws_unhost_secure (lw_ws);
561562
lw_import lw_bool lw_ws_hosting (lw_ws);
562563
lw_import lw_bool lw_ws_hosting_secure (lw_ws);
563-
lw_import long lw_ws_port (lw_ws);
564-
lw_import long lw_ws_port_secure (lw_ws);
565-
lw_import lw_bool lw_ws_load_cert_file (lw_ws, const char * filename_certchain, const char* filename_privkey, const char * passphrase);
566-
lw_import lw_bool lw_ws_load_sys_cert (lw_ws, const char * store_name, const char * common_name, const char * location);
564+
lw_import int lw_ws_port (lw_ws);
565+
lw_import int lw_ws_port_secure (lw_ws);
566+
lw_import lw_bool lw_ws_load_cert_file (lw_ws, const char * filename_certchain, const char * filename_privkey, const char * passphrase);
567+
lw_import lw_bool lw_ws_load_sys_cert (lw_ws, const char * common_name, const char * location, const char * store_name);
567568
lw_import lw_bool lw_ws_cert_loaded (lw_ws);
569+
lw_import time_t lw_ws_cert_expiry_time (lw_ws);
568570
lw_import void lw_ws_session_close (lw_ws, const char * id);
569571
lw_import void lw_ws_enable_manual_finish (lw_ws);
570572
lw_import long lw_ws_idle_timeout (lw_ws);
@@ -726,7 +728,7 @@ std::string lw_u8str_simplify(const std::string_view first, bool destructive = t
726728
/// Both control and whitespace category will always be removed. </remarks>
727729
std::string_view lw_u8str_trim(std::string_view toTrim, bool abortOnTrimNeeded = false);
728730

729-
#if defined(_WIN32) && defined(_UNICODE)
731+
#if defined(_WIN32)
730732
// For Unicode support on Windows.
731733
// Returns null or a wide-converted version of the U8 string passed. Free it with free(). Pass size -1 for null-terminated strings.
732734
extern "C" lw_import wchar_t * lw_char_to_wchar(const char * u8str, int size);
@@ -1161,11 +1163,11 @@ struct _server
11611163
lw_import long port ();
11621164

11631165
lw_import bool load_cert_file
1164-
(const char * filename_certchain, const char* filename_privkey, const char * passphrase = "");
1166+
(const char * filename_certchain, const char * filename_privkey, const char * passphrase = "");
11651167

11661168
lw_import bool load_sys_cert
1167-
(const char * storename, const char * common_name,
1168-
const char * location = "CurrentUser");
1169+
(const char * common_name, const char * location = "CurrentUser",
1170+
const char * store_name = "My");
11691171

11701172
lw_import bool cert_loaded ();
11711173

@@ -1271,17 +1273,18 @@ struct _webserver
12711273
lw_import bool hosting ();
12721274
lw_import bool hosting_secure ();
12731275

1274-
lw_import long port ();
1275-
lw_import long port_secure ();
1276+
lw_import int port ();
1277+
lw_import int port_secure ();
12761278

12771279
lw_import bool load_cert_file
12781280
(const char * filename_certchain, const char* filename_privkey, const char * passphrase = "");
12791281

12801282
lw_import bool load_sys_cert
1281-
(const char * store_name, const char * common_name,
1282-
const char * location = "CurrentUser");
1283+
(const char* common_name, const char* location = "CurrentUser",
1284+
const char* store_name = "My");
12831285

12841286
lw_import bool cert_loaded ();
1287+
lw_import time_t cert_expiry_time ();
12851288

12861289
lw_import void enable_manual_finish ();
12871290

@@ -1921,7 +1924,7 @@ struct codepointsallowlist {
19211924
struct relayserverinternal;
19221925
struct relayserver
19231926
{
1924-
static const int buildnum = 32;
1927+
static const int buildnum = 33;
19251928

19261929
void * internaltag, * tag = nullptr;
19271930

@@ -1938,6 +1941,8 @@ struct relayserver
19381941
void host_websocket(lw_ui16 portNonSecure = 80, lw_ui16 portSecure = 443);
19391942
void host_websocket(lacewing::filter& filterNonSecure, lacewing::filter& filterSecure);
19401943
void unhost();
1944+
// This works with clients of regular server, so you should use this instead of websocket->unhost/unhost_secure
1945+
void unhost_websocket(bool insecure, bool secure);
19411946

19421947
bool hosting();
19431948
lw_ui16 port();
@@ -2095,7 +2100,7 @@ struct relayserver
20952100
// Can't use socket->address, as when server_client is free'd it is no longer valid
20962101
// Since there's a logical use for looking up address during closing, we'll keep a copy.
20972102
std::string address;
2098-
in6_addr addressInt = {0};
2103+
in6_addr addressInt = {};
20992104
// Time the Relay connection was approved - zero timepoint if not yet approved
21002105
::std::chrono::high_resolution_clock::time_point connectRequestApprovedTime;
21012106
::std::chrono::steady_clock::time_point lasttcpmessagetime;

Lacewing/RelayServer.cc

Lines changed: 110 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,13 @@ struct relayserverinternal
269269
if (std::find(clients.begin(), clients.end(), client) != clients.end())
270270
{
271271
serverReadLock.lw_unlock();
272-
//auto clientWriteLock = clientsocket->lock.createWriteLock();
272+
auto clientWriteLock = client->lock.createWriteLock();
273273
if (client->_readonly)
274274
continue;
275275
client->_readonly = true;
276276

277277
auto error = lacewing::error_new();
278-
error->add("Disconnecting client ID %i due to ping timeout", client->_id);
278+
error->add("Disconnecting client ID %hu due to ping timeout", client->_id);
279279
handlererror(this->server, error);
280280
lacewing::error_delete(error);
281281

@@ -314,7 +314,8 @@ struct relayserverinternal
314314
if (std::find(clients.begin(), clients.end(), client) != clients.end())
315315
{
316316
serverReadLock.lw_unlock();
317-
//auto clientWriteLock = clientsocket->lock.createWriteLock();
317+
318+
auto clientWriteLock = client->lock.createWriteLock();
318319
if (client->_readonly)
319320
continue;
320321

@@ -323,12 +324,14 @@ struct relayserverinternal
323324
impl.erase(impl.cend());
324325

325326
auto error = lacewing::error_new();
326-
error->add("Disconnecting client ID %i due to inactivity timeout; client impl \"%s\".", client->_id, impl.c_str());
327+
error->add("Disconnecting client ID %hu due to inactivity timeout; client impl \"%s\".", client->_id, impl.c_str());
327328
handlererror(this->server, error);
328329
lacewing::error_delete(error);
329330

331+
// Don't send warning to a client that hasn't even sent Lacewing handshake after connecting
330332
if (client->gotfirstbyte)
331333
client->send(0, "You're being kicked for inactivity.", 0);
334+
332335
if (client->socket->is_websocket())
333336
client->disconnect(1000);
334337
else // Close nicely - if client has not got first byte, e.g. non-Lacewing, close immediately
@@ -1178,18 +1181,18 @@ void handlerwebserverget(lacewing::webserver webserver, lacewing::webserver_requ
11781181
char sha1[20];
11791182
lw_sha1(sha1, webSocketKey.data(), webSocketKey.size());
11801183
const std::string webSocketKeyResponse = b64encode(sha1, sizeof(sha1));
1181-
1182-
auto c = ((struct _lw_ws_req*)req)->client;
1183-
c->websocket = lw_true;
1184-
c->ws->timeout = 0; // disable timeout - next used when server inits a disconnect and is waiting for WebSocket close packet back
1184+
1185+
lwp_ws_client reqClient = ((struct _lw_ws_req*)req)->client;
1186+
reqClient->websocket = lw_true;
1187+
reqClient->ws->timeout = 0; // disable timeout - next used when server inits a disconnect and is waiting for WebSocket close packet back
11851188

11861189
lw_server server;
1187-
if (c->secure)
1188-
server = ((lw_ws)webserver)->socket;
1189-
else
1190+
if (reqClient->secure)
11901191
server = ((lw_ws)webserver)->socket_secure;
1191-
lw_server_client_set_websocket(c->socket, lw_true);
1192-
internal.generic_handlerconnect((lacewing::server)server, (lacewing::server_client)c->socket);
1192+
else
1193+
server = ((lw_ws)webserver)->socket;
1194+
lw_server_client_set_websocket(reqClient->socket, lw_true);
1195+
internal.generic_handlerconnect((lacewing::server)server, (lacewing::server_client)reqClient->socket);
11931196

11941197
req->header("Upgrade", "WebSocket");
11951198
req->header("Connection", "Upgrade");
@@ -1268,9 +1271,9 @@ void handlerwebserverdisconnect(lacewing::webserver webserver, lacewing::webserv
12681271
return; // not websocket - just some dumb client, don't pass to relayserver
12691272
lw_server server;
12701273
if (client->secure)
1271-
server = ((lw_ws)webserver)->socket;
1272-
else
12731274
server = ((lw_ws)webserver)->socket_secure;
1275+
else
1276+
server = ((lw_ws)webserver)->socket;
12741277
internal.generic_handlerdisconnect((lacewing::server)server, (lacewing::server_client)client->socket);
12751278
}
12761279

@@ -1394,6 +1397,9 @@ void relayserver::host_websocket(lw_ui16 portNonSecure, lw_ui16 portSecure)
13941397
websocket->host_secure(portSecure);
13951398
assert(websocket->hosting_secure());
13961399
}
1400+
1401+
relayserverinternal* serverInternal = (relayserverinternal*)internaltag;
1402+
serverInternal->pingtimer->start(serverInternal->tcpPingMS);
13971403
}
13981404
void relayserver::host_websocket(lacewing::filter& filterNonSecure, lacewing::filter& filterSecure)
13991405
{
@@ -1410,44 +1416,99 @@ void relayserver::host_websocket(lacewing::filter& filterNonSecure, lacewing::fi
14101416
websocket->host_secure(filterSecure);
14111417
assert(websocket->hosting_secure());
14121418
}
1419+
1420+
relayserverinternal* serverInternal = (relayserverinternal*)internaltag;
1421+
serverInternal->pingtimer->start(serverInternal->tcpPingMS);
14131422
}
14141423

14151424
void relayserver::unhost()
14161425
{
1417-
socket->unhost();
1418-
udp->unhost();
1419-
websocket->unhost();
1420-
websocket->unhost_secure();
1426+
// websocket and flash are unhosted explicitly only, as they're hosted explicitly
1427+
// flash only points to regular server, so it has no client list itself
14211428

14221429
relayserverinternal* serverInternal = (relayserverinternal*)internaltag;
1423-
serverInternal->pingtimer->stop();
1430+
const bool isWebSocketActive = websocket->hosting() || websocket->hosting_secure();
1431+
if (!isWebSocketActive)
1432+
serverInternal->pingtimer->stop();
14241433

14251434
// This will drop all clients, by doing so drop all channels
14261435
// and both of those will free the IDs
1427-
// We'll set them all as readonly so peer leave messages aren't sent as the clients leave their channels
1428-
for (auto &c : serverInternal->clients)
1429-
c->_readonly = true; // unhost() has already made clients inaccessible
1436+
// We'll set the leavers all as readonly before closing channels, so peer leave messages aren't sent to them
1437+
// as the clients leave their channels
1438+
for (auto& c : serverInternal->clients)
1439+
{
1440+
if (!c->socket->is_websocket())
1441+
c->_readonly = true; // unhost() has already made clients inaccessible
1442+
}
14301443

14311444
// Prevent the channel_close handler from being run
1432-
const auto handler = serverInternal->handlerchannel_close;
1433-
serverInternal->handlerchannel_close = nullptr;
1445+
// const auto handler = serverInternal->handlerchannel_close;
1446+
// serverInternal->handlerchannel_close = nullptr;
1447+
1448+
// disconnect handlers check server that ran them is still hosting
1449+
socket->unhost();
1450+
udp->unhost();
1451+
1452+
// Reinstate for next host() call
1453+
// serverInternal->handlerchannel_close = handler;
1454+
}
1455+
void relayserver::unhost_websocket(bool insecure, bool secure)
1456+
{
1457+
// Turn off parameters for servers that aren't hosting
1458+
insecure &= websocket->hosting();
1459+
secure &= websocket->hosting_secure();
1460+
1461+
if (!insecure && !secure)
1462+
return;
14341463

1435-
while (!serverInternal->clients.empty())
1464+
// disconnect handlers check server that ran them is still hosting
1465+
relayserverinternal* serverInternal = (relayserverinternal*)internaltag;
1466+
// If we've got a different server up (and we're not about to unhost it here), then keep ping timer running
1467+
const bool isOtherHosting = socket->hosting() || (!insecure && websocket->hosting()) || (!secure && websocket->hosting_secure());
1468+
if (!isOtherHosting)
1469+
serverInternal->pingtimer->stop();
1470+
1471+
// We'll set the leavers all as readonly before closing channels, so peer leave messages aren't sent to leavers
1472+
// as the clients leave their channels
1473+
// (they're still sent to clients on still-hosting servers, obviously)
1474+
if (insecure)
14361475
{
1437-
auto& c = serverInternal->clients.back();
1438-
serverInternal->close_client(c);
1476+
for (auto c = lw_server_client_first(((lw_ws)websocket)->socket); c; c = lw_server_client_next(c))
1477+
{
1478+
relayserver::client* client = (relayserver::client*)lw_server_client_get_relay_tag(c);
1479+
if (client)
1480+
client->_readonly = true;
1481+
}
14391482
}
1440-
1441-
// any with autoclose on
1442-
while (!serverInternal->channels.empty())
1483+
if (secure)
14431484
{
1444-
auto& c = serverInternal->channels.back();
1445-
c->_readonly = true;
1446-
serverInternal->close_channel(c);
1485+
for (auto c = lw_server_client_first(((lw_ws)websocket)->socket_secure); c; c = lw_server_client_next(c))
1486+
{
1487+
relayserver::client* client = (relayserver::client*)lw_server_client_get_relay_tag(c);
1488+
if (client)
1489+
client->_readonly = true;
1490+
}
14471491
}
14481492

1449-
// Reinstate for next host() call
1450-
serverInternal->handlerchannel_close = handler;
1493+
// Prevent the channel_close handler from being run
1494+
// const auto handler = server->handlerchannel_close;
1495+
// serverInternal->handlerchannel_close = nullptr;
1496+
1497+
// This will drop clients, by doing so drop all channels and both of those will free the IDs
1498+
//
1499+
// The lower-level handler (lacewing::handlerdisconnect) will be triggered, but will not call the RelayServer disconnect handler,
1500+
// as that will check server is hosting before calling it
1501+
// note: unhost calls handlerdisconnect, which:
1502+
// expects client still in server list
1503+
// calls close_client
1504+
// resets relay tag to null
1505+
if (insecure)
1506+
websocket->unhost();
1507+
if (secure)
1508+
websocket->unhost_secure();
1509+
1510+
// Resume close handler
1511+
// serverInternal->handlerchannel_close = handler;
14511512
}
14521513

14531514
bool relayserver::hosting()
@@ -2438,7 +2499,7 @@ bool relayserverinternal::client_messagehandler(std::shared_ptr<relayserver::cli
24382499
case 10: /* implementation response */
24392500
{
24402501
const std::string_view impl = reader.get(reader.bytesleft());
2441-
if (reader.failed || impl.empty())
2502+
if (reader.failed || impl.empty() || !lw_u8str_validate(impl))
24422503
{
24432504
errStr << "Failed to read implementation response"sv;
24442505
trustedClient = false;
@@ -2462,14 +2523,17 @@ bool relayserverinternal::client_messagehandler(std::shared_ptr<relayserver::cli
24622523
}
24632524
else if (impl.find("Android"sv) != std::string_view::npos)
24642525
client->clientImpl = relayserver::client::clientimpl::Android;
2465-
else if (impl.find("Flash"sv) != std::string_view::npos)
2466-
client->clientImpl = relayserver::client::clientimpl::Flash;
24672526
else if (impl.find("iOS"sv) != std::string_view::npos)
24682527
client->clientImpl = relayserver::client::clientimpl::iOS;
2469-
else if (impl.find("Macintosh"sv) != std::string_view::npos)
2470-
client->clientImpl = relayserver::client::clientimpl::Macintosh;
2528+
// First test in client build 99, first release as build 100
24712529
else if (impl.find("HTML5"sv) != std::string_view::npos)
24722530
client->clientImpl = relayserver::client::clientimpl::HTML5;
2531+
// While supported, Blue Flash never existed, and Relay Flash won't return a implementation response
2532+
else if (impl.find("Flash"sv) != std::string_view::npos)
2533+
client->clientImpl = relayserver::client::clientimpl::Flash;
2534+
// While supported, not created yet
2535+
else if (impl.find("Macintosh"sv) != std::string_view::npos)
2536+
client->clientImpl = relayserver::client::clientimpl::Macintosh;
24732537
else
24742538
{
24752539
errStr << "Failed to recognise platform of implementation \""sv << impl << "\". Leaving it as Unknown."sv;
@@ -3004,9 +3068,11 @@ void relayserver::connect_response(
30043068

30053069
framebuilder builder(true);
30063070

3007-
// Force a connect refusal if not hosting serevr
3071+
// Force a connect refusal if not hosting server
3072+
// TODO: Is this necessary? Client should've been d/c'd on unhost
3073+
// If it is necessary, the HTML5 server hosting check is borked, client could be on the other server
30083074
std::string_view denyReason = passedDenyReason;
3009-
if (denyReason.empty() && !hosting())
3075+
if (denyReason.empty() && (client->socket->is_websocket() ? !websocket->hosting() && !websocket->hosting_secure() : !hosting()))
30103076
denyReason = "Server has shut down."sv;
30113077

30123078
// Connect request denied
@@ -3026,7 +3092,7 @@ void relayserver::connect_response(
30263092

30273093
// Connect request accepted
30283094

3029-
lw_trace("Connect request accepted in relayserver::connectresponse");
3095+
lwp_trace("Connect request accepted in relayserver::connectresponse");
30303096
client->connectRequestApproved = true;
30313097
client->connectRequestApprovedTime = decltype(client->connectRequestApprovedTime)::clock::now();
30323098
client->clientImpl = relayserver::client::clientimpl::Unknown;

0 commit comments

Comments
 (0)