Add TLS support. - ii - irc it, simple FIFO based irc client
 (HTM) git clone git://git.suckless.org/ii
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit f301a40a332ddeb1082d729a3a3f5151967abb53
 (DIR) parent 12234d8c5f5ff22fa0fc7b902f201b88bb3be03b
 (HTM) Author: Christoph Lohmann <20h@r-36.net>
       Date:   Wed, 19 Nov 2025 19:11:43 +0100
       
       Add TLS support.
       
       Diffstat:
         M Makefile                            |       3 ++-
         M README                              |      33 +++----------------------------
         M ii.1                                |      11 ++++++++---
         M ii.c                                |      71 ++++++++++++++++++++++++++-----
       
       4 files changed, 74 insertions(+), 44 deletions(-)
       ---
 (DIR) 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
        
 (DIR) 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
 (DIR) 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)
 (DIR) 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;
        }