A clean minor-version bump (retiring the Epic 2 -bangN patched-fork suffix —
that scheme is superseded; we now own these forks outright and version them
normally). GitHub Packages won't let us re-publish 1.19, and 1.20 makes the
TLS-capable build obvious in dependency listings.
When the cluster's client port is TLS-enabled, peer connections (which connect to
that same port) must also speak TLS. PeerManager gains setPeerClientContext/
getPeerClientContext, and PeerNode installs that context on its client before
logon. Null (the default) leaves peering plaintext — a no-op, so non-TLS clusters
are unchanged.
Wraps the entire client<->server TCP object stream in TLS, strictly opt-in via
an injected SSLContext (null = byte-for-byte the original plaintext behavior).
Connection: a ByteChannel _ioChannel indirection (defaults to the raw socket;
a TLS-wrapping channel when enabled). Selector registration, connection-pending
checks and close still use the raw SocketChannel.
Server (PresentsConnectionManager/PresentsConnection/ConnectionManager):
setSSLContext/isTlsEnabled; accepted sockets are wrapped in a non-blocking
ServerTlsChannel after init+registration; reads/writes route through the io
channel. tls-channel NeedsWrite/NeedsRead on write are treated as partial writes
(stashed in the existing per-connection overflow queue, retried each timed-select
tick — the server never needed OP_WRITE); on read, NeedsRead stays OP_READ and
NeedsWrite adds OP_WRITE so the re-dispatch pumps the handshake flush, cleared
after a clean read. processAuthedConnections carries the established io channel
from the authing connection to the running connection (init() would otherwise
revert it to the raw socket mid-session).
Client (Client/BlockingCommunicator): Client.setSSLContext; the connected
blocking socket is wrapped in a ClientTlsChannel (handshake driven transparently
in blocking mode); the Reader/Writer threads share it (one-unwrap + one-wrap, the
concurrency tls-channel allows).
DatagramChannelReader: fails closed — refuses to bind datagram ports while TLS is
enabled (we have no DTLS, so plaintext datagrams alongside TLS would be a footgun;
Bang binds zero datagram ports, so this never triggers today).
Adds the marianobarrios:tls-channel:1.0.0 dependency (thin ByteChannel-over-
SSLEngine wrapper, MIT, zero transitive deps, Java 8 bytecode) and a
TlsContextFactory that builds server (keystore) and client (pinned truststore)
SSLContexts, TLS 1.3 only. No transport wiring yet — this just compiles in the
building blocks for full-session encryption of the Presents NIO stream.
I can't help but over-optimize. We're parsing the result of class.getName(),
it will be a bare class name if not in the array.
Also: Stop going out of our way to not throw an exception if the input
is malformed, as that's the desired result for bad input.
Add three defenses to the Streamable deserialization system to prevent
exploitation by hacked clients:
- Class whitelist: ObjectInputStream.setAllowedClassPrefixes() validates
class names before Class.forName(), blocking arbitrary class loading
from crafted wire data. Callers configure at startup; unconfigured
streams retain existing behavior for backward compatibility.
- Container size caps: BasicStreamers.validateSize() rejects arrays,
collections, and maps with negative or >65536 element counts. Prevents
OOM from a small frame declaring a massive allocation. Applied to all
12 array/collection read paths and Streamer.ArrayStreamer.
- Recursion depth limit: ObjectInputStream.readObject() tracks nesting
depth and throws at 32 levels, preventing stack overflow from
maliciously nested structures (e.g., list-of-list-of-list).
Some networks will kill connections sooner than 60s, especially if they are not using keepalive. This fix should reduce the random disconnects some players experience
I don't think anything is directly streaming these over the wire, but it would
have worked before, so we'll try to keep it working now to avoid unpleasant
surprises.
- It would update if an AttributeChangedEvent was received, but not an
ElementUpdatedEvent!
- If you have 50 fields and 50 editors, then every one was updating
whenever an event arrived? Filter by the field that actually changed?
Does this break something?
A balance between: This is obnoxious, I should fix it in my subclass
and/or we should be compositing functionality, not subclassing... but
adding a time zone to this old thing seems fundamental anyway and this
is an easy fix.
This is used on multi-noded servers so that I can have a service
interface for talking to the node in charge of a particular resource,
and use the same code path even if that node is the current node.
Perhaps this be more smoothly integrated in the future but for now it's
a static utility you need to manually set up.