Add the browser version, a toolbar, description previews and hints
Browser version - Self-hosted proxy (server/) that serves the web build and bridges WebSocket connections to Mumble servers over TLS, with a server allowlist or allow-any mode - WebSocket transport and a browser platform layer; identities live in the browser - npm run build:web, dev:web, proxy and test:e2e:web; proxy unit tests Interface - Toolbar next to mute and deafen: description editor, settings, expand or collapse all - Channels with a description show a marker; resting on the row previews it - Collapse all keeps channels with people open - Hints (title tooltips) can be switched on in a new Accessibility settings tab - New identities can set the username suggested when connecting - Own user information tells the client address from the one the server sees Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import tls from 'node:tls';
|
||||
import net from 'node:net';
|
||||
import { describeCert, type CertDetails } from './certs.ts';
|
||||
|
||||
export interface TlsHandlers {
|
||||
@@ -9,13 +10,37 @@ export interface TlsHandlers {
|
||||
|
||||
// Opens a TLS connection to a Mumble server. Most servers use self-signed certificates,
|
||||
// so verification is left to the caller (trust on first use via the sha256 fingerprint).
|
||||
export function openTls(host: string, port: number, cert: string, key: string, h: TlsHandlers) {
|
||||
export interface TlsOptions {
|
||||
// Custom DNS resolution (the web proxy uses it to refuse private addresses)
|
||||
lookup?: net.LookupFunction;
|
||||
// Announce this client address with a PROXY protocol v1 line before TLS starts, for a
|
||||
// receiver such as go-mmproxy that passes it on to a server without PROXY support
|
||||
proxyClient?: string;
|
||||
}
|
||||
|
||||
// PROXY protocol v1 line. Source and destination must be the same family; an IPv4 client is
|
||||
// written as a mapped address towards an IPv6 destination, anything else is announced as unknown.
|
||||
export function proxyLine(client: string, clientPort: number, dest: string, destPort: number): string {
|
||||
const d = dest.replace(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i, '$1');
|
||||
const c = client.replace(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i, '$1');
|
||||
if (net.isIPv4(c) && net.isIPv4(d)) return `PROXY TCP4 ${c} ${d} ${clientPort} ${destPort}\r\n`;
|
||||
if (net.isIPv6(d) && net.isIP(c)) return `PROXY TCP6 ${net.isIPv4(c) ? `::ffff:${c}` : c} ${d} ${clientPort} ${destPort}\r\n`;
|
||||
return 'PROXY UNKNOWN\r\n';
|
||||
}
|
||||
|
||||
export function openTls(host: string, port: number, cert: string, key: string, h: TlsHandlers, opts: TlsOptions = {}) {
|
||||
let closeReason = 'Connection closed';
|
||||
const socket = tls.connect({
|
||||
host, port, cert, key,
|
||||
rejectUnauthorized: false,
|
||||
servername: /^[\d.:]+$/.test(host) ? undefined : host
|
||||
});
|
||||
const servername = /^[\d.:]+$/.test(host) ? undefined : host;
|
||||
let socket: tls.TLSSocket;
|
||||
if (opts.proxyClient) {
|
||||
// The PROXY line goes first, in the clear; TLS then runs over the same connection
|
||||
const raw = net.connect({ host, port, lookup: opts.lookup });
|
||||
raw.once('connect', () => raw.write(proxyLine(opts.proxyClient!, raw.localPort ?? 0, raw.remoteAddress ?? '', raw.remotePort ?? port)));
|
||||
raw.on('error', (e: Error) => { closeReason = e.message; socket.destroy(); });
|
||||
socket = tls.connect({ socket: raw, cert, key, rejectUnauthorized: false, servername });
|
||||
} else {
|
||||
socket = tls.connect({ host, port, cert, key, lookup: opts.lookup, rejectUnauthorized: false, servername });
|
||||
}
|
||||
socket.setNoDelay(true);
|
||||
socket.setKeepAlive(true, 30000);
|
||||
socket.setTimeout(20000, () => {
|
||||
|
||||
Reference in New Issue
Block a user