summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--config.c73
-rw-r--r--config.h15
-rw-r--r--config.irc2
-rw-r--r--handle.c64
-rw-r--r--ircd.h6
-rw-r--r--main.c15
-rw-r--r--reply.c40
8 files changed, 205 insertions, 11 deletions
diff --git a/Makefile b/Makefile
index e70390f..b9d6948 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,7 @@ NAME ?= ircd
 CFLAGS += -Wall -Wextra
 
 OBJS += channel.o
+OBJS += config.o
 OBJS += handle.o
 OBJS += loop.o
 OBJS += main.o
diff --git a/config.c b/config.c
new file mode 100644
index 0000000..9d945eb
--- /dev/null
+++ b/config.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2026 Nakidai Perumenei <nakidai at disroot dot org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ircd.h"
+
+#include <string.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+char creation[CREATION_MAX] = CREATION_DATE;
+char info[INFO_MAX] = INFO;
+
+int
+readcfg(const char *path)
+{
+	struct Peer peer;
+	ssize_t recvd;
+	int fd, ret;
+
+	fd = open(path, O_RDONLY);
+	ensure(fd != -1, warn("open(%s)", path), 0);
+
+	peer = (struct Peer){
+		.fd = STDERR_FILENO,
+		.type = CONFIG,
+	};
+	strlcpy(peer.nick, path, sizeof(peer.nick));
+
+	for (ret = 0;;)
+	{
+		recvd = read(
+			fd,
+			peer.buf + peer.recvd,
+			sizeof(peer.buf) - peer.recvd
+		);
+		peer.recvd += recvd;
+		if (recvd == -1)
+		{
+			warn("read(%s)", path);
+			++ret;
+			goto end;
+		} else if (!recvd)
+		{
+			goto end;
+		} else if (handle(&peer))
+		{
+			++ret;
+			goto end;
+		}
+	}
+end:
+	close(fd);
+	return ret;
+}
diff --git a/config.h b/config.h
index 394ac70..dce385b 100644
--- a/config.h
+++ b/config.h
@@ -15,19 +15,22 @@
  */
 
 #define CREATION_DATE "Thu Jan 1 1970 at 00:00:00 UTC"
+#define INFO "The best IRC server ever \\o/"
 #define IRCD_VERSION "0.1.0"
 
-#define PARAM_MAX 15
+#define INFO_MAX 64
+#define CREATION_MAX 64
 
+#define PARAM_MAX 15
 #define MESSAGE_MAX 512
-#define PEERS_MAX 512
-#define CHANNELS_MAX 512
+#define PEERS_MAX 100
+#define CHANNELS_MAX 100
 
-#define CHANNEL_USERS_MAX 512
-#define CHANNEL_MODES_MAX 512
+#define CHANNEL_USERS_MAX 100
+#define CHANNEL_MODES_MAX 50
 #define CHANNEL_NAME_MAX 64
 
-#define PEER_CHANNELS_MAX 32
+#define PEER_CHANNELS_MAX 10
 #define PEER_NICK_MAX 16
 #define PEER_USER_MAX 16
 #define PEER_REAL_MAX 32
diff --git a/config.irc b/config.irc
new file mode 100644
index 0000000..9902de4
--- /dev/null
+++ b/config.irc
@@ -0,0 +1,2 @@
+setcreation :Thu Jan 1 1970 at 00:00:00 UTC

+setinfo :The best IRC server ever \o/

diff --git a/handle.c b/handle.c
index 8e91404..9e8cb5f 100644
--- a/handle.c
+++ b/handle.c
@@ -282,6 +282,28 @@ quit(struct Message *msg, struct Peer *peer)
 }
 
 static int
+setcreation(struct Message *msg, struct Peer *peer)
+{
+	ensure(peer->type == CONFIG, reply(peer, 481, "You're not a config file"), 0);
+	ensure(msg->params[0] && *msg->params[0], reply(peer, 461), 0);
+
+	strlcpy(creation, msg->params[0], sizeof(creation));
+
+	return 0;
+}
+
+static int
+setinfo(struct Message *msg, struct Peer *peer)
+{
+	ensure(peer->type == CONFIG, reply(peer, 481, "You're not a config file"), 0);
+	ensure(msg->params[0] && *msg->params[0], reply(peer, 461), 0);
+
+	strlcpy(info, msg->params[0], sizeof(info));
+
+	return 0;
+}
+
+static int
 user(struct Message *msg, struct Peer *peer)
 {
 	size_t i;
@@ -304,6 +326,45 @@ user(struct Message *msg, struct Peer *peer)
 }
 
 static int
+whois(struct Message *msg, struct Peer *peer)
+{
+	size_t i;
+
+	ensure(peer->type, reply(peer, 451), 0);
+	ensure(msg->params[0] && *msg->params[0], reply(peer, 431), 0);
+
+	for (i = 0; i < peers_c; ++i)
+		if (!strcmp(msg->params[0], peers[i].nick))
+			break;
+	ensure(i != peers_c, reply(peer, 401, msg->params[0]), 0);
+
+	reply(
+		peer,
+		311,
+		peers[i].nick,
+		peers[i].user,
+		peers[i].host,
+		peers[i].real
+	);
+	reply(
+		peer,
+		312,
+		peers[i].nick,
+		hostname,
+		info
+	);
+	if (peers[i].modes & OPER)
+		reply(
+			peer,
+			313,
+			peers[i].nick
+		);
+	reply(peer, 318, peer[i].nick);
+
+	return 0;
+}
+
+static int
 default_handler(struct Message *msg, struct Peer *peer)
 {
 	ensure(peer->type, (void)0, 0);
@@ -326,7 +387,10 @@ static struct Handler {
 	{ "pong", pong },
 	{ "privmsg", privmsg },
 	{ "quit", quit },
+	{ "setcreation", setcreation },
+	{ "setinfo", setinfo },
 	{ "user", user },
+	{ "whois", whois },
 };
 
 Handler *
diff --git a/ircd.h b/ircd.h
index 0cde9e0..1f5bba8 100644
--- a/ircd.h
+++ b/ircd.h
@@ -38,7 +38,7 @@ struct Message
 struct Peer
 {
 	int fd;
-	enum ClientType { UNREGD, CLIENT, SERVER, SERVICE } type;
+	enum ClientType { UNREGD, CLIENT, CONFIG, SERVER, SERVICE } type;
 	char nick[PEER_NICK_MAX], user[PEER_USER_MAX], real[PEER_REAL_MAX], host[PEER_HOST_MAX];
 	struct Channel *channels[PEER_CHANNELS_MAX];
 	enum PeerStatus {
@@ -80,6 +80,10 @@ extern size_t channels_c, peers_c;
 extern const char *hostname;
 extern const char *host;
 extern unsigned long port;
+extern char creation[CREATION_MAX];
+extern char info[INFO_MAX];
+
+int readcfg(const char *path);
 
 const char *getnick(const struct Peer *peer);
 enum PeerMode user_mode(char m);
diff --git a/main.c b/main.c
index 324309f..3c364a1 100644
--- a/main.c
+++ b/main.c
@@ -37,7 +37,7 @@ main(int argc, char **argv)
 
 	for (i = 1; i < 4; ++i)
 		if (!argv[i] || !*argv[i])
-			errx(1, "usage: %s hostname bindaddr port", argv[0]);
+			errx(1, "usage: %s hostname bindaddr port [config]", argv[0]);
 	hostname = argv[1];
 	host = argv[2];
 	port = strtoul(argv[3], &p, 10);
@@ -45,9 +45,20 @@ main(int argc, char **argv)
 		errx(1, "invalid port");
 
 #ifdef __OpenBSD__
-	if (pledge("stdio inet", ""))
+	if (argv[4] && unveil(argv[4], "r"))
+		err(1, "unveil()");
+	if (pledge("stdio inet rpath unveil", ""))
 		err(1, "pledge()");
 #endif /* __OpenBSD__ */
 
+	if (argv[4])
+		readcfg(argv[4]);
+
+#ifdef __OpenBSD__
+	if (argv[4] && (unveil(argv[4], "") || unveil(NULL, NULL)))
+		err(1, "unveil()");
+	if (pledge("stdio inet", ""))
+		err(1, "pledge()");
+#endif /* __OpenBSD__ */
 	ircd();
 }
diff --git a/reply.c b/reply.c
index 2cd8c04..a295da8 100644
--- a/reply.c
+++ b/reply.c
@@ -81,9 +81,10 @@ vreply(const struct Peer *peer, int number, va_list ap)
 		hostname
 	), _);
 	REPLY(3, WRITE(
-		":%s 003 %s :This server was created " CREATION_DATE,
+		":%s 003 %s :This server was created %s",
 		hostname,
-		getnick(peer)
+		getnick(peer),
+		creation
 	), _);
 	REPLY(4, WRITE(
 		":%s 004 %s :%s libreircd-" IRCD_VERSION " aiwroOs :",
@@ -103,6 +104,35 @@ vreply(const struct Peer *peer, int number, va_list ap)
 		peer->modes & LOCALOPER ? "O" : "",
 		peer->modes & SNOTICE ? "s" : ""
 	), _);
+	REPLY(311, WRITE(
+		":%s 311 %s %s %s %s * :%s",
+		hostname,
+		getnick(peer),
+		nick,
+		user,
+		host,
+		real
+	), nick, user, host, real, _);
+	REPLY(312, WRITE(
+		":%s 312 %s %s %s :%s",
+		hostname,
+		getnick(peer),
+		nick,
+		server,
+		info
+	), nick, server, info, _);
+	REPLY(313, WRITE(
+		":%s 313 %s %s :is an IRC operator",
+		hostname,
+		getnick(peer),
+		nick
+	), nick, _);
+	REPLY(318, WRITE(
+		":%s 318 %s %s :End of WHOIS list",
+		hostname,
+		getnick(peer),
+		nick
+	), nick, _);
 	REPLY(401, WRITE(
 		":%s 401 %s %s :No such nick/channel",
 		hostname,
@@ -187,6 +217,12 @@ vreply(const struct Peer *peer, int number, va_list ap)
 		getnick(peer),
 		channel
 	), channel, _);
+	REPLY(481, WRITE(
+		":%s 481 %s :Permission Denied- %s",
+		hostname,
+		getnick(peer),
+		reason
+	), reason, _);
 	REPLY(484, WRITE(
 		":%s 484 %s :Your connection is restricted!",
 		hostname,