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