changeset 970:c745bb6721fd

irccd: several cleanup and improvements for SSL
author David Demelier <markand@malikania.fr>
date Wed, 03 Feb 2021 20:05:00 +0100
parents d7501067de95
children f365e5be1261
files irccd/conf.y lib/irccd/channel.c lib/irccd/channel.h lib/irccd/conn.c lib/irccd/conn.h lib/irccd/event.c lib/irccd/event.h lib/irccd/server.c lib/irccd/server.h
diffstat 9 files changed, 203 insertions(+), 155 deletions(-) [+]
line wrap: on
line diff
--- a/irccd/conf.y	Wed Feb 03 12:37:46 2021 +0100
+++ b/irccd/conf.y	Wed Feb 03 20:05:00 2021 +0100
@@ -432,6 +432,7 @@
 			string_list_finish($4->channels);
 		}
 
+		s->flags = $4->flags;
 		irc_bot_server_add(s);
 
 		free($2);
--- a/lib/irccd/channel.c	Wed Feb 03 12:37:46 2021 +0100
+++ b/lib/irccd/channel.c	Wed Feb 03 20:05:00 2021 +0100
@@ -36,7 +36,7 @@
 }
 
 struct irc_channel *
-irc_channel_new(const char *name, const char *password, bool joined)
+irc_channel_new(const char *name, const char *password, int joined)
 {
 	assert(name);
 
--- a/lib/irccd/channel.h	Wed Feb 03 12:37:46 2021 +0100
+++ b/lib/irccd/channel.h	Wed Feb 03 20:05:00 2021 +0100
@@ -20,7 +20,6 @@
 #define IRCCD_CHANNEL_H
 
 #include <sys/queue.h>
-#include <stdbool.h>
 #include <stddef.h>
 
 #include "limits.h"
@@ -35,7 +34,7 @@
 struct irc_channel {
 	char name[IRC_CHANNEL_LEN];
 	char password[IRC_PASSWORD_LEN];
-	bool joined;
+	int joined;
 	LIST_HEAD(, irc_channel_user) users;
 	LIST_ENTRY(irc_channel) link;
 };
@@ -43,7 +42,7 @@
 LIST_HEAD(irc_channel_list, irc_channel);
 
 struct irc_channel *
-irc_channel_new(const char *, const char *, bool);
+irc_channel_new(const char *, const char *, int);
 
 void
 irc_channel_add(struct irc_channel *, const char *, char, char);
--- a/lib/irccd/conn.c	Wed Feb 03 12:37:46 2021 +0100
+++ b/lib/irccd/conn.c	Wed Feb 03 20:05:00 2021 +0100
@@ -40,10 +40,13 @@
 	if (conn->ctx)
 		SSL_CTX_free(conn->ctx);
 
+	conn->ssl_cond = IRC_CONN_SSL_ACT_NONE;
+	conn->ssl_step = IRC_CONN_SSL_ACT_NONE;
 	conn->ssl = NULL;
 	conn->ctx = NULL;
 #endif
 
+	conn->state = IRC_CONN_STATE_NONE;
 	conn->fd = -1;
 }
 
@@ -59,7 +62,7 @@
 	*line = p ? p + 1 : strchr(*line, '\0');
 }
 
-static bool
+static int
 parse(struct irc_conn_msg *msg, const char *line)
 {
 	char *ptr = msg->buf;
@@ -88,14 +91,14 @@
 	}
 
 	if (a >= IRC_UTIL_SIZE(msg->args))
-		return errno = EMSGSIZE, false;
+		return errno = EMSGSIZE, -1;
 	if (msg->cmd == NULL)
-		return errno = EBADMSG, false;
+		return errno = EBADMSG, -1;
 
-	return true;
+	return 0;
 }
 
-static bool
+static int
 create(struct irc_conn *conn)
 {
 	struct addrinfo *ai = conn->aip;
@@ -104,24 +107,24 @@
 	cleanup(conn);
 
 	if ((conn->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
-		return false;
+		return -1;
 	if ((cflags = fcntl(conn->fd, F_GETFL)) < 0)
-		return false;
+		return -1;
 	if (fcntl(conn->fd, F_SETFL, cflags | O_NONBLOCK) < 0)
-		return false;
+		return -1;
 
-	return true;
+	return 0;
 }
 
-static inline bool
+static inline int
 update_ssl_state(struct irc_conn *conn, int ret)
 {
 	switch (SSL_get_error(conn->ssl, ret)) {
 	case SSL_ERROR_WANT_READ:
-		conn->ssl_state = IRC_CONN_SSL_STATE_NEED_READ;
+		conn->ssl_cond = IRC_CONN_SSL_ACT_READ;
 		break;
 	case SSL_ERROR_WANT_WRITE:
-		conn->ssl_state = IRC_CONN_SSL_STATE_NEED_WRITE;
+		conn->ssl_cond = IRC_CONN_SSL_ACT_WRITE;
 		break;
 	case SSL_ERROR_SSL:
 		return irc_conn_disconnect(conn), -1;
@@ -129,18 +132,21 @@
 		break;
 	}
 
-	return true;
+	return 0;
 }
 
-static ssize_t
+static inline ssize_t
 input_ssl(struct irc_conn *conn, char *dst, size_t dstsz)
 {
 	int nr;
 
-	if ((nr = SSL_read(conn->ssl, dst, dstsz)) <= 0)
+	if ((nr = SSL_read(conn->ssl, dst, dstsz)) <= 0) {
+		conn->ssl_step = IRC_CONN_SSL_ACT_READ;
 		return update_ssl_state(conn, nr);
+	}
 
-	conn->ssl_state = IRC_CONN_SSL_STATE_NONE;
+	conn->ssl_cond = IRC_CONN_SSL_ACT_NONE;
+	conn->ssl_step = IRC_CONN_SSL_ACT_NONE;
 
 	return nr;
 }
@@ -150,13 +156,15 @@
 {
 	ssize_t nr;
 
-	if ((nr = recv(conn->fd, buf, bufsz, 0)) <= 0)
+	if ((nr = recv(conn->fd, buf, bufsz, 0)) <= 0) {
+		errno = ECONNRESET;
 		return irc_conn_disconnect(conn), -1;
+	}
 
 	return nr;
 }
 
-static bool
+static int
 input(struct irc_conn *conn)
 {
 	size_t len = strlen(conn->in);
@@ -171,7 +179,7 @@
 	if (nr > 0)
 		conn->in[len + nr] = '\0';
 
-	return nr > 0;
+	return nr;
 }
 
 static inline ssize_t
@@ -179,8 +187,13 @@
 {
 	int ns;
 
-	if ((ns = SSL_write(conn->ssl, conn->out, strlen(conn->out))) <= 0)
+	if ((ns = SSL_write(conn->ssl, conn->out, strlen(conn->out))) <= 0) {
+		conn->ssl_step = IRC_CONN_SSL_ACT_WRITE;
 		return update_ssl_state(conn, ns);
+	}
+
+	conn->ssl_cond = IRC_CONN_SSL_ACT_NONE;
+	conn->ssl_step = IRC_CONN_SSL_ACT_NONE;
 
 	return ns;
 }
@@ -196,7 +209,7 @@
 	return ns;
 }
 
-static bool
+static int
 output(struct irc_conn *conn)
 {
 	ssize_t ns = 0;
@@ -214,10 +227,10 @@
 			memmove(conn->out, conn->out + ns, sizeof (conn->out) - ns);
 	}
 
-	return ns != -1;
+	return ns;
 }
 
-static bool
+static int
 handshake(struct irc_conn *conn)
 {
 	if (conn->flags & IRC_CONN_SSL) {
@@ -244,25 +257,25 @@
 			return update_ssl_state(conn, r);
 
 		conn->state = IRC_CONN_STATE_READY;
+		conn->ssl_cond = IRC_CONN_SSL_ACT_NONE;
+		conn->ssl_step = IRC_CONN_SSL_ACT_NONE;
 #endif
 	} else
 		conn->state = IRC_CONN_STATE_READY;
 
-	return true;
+	return 0;
 }
 
-static bool
+static int
 dial(struct irc_conn *conn)
 {
 	/* No more address available. */
 	if (conn->aip == NULL)
-		return irc_conn_disconnect(conn), false;
+		return irc_conn_disconnect(conn), -1;
 
 	for (; conn->aip; conn->aip = conn->aip->ai_next) {
-		if (!create(conn)) {
-			// irc_log_warn("server %s: %s", s->name, strerror(errno));
+		if (create(conn) < 0)
 			continue;
-		}
 
 		/*
 		 * With some luck, the connection completes immediately,
@@ -272,14 +285,16 @@
 			return handshake(conn);
 
 		/* Connect "succeeds" but isn't complete yet. */
-		if (errno == EINPROGRESS || errno == EAGAIN)
-			return true;
+		if (errno == EINPROGRESS || errno == EAGAIN) {
+			conn->state = IRC_CONN_STATE_CONNECTING;
+			return 0;
+		}
 	}
 
-	return false;
+	return -1;
 }
 
-static bool
+static int
 lookup(struct irc_conn *conn)
 {
 	struct addrinfo hints = {
@@ -293,15 +308,15 @@
 
 	if ((ret = getaddrinfo(conn->hostname, service, &hints, &conn->ai)) != 0) {
 		// irc_log_warn gai_strerror(ret)
-		return false;
+		return -1;
 	}
 
 	conn->aip = conn->ai;
 
-	return true;
+	return 0;
 }
 
-static bool
+static int
 check_connect(struct irc_conn *conn)
 {
 	int res, err = -1;
@@ -314,37 +329,15 @@
 	return handshake(conn);
 }
 
-bool
-irc_conn_connect(struct irc_conn *conn)
-{
-	assert(conn);
-
-	conn->state = IRC_CONN_STATE_CONNECTING;
-
-	if (!lookup(conn))
-		return irc_conn_disconnect(conn), false;
-
-	return dial(conn), true;
-}
-
-void
-irc_conn_disconnect(struct irc_conn *conn)
-{
-	assert(conn);
-
-	cleanup(conn);
-	conn->state = IRC_CONN_STATE_NONE;
-}
-
 static inline void
 prepare_ssl(const struct irc_conn *conn, struct pollfd *pfd)
 {
 #if defined(IRCCD_WITH_SSL)
-	switch (conn->ssl_state) {
-	case IRC_CONN_SSL_STATE_NEED_READ:
+	switch (conn->ssl_cond) {
+	case IRC_CONN_SSL_ACT_READ:
 		pfd->events |= POLLIN;
 		break;
-	case IRC_CONN_SSL_STATE_NEED_WRITE:
+	case IRC_CONN_SSL_ACT_WRITE:
 		pfd->events |= POLLOUT;
 		break;
 	default:
@@ -355,6 +348,33 @@
 #endif
 }
 
+static inline int
+renegotiate(struct irc_conn *conn)
+{
+	return conn->ssl_step == IRC_CONN_SSL_ACT_READ
+		? input(conn)
+		: output(conn);
+}
+
+int
+irc_conn_connect(struct irc_conn *conn)
+{
+	assert(conn);
+
+	if (lookup(conn) < 0)
+		return irc_conn_disconnect(conn), -1;
+
+	return dial(conn);
+}
+
+void
+irc_conn_disconnect(struct irc_conn *conn)
+{
+	assert(conn);
+
+	cleanup(conn);
+}
+
 void
 irc_conn_prepare(const struct irc_conn *conn, struct pollfd *pfd)
 {
@@ -363,7 +383,7 @@
 
 	pfd->fd = conn->fd;
 
-	if (conn->ssl_state)
+	if (conn->ssl_cond)
 		prepare_ssl(conn, pfd);
 	else {
 		switch (conn->state) {
@@ -382,7 +402,7 @@
 	}
 }
 
-bool
+int
 irc_conn_flush(struct irc_conn *conn, const struct pollfd *pfd)
 {
 	assert(conn);
@@ -395,20 +415,22 @@
 		return handshake(conn);
 	case IRC_CONN_STATE_READY:
 		if (pfd->revents & (POLLERR | POLLHUP))
-			return irc_conn_disconnect(conn), false;
-		if (pfd->revents & POLLIN && !input(conn))
-			return irc_conn_disconnect(conn), false;
-		if (pfd->revents & POLLOUT && !output(conn))
-			return irc_conn_disconnect(conn), false;
+			return irc_conn_disconnect(conn), -1;
+		if (conn->ssl_cond && renegotiate(conn) < 0)
+			return irc_conn_disconnect(conn), -1;
+		if (pfd->revents & POLLIN && input(conn) < 0)
+			return irc_conn_disconnect(conn), -1;
+		if (pfd->revents & POLLOUT && output(conn) < 0)
+			return irc_conn_disconnect(conn), -1;
 		break;
 	default:
 		break;
 	}
 
-	return true;
+	return 0;
 }
 
-bool
+int
 irc_conn_poll(struct irc_conn *conn, struct irc_conn_msg *msg)
 {
 	assert(conn);
@@ -418,7 +440,7 @@
 	size_t length;
 
 	if (!(pos = strstr(conn->in, "\r\n")))
-		return false;
+		return 0;
 
 	/* Turn end of the string at delimiter. */
 	*pos = 0;
@@ -427,27 +449,30 @@
 	if (length > 0)
 		parse(msg, conn->in);
 
+	/* (Re)move the first message received. */
 	memmove(conn->in, pos + 2, sizeof (conn->in) - (length + 2));
 
-	return true;
+	return 1;
 }
 
-bool
+int
 irc_conn_send(struct irc_conn *conn, const char *data)
 {
 	assert(conn);
 	assert(data);
 
 	if (strlcat(conn->out, data, sizeof (conn->out)) >= sizeof (conn->out))
-		return errno = EMSGSIZE, false;
+		return errno = EMSGSIZE, -1;
 	if (strlcat(conn->out, "\r\n", sizeof (conn->out)) >= sizeof (conn->out))
-		return errno = EMSGSIZE, false;
+		return errno = EMSGSIZE, -1;
 
-	return true;
+	return 0;
 }
 
 void
 irc_conn_finish(struct irc_conn *conn)
 {
 	assert(conn);
+
+	cleanup(conn);
 }
--- a/lib/irccd/conn.h	Wed Feb 03 12:37:46 2021 +0100
+++ b/lib/irccd/conn.h	Wed Feb 03 20:05:00 2021 +0100
@@ -19,8 +19,6 @@
 #ifndef IRCCD_CONN_H
 #define IRCCD_CONN_H
 
-#include <stdbool.h>
-
 #include "config.h"
 
 #if defined(IRCCD_WITH_SSL)
@@ -45,10 +43,10 @@
 
 #if defined(IRCCD_WITH_SSL)
 
-enum irc_conn_ssl_state {
-	IRC_CONN_SSL_STATE_NONE,
-	IRC_CONN_SSL_STATE_NEED_READ,
-	IRC_CONN_SSL_STATE_NEED_WRITE,
+enum irc_conn_ssl_act {
+	IRC_CONN_SSL_ACT_NONE,
+	IRC_CONN_SSL_ACT_READ,
+	IRC_CONN_SSL_ACT_WRITE,
 };
 
 #endif
@@ -67,7 +65,8 @@
 #if defined(IRCCD_WITH_SSL)
 	SSL_CTX *ctx;
 	SSL *ssl;
-	enum irc_conn_ssl_state ssl_state;
+	enum irc_conn_ssl_act ssl_cond;
+	enum irc_conn_ssl_act ssl_step;
 #endif
 };
 
@@ -78,7 +77,7 @@
 	char buf[IRC_MESSAGE_LEN];
 };
 
-bool
+int
 irc_conn_connect(struct irc_conn *);
 
 void
@@ -87,13 +86,13 @@
 void
 irc_conn_prepare(const struct irc_conn *, struct pollfd *);
 
-bool
+int
 irc_conn_flush(struct irc_conn *, const struct pollfd *);
 
-bool
+int
 irc_conn_poll(struct irc_conn *, struct irc_conn_msg *);
 
-bool
+int
 irc_conn_send(struct irc_conn *, const char *);
 
 void
--- a/lib/irccd/event.c	Wed Feb 03 12:37:46 2021 +0100
+++ b/lib/irccd/event.c	Wed Feb 03 20:05:00 2021 +0100
@@ -43,9 +43,8 @@
 		    ev->server->name);
 		break;
 	case IRC_EVENT_INVITE:
-		written = snprintf(str, strsz, "EVENT-INVITE %s %s %s %s",
-		    ev->server->name, ev->invite.origin, ev->invite.channel,
-		    ev->invite.target);
+		written = snprintf(str, strsz, "EVENT-INVITE %s %s %s",
+		    ev->server->name, ev->invite.origin, ev->invite.channel);
 		break;
 	case IRC_EVENT_JOIN:
 		written = snprintf(str, strsz, "EVENT-JOIN %s %s %s",
--- a/lib/irccd/event.h	Wed Feb 03 12:37:46 2021 +0100
+++ b/lib/irccd/event.h	Wed Feb 03 20:05:00 2021 +0100
@@ -58,7 +58,6 @@
 struct irc_event_invite {
 	char *origin;
 	char *channel;
-	char *target;
 };
 
 struct irc_event_join {
--- a/lib/irccd/server.c	Wed Feb 03 12:37:46 2021 +0100
+++ b/lib/irccd/server.c	Wed Feb 03 20:05:00 2021 +0100
@@ -37,7 +37,7 @@
 #include "util.h"
 
 static inline void
-clear_channels(struct irc_server *s, bool free)
+clear_channels(struct irc_server *s, int free)
 {
 	struct irc_channel *c, *tmp;
 
@@ -65,7 +65,7 @@
 	memset(&s->bufwhois, 0, sizeof (s->bufwhois));
 }
 
-static inline bool
+static inline int
 is_self(const struct irc_server *s, const char *nick)
 {
 	return strncmp(s->ident.nickname, nick, strlen(s->ident.nickname)) == 0;
@@ -81,7 +81,7 @@
 }
 
 static struct irc_channel *
-add_channel(struct irc_server *s, const char *name, const char *password, bool joined)
+add_channel(struct irc_server *s, const char *name, const char *password, int joined)
 {
 	struct irc_channel *ch;
 
@@ -110,20 +110,20 @@
 	LIST_REMOVE(ch, link);
 }
 
-bool
+static int
 is_ctcp(const char *line)
 {
 	size_t length;
 
 	if (!line)
-		return false;
+		return 0;
 	if ((length = strlen(line)) < 2)
-		return false;
+		return 0;
 
 	return line[0] == 0x1 && line[length - 1] == 0x1;
 }
 
-char *
+static char *
 ctcp(char *line)
 {
 	/* Skip first \001. */
@@ -156,6 +156,10 @@
 		for (size_t i = 0; i < IRC_UTIL_SIZE(s->params.prefixes) && *pm && *tk; ++i) {
 			s->params.prefixes[i].mode = *pm++;
 			s->params.prefixes[i].token = *tk++;
+
+			irc_log_info("server %s: supports prefix %c=%c", s->name,
+			    s->params.prefixes[i].mode,
+			    s->params.prefixes[i].token);
 		}
 	}
 }
@@ -164,6 +168,7 @@
 read_support_chantypes(struct irc_server *s, const char *value)
 {
 	strlcpy(s->params.chantypes, value, sizeof (s->params.chantypes));
+	irc_log_info("server %s: supports channel types: %s", s->name, s->params.chantypes);
 }
 
 static void
@@ -173,13 +178,14 @@
 
 	struct irc_channel *ch;
 
-	s->state = IRC_SERVER_STATE_CONNECTED;
-
 	/* Now join all channels that were requested. */
 	LIST_FOREACH(ch, &s->channels, link)
 		irc_server_join(s, ch->name, ch->password);
 
+	s->state = IRC_SERVER_STATE_CONNECTED;
 	ev->type = IRC_EVENT_CONNECT;
+
+	irc_log_info("server %s: connection complete", s->name);
 }
 
 static void
@@ -188,6 +194,7 @@
 	s->state = IRC_SERVER_STATE_NONE;
 	ev->type = IRC_EVENT_DISCONNECT;
 	ev->server = s;
+	irc_log_info("server %s: connection lost", s->name);
 }
 
 static void
@@ -215,10 +222,11 @@
 	ev->type = IRC_EVENT_INVITE;
 	ev->invite.origin = strdup(msg->args[0]);
 	ev->invite.channel = strdup(msg->args[1]);
-	ev->invite.target = strdup(msg->args[2]);
 
-	if (is_self(s, ev->invite.target) && s->flags & IRC_SERVER_FLAGS_JOIN_INVITE)
+	if (s->flags & IRC_SERVER_FLAGS_JOIN_INVITE) {
 		irc_server_join(s, ev->invite.channel, NULL);
+		irc_log_info("server %s: joining %s on invite", s->name, ev->invite.channel);
+	}
 }
 
 static void
@@ -229,6 +237,9 @@
 	ev->join.channel = strdup(msg->args[0]);
 
 	add_channel(s, ev->join.channel, NULL, true);
+
+	if (is_self(s, ev->join.origin))
+		irc_log_info("server %s: joined channel %s", s->name, ev->join.channel);
 }
 
 static void
@@ -246,12 +257,14 @@
 	 * If the bot was kicked itself mark the channel as not joined and
 	 * rejoin it automatically if the option is set.
 	 */
-	if (is_self(s, ev->kick.target) == 0) {
-		ch->joined = false;
+	if (is_self(s, ev->kick.target)) {
+		ch->joined = 0;
 		irc_channel_clear(ch);
 
-		if (s->flags & IRC_SERVER_FLAGS_AUTO_REJOIN)
+		if (s->flags & IRC_SERVER_FLAGS_AUTO_REJOIN) {
 			irc_server_join(s, ch->name, ch->password);
+			irc_log_info("server %s: rejoining %s after kick", s->name, ch->name);
+		}
 	} else
 		irc_channel_remove(ch, ev->kick.target);
 }
@@ -286,9 +299,10 @@
 
 	ch = add_channel(s, ev->part.channel, NULL, true);
 
-	if (is_self(s, ev->part.origin) == 0)
+	if (is_self(s, ev->part.origin) == 0) {
 		remove_channel(ch);
-	else
+		irc_log_info("server %s: leaving channel %s", s->name, ev->part.channel);
+	} else
 		irc_channel_remove(ch, ev->part.origin);
 }
 
@@ -323,8 +337,11 @@
 	ev->nick.nickname = strdup(msg->args[0]);
 
 	/* Update nickname if it is myself. */
-	if (is_self(s, ev->nick.origin) == 0)
+	if (is_self(s, ev->nick.origin) == 0) {
+		irc_log_info("server %s: nick change %s -> %s", s->name,
+		    s->ident.nickname, ev->nick.nickname);
 		strlcpy(s->ident.nickname, ev->nick.nickname, sizeof (s->ident.nickname));
+	}
 }
 
 static void
@@ -540,9 +557,8 @@
 
 	struct irc_server *s;
 
+	/* Connection. */
 	s = irc_util_calloc(1, sizeof (*s));
-
-	/* Connection. */
 	s->conn.port = port;
 	strlcpy(s->conn.hostname, hostname, sizeof (s->conn.hostname));
 
@@ -563,10 +579,13 @@
 {
 	assert(s);
 
-	if (irc_conn_connect(&s->conn))
+	if (s->flags & IRC_SERVER_FLAGS_SSL)
+		s->conn.flags |= IRC_CONN_SSL;
+
+	if (irc_conn_connect(&s->conn) < 0)
+		s->state = IRC_SERVER_STATE_DISCONNECTED;
+	else
 		s->state = IRC_SERVER_STATE_CONNECTING;
-	else
-		s->state = IRC_SERVER_STATE_DISCONNECTED;
 }
 
 void
@@ -597,7 +616,7 @@
 	assert(s);
 	assert(pfd);
 
-	if (!irc_conn_flush(&s->conn, pfd))
+	if (irc_conn_flush(&s->conn, pfd) < 0)
 		return irc_server_disconnect(s);
 	if (s->conn.state != IRC_CONN_STATE_READY)
 		return;
@@ -615,7 +634,7 @@
 	}
 }
 
-bool
+int
 irc_server_poll(struct irc_server *s, struct irc_event *ev)
 {
 	assert(s);
@@ -623,12 +642,20 @@
 
 	struct irc_conn_msg msg = {0};
 
-	if (irc_conn_poll(&s->conn, &msg))
-		return handle(s, ev, &msg), true;
+	/*
+	 * When the server gets disconnected, the state changes to
+	 * IRC_SERVER_STATE_DISCONNECTED which notifies the caller with the
+	 * appropriate event. Then to avoid returning this same event each time
+	 * this function is called again, we immediately change the state to
+	 * something else.
+	 */
 	if (s->state == IRC_SERVER_STATE_DISCONNECTED)
-		return handle_disconnect(s, ev), true;
+		return handle_disconnect(s, ev), 1;
 
-	return false;
+	if (irc_conn_poll(&s->conn, &msg))
+		return handle(s, ev, &msg), 1;
+
+	return 0;
 }
 
 struct irc_channel *
@@ -646,7 +673,7 @@
 	return NULL;
 }
 
-bool
+int
 irc_server_send(struct irc_server *s, const char *fmt, ...)
 {
 	assert(s);
@@ -662,7 +689,7 @@
 	return irc_conn_send(&s->conn, buf);
 }
 
-bool
+int
 irc_server_invite(struct irc_server *s, const char *channel, const char *target)
 {
 	assert(s);
@@ -672,7 +699,7 @@
 	return irc_server_send(s, "INVITE %s %s", target, channel);
 }
 
-bool
+int
 irc_server_join(struct irc_server *s, const char *name, const char *pass)
 {
 	assert(s);
@@ -687,7 +714,7 @@
 	 * and wait for connection.
 	 */
 	if (!(ch = irc_server_find(s, name)))
-		ch = add_channel(s, name, pass, false);
+		ch = add_channel(s, name, pass, 0);
 
 	if (!ch->joined && s->state == IRC_SERVER_STATE_CONNECTED) {
 		if (pass)
@@ -699,14 +726,14 @@
 	return ret;
 }
 
-bool
+int
 irc_server_kick(struct irc_server *s, const char *channel, const char *target, const char *reason)
 {
 	assert(s);
 	assert(channel);
 	assert(target);
 
-	bool ret;
+	int ret;
 
 	if (reason)
 		ret = irc_server_send(s, "KICK %s %s :%s", channel, target, reason);
@@ -716,13 +743,13 @@
 	return ret;
 }
 
-bool
+int
 irc_server_part(struct irc_server *s, const char *channel, const char *reason)
 {
 	assert(s);
 	assert(channel);
 
-	bool ret;
+	int ret;
 
 	if (reason && strlen(reason) > 0)
 		ret = irc_server_send(s, "PART %s :%s", channel, reason);
@@ -732,7 +759,7 @@
 	return ret;
 }
 
-bool
+int
 irc_server_topic(struct irc_server *s, const char *channel, const char *topic)
 {
 	assert(s);
@@ -742,7 +769,7 @@
 	return irc_server_send(s, "TOPIC %s :%s", channel, topic);
 }
 
-bool
+int
 irc_server_message(struct irc_server *s, const char *chan, const char *msg)
 {
 	assert(s);
@@ -752,7 +779,7 @@
 	return irc_server_send(s, "PRIVMSG %s :%s", chan, msg);
 }
 
-bool
+int
 irc_server_me(struct irc_server *s, const char *chan, const char *message)
 {
 	assert(s);
@@ -762,7 +789,7 @@
 	return irc_server_send(s, "PRIVMSG %s :\001ACTION %s\001", chan, message);
 }
 
-bool
+int
 irc_server_mode(struct irc_server *s,
                 const char *channel,
                 const char *mode,
@@ -780,13 +807,13 @@
 	    mask ? mask : "");
 }
 
-bool
+int
 irc_server_names(struct irc_server *s, const char *channel)
 {
 	return irc_server_send(s, "NAMES %s", channel);
 }
 
-bool
+int
 irc_server_nick(struct irc_server *s, const char *nick)
 {
 	assert(s);
@@ -794,13 +821,13 @@
 
 	if (s->state <= IRC_SERVER_STATE_DISCONNECTED) {
 		strlcpy(s->ident.nickname, nick, sizeof (s->ident.nickname));
-		return true;
+		return 1;
 	}
 
 	return irc_server_send(s, "NICK %s", nick);
 }
 
-bool
+int
 irc_server_notice(struct irc_server *s, const char *channel, const char *message)
 {
 	assert(s);
@@ -810,7 +837,7 @@
 	return irc_server_send(s, "NOTICE %s: %s", channel, message);
 }
 
-bool
+int
 irc_server_whois(struct irc_server *s, const char *target)
 {
 	assert(s);
--- a/lib/irccd/server.h	Wed Feb 03 12:37:46 2021 +0100
+++ b/lib/irccd/server.h	Wed Feb 03 20:05:00 2021 +0100
@@ -20,7 +20,6 @@
 #define IRCCD_SERVER_H
 
 #include <sys/queue.h>
-#include <stdbool.h>
 #include <stddef.h>
 
 #include "channel.h"
@@ -107,37 +106,37 @@
 void
 irc_server_flush(struct irc_server *, const struct pollfd *);
 
-bool
+int
 irc_server_poll(struct irc_server *, struct irc_event *);
 
 struct irc_channel *
 irc_server_find(struct irc_server *, const char *);
 
-bool
+int
 irc_server_send(struct irc_server *, const char *, ...);
 
-bool
+int
 irc_server_invite(struct irc_server *, const char *, const char *);
 
-bool
+int
 irc_server_join(struct irc_server *, const char *, const char *);
 
-bool
+int
 irc_server_kick(struct irc_server *, const char *, const char *, const char *);
 
-bool
+int
 irc_server_part(struct irc_server *, const char *, const char *);
 
-bool
+int
 irc_server_topic(struct irc_server *, const char *, const char *);
 
-bool
+int
 irc_server_message(struct irc_server *, const char *, const char *);
 
-bool
+int
 irc_server_me(struct irc_server *, const char *, const char *);
 
-bool
+int
 irc_server_mode(struct irc_server *,
                 const char *,
                 const char *,
@@ -145,16 +144,16 @@
                 const char *,
                 const char *);
 
-bool
+int
 irc_server_names(struct irc_server *, const char *);
 
-bool
+int
 irc_server_nick(struct irc_server *, const char *);
 
-bool
+int
 irc_server_notice(struct irc_server *, const char *, const char *);
 
-bool
+int
 irc_server_whois(struct irc_server *, const char *);
 
 void