Tobias Girstmair [Sat, 25 May 2024 21:21:50 +0000 (23:21 +0200)]
clean up some todos
the thing regarding tls_default_ca_cert_file was wrong and is never
needed. EPIPE et al are already "handled" that way. i (foolishly?) don't
expect EINTR or EAGAIN, given that we call poll immediately before.
the sasl todo isn't that important, as we only support plain password
auth with sasl and we don't expect passphrases longer than 300 chars (if
this were violated, the server would just fail our auth attempt with
ERR_SASLTOOLONG/905 (at which point we'd hang, until we implement proper
timeouts during setup. too bad.)).
Tobias Girstmair [Sat, 25 May 2024 21:00:12 +0000 (23:00 +0200)]
params cannot be empty
... because we always append a final argument, even if it's the empty
string. there actually is a slight bug with empty line or ones with
only a prefix here (command becomes b""), but these are broken anyways.
Tobias Girstmair [Sat, 25 May 2024 20:14:57 +0000 (22:14 +0200)]
provide a tool to highlight irc protocol messages
this can be used as a bare-bones irc client like so:
IRC_PASSWD=... ./ircpipe -n $NICK -p -s $BOUNCER_HOST | ./highlight | ts
('ts' being the timestamping tool from moreutils)
the code for this looks a bit wonky, but that's kind of on purpose:
while we first parse the message, we then not use this for actual output
and instead slice the raw line and splice in control sequences, as to
not accidentally modify the input; copy-pasting from it should yield the
same bytes as it processed (modulo mIRC style color control chars).
using bytes instead of native strings is necessary as to not break on
non-utf8-encoded messages, something that regularly happens in practise.
Tobias Girstmair [Sat, 25 May 2024 13:11:10 +0000 (15:11 +0200)]
fix segfault on D-line
when connecting to irc.efnet.nl we get banned after RPL_WELCOME. the
whole (looong) MOTD loads, then we get ERR_YOUREBANNEDCREEP and a fatal
ERROR and the server closes the connection.
this bug manifested, when setting '-j', that poll (ircpipe.c:246)
returns revents == POLLHUP|POLLIN (while we expected only POLLIN). when
read(2)ing from the now-closed socket, we then read 0 bytes. in
irc_answer (ircpipe.c:260), strtok_r then returns NULL on the first
invocation (which again we did not expect, as we did not expect an empty
buffer to get passed). then we tried to look at the first character of
the string, dereferencing NULL and causing a SIGSEGV.
the solution is of course to check that return value of read(2) is > 0.
this is equivalent to aborting on POLLHUP; the comment in irc_poll() has
been adjusted to reflect this as well.
Tobias Girstmair [Mon, 20 May 2024 16:17:07 +0000 (18:17 +0200)]
update ircpipe spec document
note that joining multiple channels doesn't need special handling; c.f.
Command: JOIN
Parameters: <channel>{,<channel>} [<key>{,<key>}]
Alt Params: 0
one can just say `-j '#chan1,#chan2`. even protected channels work:
-j '#chan1,#chan2 key1,key2'
Tobias Girstmair [Mon, 20 May 2024 16:04:18 +0000 (18:04 +0200)]
improve PONG sending
this gets rid of the double-send(2) (which in theory allows us to enable
Nagle's TCP_NODELAY). we need to restore the line terminator that strtok
removed. while the spec says we should send \r\n, it would probably
suffice to non-compliantly just do line[n] = '\n' (and rely on postel's
law to save us). but strtok only overwrites the first delimeter;
transforming "...\n..." into "...\0..." and "...\r\n..." into
"...\0\n...". so by looking ahead one byte we can determine which
terminator(s) were sent by the server and send back the same one(s). of
course, this doesn't hold for \n\r, just \r or \n\n (neither of which we
expect), but it would fall back to a sensible form regardless.
Tobias Girstmair [Mon, 20 May 2024 15:44:30 +0000 (17:44 +0200)]
implement socket timeout on connect
before this, we could have hung connecting to unavailable servers for
essentially ever. we only set SO_SNDTIMEO (not SO_RCVTIMEO) as we only
care when packets we send don't get ACKed in time (more specifically, we
only care about the server ACKing our initial SYN), not when the server
stops sending data for a while (the latter we handle using PING/PONG
ourselves).
c.f. https://stackoverflow.com/a/4182564. should cause EINPROGRESS for
connect(2), EAGAIN or EWOULDBLOCK otherwise.
Tobias Girstmair [Mon, 20 May 2024 15:40:21 +0000 (17:40 +0200)]
implement handler for the usual terminating signals
right now, we just send /QUIT and cleanly shut down the socket. in the
future, this allows us to do something to the socket on SIGUSR{1,2}, for
example.
Tobias Girstmair [Fri, 17 May 2024 14:55:37 +0000 (16:55 +0200)]
initiate PING if socket idles for too long
note that we only start tracking the monotonic timestamp of last
incoming socket activity in irc_poll(). because of that, we must
initialize recv_ts or we risk never sending the first PING when the
server doesn't send anything after the 001 numeric (e.g. znc with no
joined channels).
there isn't really a reason to configure ping interval/pong timeout, so
we just pick some values.
Tobias Girstmair [Fri, 17 May 2024 12:10:06 +0000 (14:10 +0200)]
simplify sasl handling
note that we still don't support multiple SASL chunks; the error
handling was just removed from irc_base64() (OR_DIE is incompatible with
assignment). we should probably verify this constraint in main().
Tobias Girstmair [Sun, 12 May 2024 18:29:46 +0000 (20:29 +0200)]
NULL-terminate read() calls
we reuse the samme buffer for all network and local read(2)s. irc_answer
(or more specifically, strtok(3)) expects NULL-terminated strings. this
caused us to re-parse older strings when the buffer shrank between two
consecutive read(2) calls.
we always listen for POLLIN events, so we don't need to explicitly check
for it. not sure if this implementation is correct. modeled after
https://github.com/openbsd/src/blob/master/usr.bin/nc/netcat.c