Added logging, repaired server.

This commit is contained in:
retoor 2025-05-06 15:52:54 +02:00
parent 020138aa7f
commit ea43985f79

View File

@ -71,14 +71,14 @@ static int epoll_fd;
static char *password = NULL; static char *password = NULL;
static int logging_enabled = 0; static int logging_enabled = 0;
static void logmsg(const char *fmt, ...) { static void log_event(const char *fmt, ...) {
if (!logging_enabled) return; if (!logging_enabled) return;
char timebuf[32];
time_t now = time(NULL); time_t now = time(NULL);
struct tm tm; struct tm tm;
char tbuf[32];
gmtime_r(&now, &tm); gmtime_r(&now, &tm);
strftime(tbuf, sizeof(tbuf), "%Y-%m-%dT%H:%M:%SZ", &tm); strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%H:%M:%SZ", &tm);
fprintf(stderr, "%s ", tbuf); fprintf(stderr, "%s ", timebuf);
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
@ -167,7 +167,7 @@ static void add_client_to_channel(Client *c, Channel *ch) {
c->channels = realloc(c->channels, c->channel_capacity * sizeof(Channel*)); c->channels = realloc(c->channels, c->channel_capacity * sizeof(Channel*));
} }
c->channels[c->channel_count++] = ch; c->channels[c->channel_count++] = ch;
logmsg("User '%s' joined channel '%s'", c->nickname[0]?c->nickname:"<unreg>", ch->name); log_event("user joined: %s (%s) joined channel %s", c->nickname[0]?c->nickname:"<unreg>", c->host, ch->name);
} }
static void remove_client_from_channel(Client *c, Channel *ch) { static void remove_client_from_channel(Client *c, Channel *ch) {
@ -176,6 +176,7 @@ static void remove_client_from_channel(Client *c, Channel *ch) {
for (int j = i; j < ch->member_count-1; ++j) for (int j = i; j < ch->member_count-1; ++j)
ch->members[j] = ch->members[j+1]; ch->members[j] = ch->members[j+1];
ch->member_count--; ch->member_count--;
log_event("user left: %s (%s) left channel %s", c->nickname[0]?c->nickname:"<unreg>", c->host, ch->name);
break; break;
} }
} }
@ -187,7 +188,6 @@ static void remove_client_from_channel(Client *c, Channel *ch) {
break; break;
} }
} }
logmsg("User '%s' left channel '%s'", c->nickname[0]?c->nickname:"<unreg>", ch->name);
} }
static Client *find_client_by_nick(const char *nick) { static Client *find_client_by_nick(const char *nick) {
@ -212,23 +212,23 @@ static void broadcast_channel(Channel *ch, Client *from, const char *fmt, ...) {
static void send_motd(Client *c) { static void send_motd(Client *c) {
FILE *f = fopen(MOTD_FILE, "r"); FILE *f = fopen(MOTD_FILE, "r");
if (!f) { if (!f) {
send_reply(c, "%s :MOTD File is missing", c->nickname[0]?c->nickname:"*"); send_reply(c, "422 %s :MOTD File is missing", c->nickname[0]?c->nickname:"*");
return; return;
} }
send_reply(c, "%s :- %s Message of the day -", c->nickname, SERVER_NAME); send_reply(c, "375 %s :- %s Message of the day -", c->nickname, SERVER_NAME);
char line[256]; char line[256];
while (fgets(line, sizeof(line), f)) { while (fgets(line, sizeof(line), f)) {
line[strcspn(line, "\r\n")] = 0; line[strcspn(line, "\r\n")] = 0;
send_reply(c, "%s :- %s", c->nickname, line); send_reply(c, "372 %s :- %s", c->nickname, line);
} }
send_reply(c, "%s :End of /MOTD command", c->nickname); send_reply(c, "376 %s :End of /MOTD command", c->nickname);
fclose(f); fclose(f);
} }
static void send_lusers(Client *c) { static void send_lusers(Client *c) {
int count = 0; int count = 0;
for (Client *cl = clients; cl; cl = cl->next) count++; for (Client *cl = clients; cl; cl = cl->next) count++;
send_reply(c, "%s :There are %d users and 0 services on 1 server", c->nickname, count); send_reply(c, "251 %s :There are %d users and 0 services on 1 server", c->nickname, count);
} }
static void send_names(Client *c, Channel *ch) { static void send_names(Client *c, Channel *ch) {
@ -238,50 +238,53 @@ static void send_names(Client *c, Channel *ch) {
strncat(names, ch->members[i]->nickname, sizeof(names) - strlen(names) - 1); strncat(names, ch->members[i]->nickname, sizeof(names) - strlen(names) - 1);
if (i < ch->member_count-1 && strlen(names) + 2 < sizeof(names)) strncat(names, " ", sizeof(names) - strlen(names) - 1); if (i < ch->member_count-1 && strlen(names) + 2 < sizeof(names)) strncat(names, " ", sizeof(names) - strlen(names) - 1);
} }
send_reply(c, "%s = %s :%s", c->nickname, ch->name, names); send_reply(c, "353 %s = %s :%s", c->nickname, ch->name, names);
send_reply(c, "%s %s :End of NAMES list", c->nickname, ch->name); send_reply(c, "366 %s %s :End of NAMES list", c->nickname, ch->name);
} }
static void send_list(Client *c, Channel *ch) { static void send_list(Client *c, Channel *ch) {
send_reply(c, "%s %s %d :%s", c->nickname, ch->name, ch->member_count, ch->topic[0]?ch->topic:""); send_reply(c, "322 %s %s %d :%s", c->nickname, ch->name, ch->member_count, ch->topic[0]?ch->topic:"");
} }
static void handle_registration(Client *c) { static void handle_registration(Client *c) {
if (c->registered) return; if (c->registered) return;
if (!c->nickname[0] || !c->user[0]) return; if (!c->nickname[0] || !c->user[0]) return;
c->registered = 1; c->registered = 1;
send_reply(c, "%s %s rirc-%s o o", c->nickname, SERVER_NAME, VERSION); send_reply(c, "001 %s :Hi, welcome to IRC", c->nickname);
send_reply(c, "002 %s :Your host is %s, running version rirc-%s", c->nickname, SERVER_NAME, VERSION);
send_reply(c, "003 %s :This server was created sometime", c->nickname);
send_reply(c, "004 %s %s rirc-%s o o", c->nickname, SERVER_NAME, VERSION);
send_lusers(c); send_lusers(c);
send_motd(c); send_motd(c);
} }
static void handle_line(Client *c, char *line) { static void handle_line(Client *c, char *line) {
logmsg("Message received from '%s': %s", c->nickname[0]?c->nickname:"<unreg>", line); log_event("message received: %s (%s): %s", c->nickname[0]?c->nickname:"<unreg>", c->host, line);
char *cmd = strtok(line, " "); char *cmd = strtok(line, " ");
if (!cmd) return; if (!cmd) return;
if (strcasecmp(cmd, "NICK") == 0) { if (strcasecmp(cmd, "NICK") == 0) {
char *nick = strtok(NULL, " "); char *nick = strtok(NULL, " ");
if (!nick) { send_reply(c, ":No nickname given"); return; } if (!nick) { send_reply(c, "431 :No nickname given"); return; }
if (find_client_by_nick(nick)) { send_reply(c, "* %s :Nickname is already in use", nick); return; } if (find_client_by_nick(nick)) { send_reply(c, "433 * %s :Nickname is already in use", nick); return; }
strncpy(c->nickname, nick, MAX_NICKLEN); strncpy(c->nickname, nick, MAX_NICKLEN);
handle_registration(c); handle_registration(c);
} else if (strcasecmp(cmd, "USER") == 0) { } else if (strcasecmp(cmd, "USER") == 0) {
char *user = strtok(NULL, " "); char *user = strtok(NULL, " ");
strtok(NULL, " "); strtok(NULL, " "); strtok(NULL, " "); strtok(NULL, " ");
char *realname = strtok(NULL, ":"); char *realname = strtok(NULL, ":");
if (!user || !realname) { send_reply(c, "* USER :Not enough parameters"); return; } if (!user || !realname) { send_reply(c, "461 * USER :Not enough parameters"); return; }
strncpy(c->user, user, MAX_USERLEN); strncpy(c->user, user, MAX_USERLEN);
strncpy(c->realname, realname, MAX_USERLEN); strncpy(c->realname, realname, MAX_USERLEN);
handle_registration(c); handle_registration(c);
} else if (strcasecmp(cmd, "PASS") == 0) { } else if (strcasecmp(cmd, "PASS") == 0) {
char *pass = strtok(NULL, " "); char *pass = strtok(NULL, " ");
if (!password) { c->pass_ok = 1; return; } if (!password) { c->pass_ok = 1; return; }
if (!pass) { send_reply(c, "* PASS :Not enough parameters"); return; } if (!pass) { send_reply(c, "461 * PASS :Not enough parameters"); return; }
if (strcmp(pass, password) == 0) c->pass_ok = 1; if (strcmp(pass, password) == 0) c->pass_ok = 1;
else send_reply(c, ":Password incorrect"); else send_reply(c, "464 :Password incorrect");
} else if (strcasecmp(cmd, "PING") == 0) { } else if (strcasecmp(cmd, "PING") == 0) {
char *arg = strtok(NULL, " "); char *arg = strtok(NULL, " ");
if (!arg) { send_reply(c, "%s :No origin specified", c->nickname); return; } if (!arg) { send_reply(c, "409 %s :No origin specified", c->nickname); return; }
send_reply(c, "PONG %s :%s", SERVER_NAME, arg); send_reply(c, "PONG %s :%s", SERVER_NAME, arg);
} else if (strcasecmp(cmd, "PONG") == 0) { } else if (strcasecmp(cmd, "PONG") == 0) {
c->sent_ping = 0; c->sent_ping = 0;
@ -291,57 +294,57 @@ static void handle_line(Client *c, char *line) {
close(c->fd); c->fd = -1; close(c->fd); c->fd = -1;
} else if (strcasecmp(cmd, "JOIN") == 0) { } else if (strcasecmp(cmd, "JOIN") == 0) {
char *chan = strtok(NULL, " "); char *chan = strtok(NULL, " ");
if (!chan) { send_reply(c, "%s JOIN :Not enough parameters", c->nickname); return; } if (!chan) { send_reply(c, "461 %s JOIN :Not enough parameters", c->nickname); return; }
Channel *ch = get_or_create_channel(chan); Channel *ch = get_or_create_channel(chan);
add_client_to_channel(c, ch); add_client_to_channel(c, ch);
send_raw(c, ":%s!%s@%s JOIN %s", c->nickname, c->user, c->host, ch->name); send_raw(c, ":%s!%s@%s JOIN %s", c->nickname, c->user, c->host, ch->name);
if (ch->topic[0]) if (ch->topic[0])
send_reply(c, "%s %s :%s", c->nickname, ch->name, ch->topic); send_reply(c, "332 %s %s :%s", c->nickname, ch->name, ch->topic);
else else
send_reply(c, "%s %s :No topic is set", c->nickname, ch->name); send_reply(c, "331 %s %s :No topic is set", c->nickname, ch->name);
send_names(c, ch); send_names(c, ch);
} else if (strcasecmp(cmd, "PART") == 0) { } else if (strcasecmp(cmd, "PART") == 0) {
char *chan = strtok(NULL, " "); char *chan = strtok(NULL, " ");
Channel *ch = find_channel(chan); Channel *ch = find_channel(chan);
if (!ch) { send_reply(c, "%s %s :No such channel", c->nickname, chan); return; } if (!ch) { send_reply(c, "403 %s %s :No such channel", c->nickname, chan); return; }
remove_client_from_channel(c, ch); remove_client_from_channel(c, ch);
send_raw(c, ":%s!%s@%s PART %s", c->nickname, c->user, c->host, ch->name); send_raw(c, ":%s!%s@%s PART %s", c->nickname, c->user, c->host, ch->name);
} else if (strcasecmp(cmd, "PRIVMSG") == 0 || strcasecmp(cmd, "NOTICE") == 0) { } else if (strcasecmp(cmd, "PRIVMSG") == 0 || strcasecmp(cmd, "NOTICE") == 0) {
char *target = strtok(NULL, " "); char *target = strtok(NULL, " ");
char *msg = strtok(NULL, ":"); char *msg = strtok(NULL, ":");
if (!target) { send_reply(c, "%s :No recipient given (%s)", c->nickname, cmd); return; } if (!target) { send_reply(c, "411 %s :No recipient given (%s)", c->nickname, cmd); return; }
if (!msg) { send_reply(c, "%s :No text to send", c->nickname); return; } if (!msg) { send_reply(c, "412 %s :No text to send", c->nickname); return; }
Client *dest = find_client_by_nick(target); Client *dest = find_client_by_nick(target);
if (dest) send_raw(dest, ":%s!%s@%s %s %s :%s", c->nickname, c->user, c->host, cmd, target, msg); if (dest) send_raw(dest, ":%s!%s@%s %s %s :%s", c->nickname, c->user, c->host, cmd, target, msg);
else { else {
Channel *ch = find_channel(target); Channel *ch = find_channel(target);
if (ch) broadcast_channel(ch, c, ":%s!%s@%s %s %s :%s", c->nickname, c->user, c->host, cmd, target, msg); if (ch) broadcast_channel(ch, c, ":%s!%s@%s %s %s :%s", c->nickname, c->user, c->host, cmd, target, msg);
else send_reply(c, "%s %s :No such nick/channel", c->nickname, target); else send_reply(c, "401 %s %s :No such nick/channel", c->nickname, target);
} }
} else if (strcasecmp(cmd, "TOPIC") == 0) { } else if (strcasecmp(cmd, "TOPIC") == 0) {
char *chan = strtok(NULL, " "); char *chan = strtok(NULL, " ");
Channel *ch = find_channel(chan); Channel *ch = find_channel(chan);
if (!ch) { send_reply(c, "%s %s :No such channel", c->nickname, chan); return; } if (!ch) { send_reply(c, "403 %s %s :No such channel", c->nickname, chan); return; }
char *topic = strtok(NULL, ":"); char *topic = strtok(NULL, ":");
if (topic) { if (topic) {
strncpy(ch->topic, topic, MAX_TOPICLEN); strncpy(ch->topic, topic, MAX_TOPICLEN);
broadcast_channel(ch, NULL, ":%s!%s@%s TOPIC %s :%s", c->nickname, c->user, c->host, ch->name, ch->topic); broadcast_channel(ch, NULL, ":%s!%s@%s TOPIC %s :%s", c->nickname, c->user, c->host, ch->name, ch->topic);
} else { } else {
if (ch->topic[0]) if (ch->topic[0])
send_reply(c, "%s %s :%s", c->nickname, ch->name, ch->topic); send_reply(c, "332 %s %s :%s", c->nickname, ch->name, ch->topic);
else else
send_reply(c, "%s %s :No topic is set", c->nickname, ch->name); send_reply(c, "331 %s %s :No topic is set", c->nickname, ch->name);
} }
} else if (strcasecmp(cmd, "LIST") == 0) { } else if (strcasecmp(cmd, "LIST") == 0) {
for (Channel *ch = channels; ch; ch = ch->next) for (Channel *ch = channels; ch; ch = ch->next)
send_list(c, ch); send_list(c, ch);
send_reply(c, "%s :End of LIST", c->nickname); send_reply(c, "323 %s :End of LIST", c->nickname);
} else if (strcasecmp(cmd, "NAMES") == 0) { } else if (strcasecmp(cmd, "NAMES") == 0) {
char *chan = strtok(NULL, " "); char *chan = strtok(NULL, " ");
if (chan) { if (chan) {
Channel *ch = find_channel(chan); Channel *ch = find_channel(chan);
if (ch) send_names(c, ch); if (ch) send_names(c, ch);
else send_reply(c, "%s %s :No such channel", c->nickname, chan); else send_reply(c, "403 %s %s :No such channel", c->nickname, chan);
} else { } else {
for (int i = 0; i < c->channel_count; ++i) for (int i = 0; i < c->channel_count; ++i)
send_names(c, c->channels[i]); send_names(c, c->channels[i]);
@ -349,20 +352,20 @@ static void handle_line(Client *c, char *line) {
} else if (strcasecmp(cmd, "MODE") == 0) { } else if (strcasecmp(cmd, "MODE") == 0) {
char *chan = strtok(NULL, " "); char *chan = strtok(NULL, " ");
Channel *ch = find_channel(chan); Channel *ch = find_channel(chan);
if (!ch) { send_reply(c, "%s %s :No such channel", c->nickname, chan); return; } if (!ch) { send_reply(c, "403 %s %s :No such channel", c->nickname, chan); return; }
char *flag = strtok(NULL, " "); char *flag = strtok(NULL, " ");
if (!flag) { if (!flag) {
send_reply(c, "%s %s +", c->nickname, ch->name); send_reply(c, "324 %s %s +", c->nickname, ch->name);
} else if (strcmp(flag, "+k") == 0) { } else if (strcmp(flag, "+k") == 0) {
char *key = strtok(NULL, " "); char *key = strtok(NULL, " ");
if (!key) { send_reply(c, "%s MODE :Not enough parameters", c->nickname); return; } if (!key) { send_reply(c, "461 %s MODE :Not enough parameters", c->nickname); return; }
strncpy(ch->key, key, MAX_CHANNELLEN); strncpy(ch->key, key, MAX_CHANNELLEN);
broadcast_channel(ch, NULL, ":%s!%s@%s MODE %s +k %s", c->nickname, c->user, c->host, ch->name, key); broadcast_channel(ch, NULL, ":%s!%s@%s MODE %s +k %s", c->nickname, c->user, c->host, ch->name, key);
} else if (strcmp(flag, "-k") == 0) { } else if (strcmp(flag, "-k") == 0) {
ch->key[0] = 0; ch->key[0] = 0;
broadcast_channel(ch, NULL, ":%s!%s@%s MODE %s -k", c->nickname, c->user, c->host, ch->name); broadcast_channel(ch, NULL, ":%s!%s@%s MODE %s -k", c->nickname, c->user, c->host, ch->name);
} else { } else {
send_reply(c, "%s %s :Unknown MODE flag", c->nickname, flag); send_reply(c, "472 %s %s :Unknown MODE flag", c->nickname, flag);
} }
} else if (strcasecmp(cmd, "MOTD") == 0) { } else if (strcasecmp(cmd, "MOTD") == 0) {
send_motd(c); send_motd(c);
@ -379,33 +382,33 @@ static void handle_line(Client *c, char *line) {
} }
nick = strtok(NULL, " "); nick = strtok(NULL, " ");
} }
send_reply(c, "%s :%s", c->nickname, buf); send_reply(c, "303 %s :%s", c->nickname, buf);
} else if (strcasecmp(cmd, "WHO") == 0) { } else if (strcasecmp(cmd, "WHO") == 0) {
char *chan = strtok(NULL, " "); char *chan = strtok(NULL, " ");
Channel *ch = find_channel(chan); Channel *ch = find_channel(chan);
if (ch) { if (ch) {
for (int i = 0; i < ch->member_count; ++i) { for (int i = 0; i < ch->member_count; ++i) {
Client *m = ch->members[i]; Client *m = ch->members[i];
send_reply(c, "%s %s %s %s %s %s H :0 %s", c->nickname, ch->name, m->user, m->host, SERVER_NAME, m->nickname, m->realname); send_reply(c, "352 %s %s %s %s %s %s H :0 %s", c->nickname, ch->name, m->user, m->host, SERVER_NAME, m->nickname, m->realname);
} }
send_reply(c, "%s %s :End of WHO list", c->nickname, ch->name); send_reply(c, "315 %s %s :End of WHO list", c->nickname, ch->name);
} }
} else if (strcasecmp(cmd, "WHOIS") == 0) { } else if (strcasecmp(cmd, "WHOIS") == 0) {
char *nick = strtok(NULL, " "); char *nick = strtok(NULL, " ");
Client *u = find_client_by_nick(nick); Client *u = find_client_by_nick(nick);
if (u) { if (u) {
send_reply(c, "%s %s %s %s * :%s", c->nickname, u->nickname, u->user, u->host, u->realname); send_reply(c, "311 %s %s %s %s * :%s", c->nickname, u->nickname, u->user, u->host, u->realname);
send_reply(c, "%s %s %s :%s", c->nickname, u->nickname, SERVER_NAME, SERVER_NAME); send_reply(c, "312 %s %s %s :%s", c->nickname, u->nickname, SERVER_NAME, SERVER_NAME);
char buf[MAX_MSG] = ""; char buf[MAX_MSG] = "";
for (int i = 0; i < u->channel_count; ++i) { for (int i = 0; i < u->channel_count; ++i) {
if (strlen(buf) + strlen(u->channels[i]->name) + 2 >= sizeof(buf)) break; if (strlen(buf) + strlen(u->channels[i]->name) + 2 >= sizeof(buf)) break;
strncat(buf, u->channels[i]->name, sizeof(buf) - strlen(buf) - 1); strncat(buf, u->channels[i]->name, sizeof(buf) - strlen(buf) - 1);
strncat(buf, " ", sizeof(buf) - strlen(buf) - 1); strncat(buf, " ", sizeof(buf) - strlen(buf) - 1);
} }
send_reply(c, "%s %s :%s", c->nickname, u->nickname, buf); send_reply(c, "319 %s %s :%s", c->nickname, u->nickname, buf);
send_reply(c, "%s %s :End of WHOIS list", c->nickname, u->nickname); send_reply(c, "318 %s %s :End of WHOIS list", c->nickname, u->nickname);
} else { } else {
send_reply(c, "%s %s :No such nick", c->nickname, nick); send_reply(c, "401 %s %s :No such nick", c->nickname, nick);
} }
} else if (strcasecmp(cmd, "AWAY") == 0) { } else if (strcasecmp(cmd, "AWAY") == 0) {
} else if (strcasecmp(cmd, "WALLOPS") == 0) { } else if (strcasecmp(cmd, "WALLOPS") == 0) {
@ -422,7 +425,7 @@ static void handle_line(Client *c, char *line) {
send_raw(c, "CAP * NAK :%s", arg ? arg : ""); send_raw(c, "CAP * NAK :%s", arg ? arg : "");
} }
} else { } else {
send_reply(c, "%s %s :Unknown command", c->nickname, cmd); send_reply(c, "421 %s %s :Unknown command", c->nickname, cmd);
} }
} }
@ -436,7 +439,7 @@ static void remove_client(Client *c) {
while (prev && prev->next != c) prev = prev->next; while (prev && prev->next != c) prev = prev->next;
if (prev) prev->next = c->next; if (prev) prev->next = c->next;
} }
logmsg("User '%s' disconnected", c->nickname[0]?c->nickname:"<unreg>"); log_event("user disconnected: %s (%s)", c->nickname[0]?c->nickname:"<unreg>", c->host);
del_epoll(c->fd); del_epoll(c->fd);
if (c->fd >= 0) close(c->fd); if (c->fd >= 0) close(c->fd);
c->fd = -1; c->fd = -1;
@ -456,26 +459,28 @@ static void accept_new_client() {
c->next = clients; c->next = clients;
clients = c; clients = c;
add_epoll(fd, EPOLLIN, c); add_epoll(fd, EPOLLIN, c);
logmsg("User connected from %s (fd=%d)", c->host, fd); log_event("user connected: %s", c->host);
}
static int parse_args(int argc, char **argv, int *port, char **pass, int *log_enabled) {
*port = 6667;
*pass = NULL;
*log_enabled = 0;
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--port") == 0 && i+1 < argc) {
*port = atoi(argv[++i]);
} else if (strcmp(argv[i], "--password") == 0 && i+1 < argc) {
*pass = argv[++i];
} else if (strcmp(argv[i], "--log") == 0) {
*log_enabled = 1;
}
}
return 0;
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
int port = 6667; int port;
int argi = 1; parse_args(argc, argv, &port, &password, &logging_enabled);
while (argi < argc) {
if (strcmp(argv[argi], "--log") == 0) {
logging_enabled = 1;
argi++;
} else if (argi == 1) {
port = atoi(argv[argi]);
argi++;
} else if (argi == 2) {
password = argv[argi];
argi++;
} else {
argi++;
}
}
listen_fd = socket(AF_INET, SOCK_STREAM, 0); listen_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1; int opt = 1;