Branch: ircv3.2-upgrade @ 3868b34 (also present on MrLenin/nefarious2:ircv3.2-hardening as far as I can tell — same code path).
Summary
On a non-TLS listener with websocket = yes;, the ident/DNS progress notices are written to the socket as bare IRC lines before the HTTP/1.1 101 Switching Protocols response. Any HTTP client (Node's parser, every browser) rejects the upgrade, so plain-text WebSocket is unusable. The same listener with ssl = yes; works.
Reproduction
ircd.conf:
Port {
port = 8067;
websocket = yes;
};
Then:
printf 'GET / HTTP/1.1\r\nHost: localhost:8067\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Protocol: text.ircv3.net\r\n\r\n' | nc -q 3 127.0.0.1 8067 | cat -v
Output:
NOTICE * :*** Looking up your hostname^M
NOTICE * :*** Checking Ident^M
NOTICE * :*** No ident response^M
HTTP/1.1 101 Switching Protocols^M
Upgrade: websocket^M
Connection: Upgrade^M
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=^M
Sec-WebSocket-Protocol: text.ircv3.net^M
^M
M-^A;ERROR :Closing Link: * by irc.seance.test (EOF from client)
Node 22 (ws or the global WebSocket) fails the connection with Parse Error: Expected HTTP/, RTSP/ or ICE/ and close code 1006. Same request against wss:// on an ssl = yes; websocket = yes; port succeeds and registers normally.
Cause
send_queued() in ircd/s_bsd.c:318-325 correctly holds all MsgQ output while IsWSNeedHandshake(cptr) / IsWSSniff(cptr) is set. But the auth notices don't go through the MsgQ: sendheader() in ircd/s_auth.c:164-169 is
#ifdef USE_SSL
#define sendheader(c, r) \
ssl_send(c, HeaderMessages[(r)].message, HeaderMessages[(r)].length)
#else
#define sendheader(c, r) \
send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
#endif
i.e. a direct write to the fd, so it lands on the wire before websocket_handshake() has replied. On TLS ports it happens to come out in the right order (I assume because ssl_send can't write until the TLS handshake completes, by which point the 101 has been queued) — I have not verified that part.
Suggested fix
Either route sendheader() through the normal send path so the IsWSNeedHandshake hold applies (and the lines get framed once the socket is a WebSocket), or skip/defer the notices for clients that still have FLAG_WS_NEEDHANDSHAKE / FLAG_WS_SNIFF set. The autodetect mode has the same exposure: the sniff can't complete before the notices are written.
Context
Found while bringing up a browser IRCv3 client (Seance, a TheLounge fork) that talks to nefarious2 directly over WebSocket. Happy to test a fix against the same setup — it's a docker build of the branch with the port above added.
Branch:
ircv3.2-upgrade@3868b34(also present onMrLenin/nefarious2:ircv3.2-hardeningas far as I can tell — same code path).Summary
On a non-TLS listener with
websocket = yes;, the ident/DNS progress notices are written to the socket as bare IRC lines before theHTTP/1.1 101 Switching Protocolsresponse. Any HTTP client (Node's parser, every browser) rejects the upgrade, so plain-text WebSocket is unusable. The same listener withssl = yes;works.Reproduction
ircd.conf:Then:
Output:
Node 22 (
wsor the globalWebSocket) fails the connection withParse Error: Expected HTTP/, RTSP/ or ICE/and close code 1006. Same request againstwss://on anssl = yes; websocket = yes;port succeeds and registers normally.Cause
send_queued()inircd/s_bsd.c:318-325correctly holds all MsgQ output whileIsWSNeedHandshake(cptr)/IsWSSniff(cptr)is set. But the auth notices don't go through the MsgQ:sendheader()inircd/s_auth.c:164-169isi.e. a direct write to the fd, so it lands on the wire before
websocket_handshake()has replied. On TLS ports it happens to come out in the right order (I assume becausessl_sendcan't write until the TLS handshake completes, by which point the 101 has been queued) — I have not verified that part.Suggested fix
Either route
sendheader()through the normal send path so theIsWSNeedHandshakehold applies (and the lines get framed once the socket is a WebSocket), or skip/defer the notices for clients that still haveFLAG_WS_NEEDHANDSHAKE/FLAG_WS_SNIFFset. Theautodetectmode has the same exposure: the sniff can't complete before the notices are written.Context
Found while bringing up a browser IRCv3 client (Seance, a TheLounge fork) that talks to nefarious2 directly over WebSocket. Happy to test a fix against the same setup — it's a
docker buildof the branch with the port above added.