commit f301a40a332ddeb1082d729a3a3f5151967abb53
parent 12234d8c5f5ff22fa0fc7b902f201b88bb3be03b
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(-)
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;
}