ii

irc it, simple FIFO based irc client
git clone git://git.suckless.org/ii
Log | Files | Refs | README | LICENSE

commit f301a40a332ddeb1082d729a3a3f5151967abb53
parent 12234d8c5f5ff22fa0fc7b902f201b88bb3be03b
Author: Christoph Lohmann <20h@r-36.net>
Date:   Wed, 19 Nov 2025 19:11:43 +0100

Add TLS support.

Diffstat:
MMakefile | 3++-
MREADME | 33+++------------------------------
Mii.1 | 11++++++++---
Mii.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
4 files changed, 74 insertions(+), 44 deletions(-)

diff --git a/Makefile b/Makefile @@ -18,7 +18,8 @@ II_LDFLAGS = $(LDFLAGS) # remove NEED_STRLCPY from CPPFLAGS and # remove strlcpy.o from LIBS II_CPPFLAGS = $(CPPFLAGS) -DVERSION=\"$(VERSION)\" -D_DEFAULT_SOURCE -DNEED_STRLCPY -LIBS = strlcpy.o +# -tls is easily provided using libretls. +LIBS = strlcpy.o -ltls all: ii diff --git a/README b/README @@ -46,38 +46,11 @@ http://nion.modprobe.de/blog/archives/440-Using-the-ii-irc-client.html SSL/TLS support --------------- +ii uses libretls for direct TLS support. Here is an example: -Below is an example using OpenBSD relayd which sets up a TCP TLS relay -connection on localhost. A similar setup can be accomplished using -stunnel or netcat with TLS support. This also works for other programs -that don't support TLS natively. + ii -s irc.libera.chat -p 6697 -t -/etc/relayd.conf: - - table <freenode> { irc.freenode.net } - table <oftc> { irc.oftc.net } - - protocol "irctls" { - tcp { nodelay, sack } - } - - relay "freenode" { - listen on 127.0.0.1 port 6668 - protocol "irctls" - forward with tls to <freenode> port 6697 - } - - relay "oftc" { - listen on 127.0.0.1 port 6669 - protocol "irctls" - forward with tls to <oftc> port 6697 - } - - -Then connect: - - ./irc -n nick -u name -s 127.0.0.1 -p 6668 - ./irc -n nick -u name -s 127.0.0.1 -p 6669 +You can turn off TLS certificate verification using -v. Configuration diff --git a/ii.1 b/ii.1 @@ -6,6 +6,8 @@ ii - irc it or irc improved .RB [ -46 ] .B -s .I host +.RB [ -t ] +.RB [ -v ] .RB [ -p .I port | @@ -44,6 +46,12 @@ only connect to some IPv6 host .BI -s " host" server/host to connect to, for example: irc.freenode.net .TP +.BI -t +connect using TLS +.TP +.BI -v +do not verify TLS certificate +.TP .BI -p " port" lets you override the default port (6667) .TP @@ -120,9 +128,6 @@ set the topic of a channel with Everything which is not a command will be posted into the channel or to the server. So if you need /who just write /WHO as described in RFC#1459 to the server in FIFO. -.SH SSL/TLS PROTOCOL SUPPORT -For SSL/TLS protocol support you can connect to a local tunnel, for example -with stunnel or socat. .SH SEE ALSO .BR echo (1), .BR tail (1) diff --git a/ii.c b/ii.c @@ -19,6 +19,7 @@ #include <string.h> #include <time.h> #include <unistd.h> +#include <tls.h> char *argv0; @@ -67,7 +68,7 @@ static void loginuser(int, const char *, const char *); static void proc_channels_input(int, Channel *, char *); static void proc_channels_privmsg(int, Channel *, char *); static void proc_server_cmd(int, char *); -static int read_line(int, char *, size_t); +static int read_line(int, char *, size_t, int); static void run(int, const char *); static void setup(void); static void sighandler(int); @@ -85,6 +86,10 @@ static char _nick[32]; /* nickname at startup */ static char ircpath[PATH_MAX]; /* irc dir (-i) */ static char msg[IRC_MSG_MAX]; /* message buf used for communication */ +static int usetls = 0; +static struct tls *tls = NULL; +static struct tls_config *tlscfg = NULL; + static void die(const char *fmt, ...) { @@ -101,7 +106,7 @@ die(const char *fmt, ...) static void usage(void) { - die("usage: %s [-46] -s host [-p port | -u sockname] [-i ircdir]\n" + die("usage: %s [-46] -s host [-tv] [-p port | -u sockname] [-i ircdir]\n" " [-n nickname] [-f fullname] [-k env_pass]\n", argv0); } @@ -113,8 +118,13 @@ ewritestr(int fd, const char *s) len = strlen(s); for (off = 0; off < len; off += w) { - if ((w = write(fd, s + off, len - off)) == -1) - break; + if (usetls) { + if ((w = tls_write(tls, s + off, len - off)) == -1) + break; + } else { + if ((w = write(fd, s + off, len - off)) == -1) + break; + } } if (w == -1) die("%s: write: %s\n", argv0, strerror(errno)); @@ -680,14 +690,29 @@ proc_server_cmd(int fd, char *buf) } static int -read_line(int fd, char *buf, size_t bufsiz) +read_line(int fd, char *buf, size_t bufsiz, int readtls) { size_t i = 0; char c = '\0'; + ssize_t sread = 0; do { - if (read(fd, &c, sizeof(char)) != sizeof(char)) - return -1; + if (usetls && readtls) { + sread = tls_read(tls, &c, sizeof(char)); + /* + * Only try twice. If things go wrong, this is + * the best heuristics to fail. + */ + if (sread == TLS_WANT_POLLIN) + sread = tls_read(tls, &c, sizeof(char)); + if (sread == TLS_WANT_POLLIN) + sread = tls_read(tls, &c, sizeof(char)); + if (sread != sizeof(char)) + return -1; + } else { + if (read(fd, &c, sizeof(char)) != sizeof(char)) + return -1; + } buf[i++] = c; } while (c != '\n' && i < bufsiz); buf[i - 1] = '\0'; /* eliminates '\n' */ @@ -706,7 +731,7 @@ handle_channels_input(int ircfd, Channel *c) */ char buf[IRC_MSG_MAX-64]; - if (read_line(c->fdin, buf, sizeof(buf)) == -1) { + if (read_line(c->fdin, buf, sizeof(buf), 0) == -1) { if (channel_reopen(c) == -1) channel_rm(c); return; @@ -719,7 +744,7 @@ handle_server_output(int ircfd) { char buf[IRC_MSG_MAX]; - if (read_line(ircfd, buf, sizeof(buf)) == -1) + if (read_line(ircfd, buf, sizeof(buf), 1) == -1) die("%s: remote host closed connection: %s\n", argv0, strerror(errno)); fprintf(stdout, "%lu %s\n", (unsigned long)time(NULL), buf); @@ -732,6 +757,7 @@ sighandler(int sig) { if (sig == SIGTERM || sig == SIGINT) isrunning = 0; + /* Ignore SIGPIPE */ } static void @@ -743,6 +769,7 @@ setup(void) sa.sa_handler = sighandler; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); } static void @@ -799,7 +826,7 @@ main(int argc, char *argv[]) const char *key = NULL, *fullname = NULL, *host = ""; const char *uds = NULL, *service = "6667"; char prefix[PATH_MAX]; - int ircfd, r, af = AF_UNSPEC; + int ircfd, r, af = AF_UNSPEC, doverifytls = 1; /* use nickname and home dir of user by default */ if (!(spw = getpwuid(getuid()))) @@ -833,6 +860,12 @@ main(int argc, char *argv[]) case 's': host = EARGF(usage()); break; + case 't': + usetls = 1; + break; + case 'v': + doverifytls = 0; + break; case 'u': uds = EARGF(usage()); break; @@ -849,6 +882,21 @@ main(int argc, char *argv[]) else ircfd = tcpopen(host, service, af); + if (usetls && !uds) { + if (tls_init() < 0) + die("%s: tls_init: %s\n", strerror(errno)); + if ((tlscfg = tls_config_new()) == NULL) + die("%s: tls_config_new: %s\n", strerror(errno)); + if (!doverifytls) + tls_config_insecure_noverifycert(tlscfg); + if (!(tls = tls_client())) + die("%s: tls_client: %s\n", tls_error(tls)); + if (tls_configure(tls, tlscfg)) + die("%s: tls_configure: %s\n", tls_error(tls)); + if (tls_connect_socket(tls, ircfd, host) < 0) + die("%s: tls_connect_socket: %s\n", tls_error(tls)); + } + #ifdef __OpenBSD__ /* OpenBSD pledge(2) support */ if (pledge("stdio rpath wpath cpath dpath", NULL) == -1) @@ -868,5 +916,8 @@ main(int argc, char *argv[]) run(ircfd, host); cleanup(); + if (tls) + tls_close(tls); + return 0; }