diff --git a/CMakeLists.txt b/CMakeLists.txt index e73bddf..d4c62c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,16 @@ cmake_minimum_required(VERSION 3.16) project( uIRCd - VERSION 2021.01.19 + VERSION 2021.01.24 DESCRIPTION "High performance IRC daemon based on uIRC" LANGUAGES C ) set(UIRCD_VERSION "${PROJECT_VERSION}") add_compile_definitions(UIRCD_VERSION="${UIRCD_VERSION}") +add_compile_definitions(UIRC_HELPERS UIRC_IRCV3) OPTION(BUILD_LIBCONFIG "Build support for configurations" ON ) -OPTION(UIRCD_RELAXED_RFC "Build without following RFC2812 entirely" OFF) # This allows features like a dynamic buffer and more to exist OPTION(CODE_ANALYZER "Analyze the code statically" OFF) OPTION(CODE_COVERAGE "Build with coverage tools" OFF) @@ -19,7 +19,6 @@ add_executable(uircd src/configuration.c src/connection.c src/filesystem.c - src/logging.c src/main.c src/memory.c src/misc.c @@ -43,40 +42,14 @@ endif() set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Werror") if (CMAKE_C_COMPILER_ID STREQUAL "GNU") - add_compile_options( - -Wall - -Wextra - -Wformat-overflow=2 - -Wformat-security - -Winit-self - -Wstrict-overflow=2 - -Wstringop-overflow=2 - -Walloc-zero - -Wduplicated-branches - -Wduplicated-cond - -Wtrampolines - -Wfloat-equal - -Wshadow - -Wunsafe-loop-optimizations - -Wparentheses - -fstack-check - -pedantic - ) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat-overflow=2 -Wformat-security -Winit-self -Wstrict-overflow=2 -Wstringop-overflow=2 -Walloc-zero -Wduplicated-branches -Wduplicated-cond -Wtrampolines -Wfloat-equal -Wshadow -Wunsafe-loop-optimizations -Wparentheses -fstack-check -pedantic") if (CODE_ANALYZER) - add_compile_options(-fanalyzer) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fanalyzer") endif() elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") - add_compile_options( - -Weverything - -Wno-padded - -Wno-disabled-macro-expansion - -pedantic - ) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Weverything -Wno-padded -Wno-disabled-macro-expansion -pedantic") if (CODE_COVERAGE) - add_compile_options(-fprofile-instr-generate -fcoverage-mapping) - endif() - if (CODE_ANALYZER) - add_compile_options(--analyze) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-instr-generate -fcoverage-mapping") endif() endif() diff --git a/README.md b/README.md index 5adfd3b..277ae62 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ cmake -B build/ -DCMAKE_BUILD_TYPE=Release . | Option | Description | Type | Default | Supported since | |:------------------:|:----------------------------------------------------------------------:|:--------:|:-------:|:---------------:| | BUILD_LIBCONFIG | Build and link against libconfig | boolean | true | 2021.01.04 | -| UIRCD_RELAXED_RFC |Don't enforce strict RFC2812. Allows dynamic buffers and more features. | boolean | false | 2021.01.12 | | CODE_ANALYZER | Use static analysis tools | boolean | false | 2020.10.30 | | CODE_COVERAGE | Generate code coverage output | boolean | false | 2020.10.30 | diff --git a/src/buffer.c b/src/buffer.c index fdebec7..aca0fd0 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -29,38 +29,39 @@ #include // write() ssize_t -read_buffer(Buffer_Info* binfo) +read_buffer(char* dest, int fd, size_t len) { - assert(binfo != NULL); + assert(dest != NULL); + if (len == 0) return 0; ssize_t brd; - if ((brd = read(binfo->fd, binfo->buffer + binfo->data_size, binfo->buf_size - binfo->data_size - 1)) > 0) { - *(binfo->buffer + (binfo->data_size += (size_t) brd)) = '\0'; - LOG(LOG_DEBUG, "Read %li bytes from socket buffer and currently at position %li", brd, binfo->data_size); + if ((brd = read(fd, dest, len)) > 0) { + *(dest + brd) = '\0'; + LOG(LOG_DEBUG, "Read %li bytes from fd buffer %i", brd, fd); } else if (brd == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) return 0; - LOG(LOG_WARNING, "Failed to read inbound traffic on fd %i: " ERRNOFMT, binfo->fd, strerror(errno), errno); + LOG(LOG_WARNING, "Failed to read buffer on fd %i: " ERRNOFMT, fd, strerror(errno), errno); } return brd; } ssize_t -write_buffer(Buffer_Info* binfo) +write_buffer(const char* src, int fd, size_t len, bool fullflush) { - assert(binfo != NULL); + assert(src != NULL); + if (len == 0) return 0; ssize_t res; - size_t pos = 0; - for (;;) { - size_t left = binfo->data_size - pos; - if ((size_t)(res = write(binfo->fd, binfo->buffer + pos, left)) != left) { - if (res == -1 && errno != EINTR) { - LOG(LOG_WARNING, "Couldn't flush buffer to fd %i: " ERRNOFMT, binfo->fd, strerror(errno), errno); - return -1; - } - pos += res; - LOG(LOG_DEBUG, "Wrote %lu bytes to fd %i", res, binfo->fd); - } else + size_t b_wr = 0; + do { + if ((res = write(fd, src + b_wr, len - b_wr)) > 0) { + LOG(LOG_DEBUG, "Wrote %lu bytes to fd %i", res, len); + b_wr += (size_t) res; + } else if (res == -1) { + if (!fullflush && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) return 0; + LOG(LOG_WARNING, "Couldn't flush buffer to fd %i: " ERRNOFMT, fd, strerror(errno), errno); return res; - } + } + } while (fullflush && b_wr < len); + return (ssize_t) b_wr; } /*! @@ -71,36 +72,18 @@ write_buffer(Buffer_Info* binfo) * This is done because strcpy may be optimized and copy order is not defined, so destination might overwrite source on a rtl copy */ size_t -buffer_offset_move(Buffer_Info* binfo, size_t offset) +dequeue_bytes(char* str, size_t cnt, size_t len) { - assert(binfo != NULL); + assert(str != NULL); + assert(cnt <= len); size_t pos = 0; do { - char* dest = binfo->buffer + pos; - size_t b_left = binfo->data_size - pos - offset; - strncpy(dest, dest + offset, (b_left < offset) ? b_left : offset); - LOG(LOG_DEBUG, "Moved %lu bytes from %lu to %lu", (b_left < offset) ? b_left : offset, offset + pos, pos); - pos += offset; - } while (offset + pos < binfo->data_size); - binfo->data_size -= offset; - return binfo->data_size; + char* dest = str + pos; + size_t b_left = len - cnt - pos; + strncpy(dest, dest + cnt, (b_left < cnt) ? b_left : cnt); + LOG(LOG_DEBUG, "Moved %lu bytes from %lu to %lu", (b_left < cnt) ? b_left : cnt, cnt + pos, pos); + pos += cnt; + } while (cnt + pos < len); + return cnt; } -int -reset_buffer(Buffer_Info* binfo, short clr_fd) -{ - if (clr_fd) { - if (clr_fd == 2) close(binfo->fd); - binfo->fd = -1; - } -#ifdef UIRCD_RELAXED_RFC - if (binfo->buffer != NULL) free(binfo->buffer); - binfo->buffer = NULL; - binfo->buf_size = 0; -#else /* UIRCD_RELAXED_RFC */ - *binfo->buffer = '\0'; - binfo->buf_size = UIRCD_LIMITS_LINE + 1; -#endif /* UIRCD_RELAXED_RFC */ - binfo->data_size = 0; - return 1; -} diff --git a/src/buffer.h b/src/buffer.h index b4aa7e7..7feef7c 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -24,21 +24,9 @@ #ifndef UIRCD_GUARD_BUFFER #define UIRCD_GUARD_BUFFER -typedef struct { - char -#ifdef UIRCD_RELAXED_RFC - * buffer -#else /* UIRCD_RELAXED_RFC */ - buffer[UIRCD_LIMITS_LINE + 1] -#endif /* UIRCD_RELAXED_RFC */ - ; - size_t data_size, buf_size; - int fd; -} Buffer_Info; - -ssize_t read_buffer(Buffer_Info* binfo); -ssize_t write_buffer(Buffer_Info* binfo); -size_t buffer_offset_move(Buffer_Info* binfo, size_t offset); -int reset_buffer(Buffer_Info* binfo, short clr_fd); +ssize_t read_buffer(char* dest, int fd, size_t len); +ssize_t write_buffer(const char* src, int fd, size_t len, bool fullflush); +size_t dequeue_bytes(char* str, size_t cnt, size_t len); #endif /* UIRCD_GUARD_BUFFER */ + diff --git a/src/connection.c b/src/connection.c index 2896dcd..f646ab3 100644 --- a/src/connection.c +++ b/src/connection.c @@ -34,10 +34,8 @@ #include // size_t ssize_t socklen_t #include #include -#include // ?? - -#define UIRC_HELPERS #include // Assm_mesg Assm_cmd... +#include // ?? signed int init_connection(Connection* conn) @@ -68,227 +66,45 @@ init_connection(Connection* conn) return sockfd; } -signed int -get_msgchannel(IRC_Message* mesg) +bool +register_user(char* buf, size_t len, int fd, Connection_User* user) { - // TODO: These message arguments might be parsed differently depending on what command they offer. - // Maybe offering a static context would allow reentrant calls - switch (mesg->cmd) { - case JOIN: - case PART: - case MODE: - case TOPIC: - case LIST: - case NAMES: - case INVITE: - case KICK: - case PRIVMSG: - case NOTICE: { - if (CHANNELMASK(mesg->args[0])) return 0; - break; - } - case RPL_TOPIC: - case RPL_NOTOPIC: - case RPL_ENDOFNAMES: { - if (CHANNELMASK(mesg->args[1])) return 1; - break; - } - case RPL_NAMREPLY: { - if (CHANNELMASK(mesg->args[2])) return 2; - break; - } + ssize_t tmp; + IRC_Message build_buf; + if (user->password != NULL && (tmp = Assm_mesg(buf, Assm_cmd_PASS(&build_buf, user->password), len)) > 0) { + if (write_buffer(buf, fd, (size_t) tmp, true) != -1) { + LOG(LOG_INFO, "Sent PASS authentication"); + } else + return false; } - return -1; + if ((tmp = Assm_mesg(buf, Assm_cmd_NICK(&build_buf, user->nickname), len)) > 0) { + if (write_buffer(buf, fd, (size_t) tmp, true) != -1) { + LOG(LOG_INFO, "Sent NICK registration with nickname \"%s\".", user->nickname); + } else + return false; + } + if ((tmp = Assm_mesg(buf, Assm_cmd_USER(&build_buf, user->username, user->realname, 0), len)) > 0) { + if (write_buffer(buf, fd, (size_t) tmp, true) != -1) { + LOG(LOG_INFO, "Sent USER registration with username \"%s\" and realname \"%s\".", user->username, user->realname); + } else + return false; + } + return true; } -const char* -get_categ(IRC_Message* mesg) +bool +commit_channelist(char* buf, size_t len, int fd, Channel* chans) { - switch (mesg->cmd) { - case JOIN: - case PART: - case QUIT: - case KICK: return "events"; - case TOPIC: - case RPL_TOPIC: - case RPL_NOTOPIC: return "topic"; - case PRIVMSG: - case NOTICE: return "msgs"; - case RPL_MOTD: - case RPL_MOTDSTART: - case RPL_ENDOFMOTD: - case ERR_NOMOTD: return "motd"; - case PING: - case PONG: return "pings"; - case RPL_NAMREPLY: - case RPL_ENDOFNAMES: return "names"; - case RPL_LISTSTART: - case RPL_LIST: - case RPL_LISTEND: - case LIST: return "list"; - default: return NULL; - } -} - -int -auto_msg_actions(IRC_Message* mesg, Connection* conn, Buffer_Info* buf) -{ - signed long len; - time_t ctime = time(NULL); - switch (mesg->cmd) { - case (PING): { - LOG(LOG_DEBUG, "Auto-replying to ping \"%s\"", mesg->args[0]); - if ((buf->data_size = Assm_mesg(buf->buffer, Assm_cmd_PONG(mesg->args[0], NULL), buf->buf_size)) > 0) - if (write_buffer(buf) == -1) { - LOG(LOG_WARNING, "Couldn't pong " ADDRFMT ". " ERRNOFMT, conn->data.address, conn->data.service, strerror(errno), errno); - conn->info.state = CONN_RECONNECTING; - return 0; - } - break; - } - case (PONG): { - if (mesg->trailing && mesg->args[1] != NULL) { LOG(LOG_DEBUG, "Got PONG with mesg \"%s\"", mesg->args[1]); } - break; - } - /* Autojoin channels from current conn on first response from the server */ - case (RPL_WELCOME): { - LOG(LOG_NOTICE, "Connection established to " ADDRFMT, conn->data.address, conn->data.service); - conn->info.state = CONN_ACTIVE; - conn->info.reconinter = 0; - if (!commit_channelist(buf, conn)) return 0; - break; - } - /* TODO: IRCv3 capabilities - case (CAP): { - if (mesg->args[1] != NULL) { - if (strcmp(mesg->args[1], "LS") == 0) { - LOG(LOG_INFO, - "Requesting capabilities \"%s\" on " ADDRFMT ".", - mesg->args[2], - conn->data.address, - conn->data.service); - if ((len = Assm_mesg(buf->buffer, Assm_cmd_CAP_REQ(mesg->args[2]), buf->csize)) > 0) { - if (flush_buffer(buf->buffer, (size_t) len, buf->fd) == -1) { - LOG(LOG_WARNING, - "Couldn't request capabilities \"%s\" on " ADDRFMT ". " ERRNOFMT, - mesg->args[2], - conn->data.address, - conn->data.service, - strerror(errno), - errno); - conn->info.state = CONN_RECONNECTING; - return 0; - } - } - } else if (strcmp(mesg->args[1], "ACK") == 0) { - LOG(LOG_INFO, "Ending capability negotiation on " ADDRFMT ".", conn->data.address, conn->data.service); - if ((len = Assm_mesg(buf->buffer, Assm_cmd_CAP_END(), buf->csize)) > 0) { - if (flush_buffer(buf->buffer, (size_t) len, buf->fd) == -1) { - LOG(LOG_WARNING, - "Couldn't end capability negotiation on " ADDRFMT ". " ERRNOFMT, - conn->data.address, - conn->data.service, - strerror(errno), - errno); - conn->info.state = CONN_RECONNECTING; - return 0; - } - } - } - } - break; - } - */ - case (ERROR): { - LOG(LOG_ERR, "Received error: %s", mesg->args[0]); - break; - } - case (JOIN): - case (PART): { - resize_chanarray(&conn->info.channels); - set_channel(&conn->info.channels[get_channelindex(mesg->args[0], conn->info.channels)], mesg->args[0], mesg->args[1], mesg->cmd == JOIN); - break; - } - case (ERR_NICKNAMEINUSE): - case (ERR_NICKCOLLISION): { - LOG(LOG_WARNING, "Nickname \"%s\" is already taken", conn->user.nickname); - conn->info.state = CONN_RECONNECTING; - break; - } - } - return 1; -} - -int -commit_channelist(Buffer_Info* buf, Connection* conn) -{ - signed long len; - for (unsigned int i = 0; conn->info.channels != NULL && conn->info.channels[i].name != NULL; i++) { - IRC_Message msg = { .args = { conn->info.channels[i].name, conn->info.channels[i].key, NULL }, .trailing = true, .cmd = (conn->info.channels[i].joined) ? JOIN : PART }; - if ((buf->data_size = Assm_mesg(buf->buffer, &msg, buf->buf_size)) > 0) { - if (write_buffer(buf) == -1) { - LOG(LOG_WARNING, - "Couldn't auto-join channels \"%s\" " ADDRFMT ". " ERRNOFMT, - conn->info.channels[i].name, - conn->data.address, - conn->data.service, - strerror(errno), - errno); - conn->info.state = CONN_RECONNECTING; - return 0; + ssize_t tmp; + for (unsigned int i = 0; chans != NULL && chans[i].name != NULL; i++) { + IRC_Message msg = { .args = { chans[i].name, chans[i].key, NULL }, .trailing = true, .cmd = (chans[i].joined) ? JOIN : PART }; + if ((tmp = Assm_mesg(buf, &msg, len)) > 0) { + if (write_buffer(buf, fd, (size_t) tmp, true) == -1) { + LOG(LOG_WARNING, "Couldn't auto-join channel \"%s\": " ERRNOFMT, chans[i].name, strerror(errno), errno); + return false; } } } - return 1; + return true; } -int -register_user(Connection* conn, Buffer_Info* buf) -{ - /* Registration process and CAP negotiation (TODO) - if ((temp = Assm_mesg(buf.send[0].buffer, Assm_cmd_CAP_LS("302"), sizeof(buf.send[0].buffer))) > 0) { - buf.send[0].data_size = (size_t) temp; - LOG(LOG_INFO, "Sending a CAP LS to " ADDRFMT ".", connection.data.address, connection.data.service); - if (flush_buffer(buf.send[0].buffer, buf.send[0].data_size, buf.send[0].fd) == -1) { - LOG(LOG_WARNING, - "Couldn't send CAP LS to " ADDRFMT ". " ERRNOFMT, - connection.data.address, - connection.data.service, - strerror(errno), - errno); - connection.info.state = CONN_RECONNECTING; - continue; - } - } - */ - if (conn->user.password != NULL && (buf->data_size = Assm_mesg(buf->buffer, Assm_cmd_PASS(conn->user.password), buf->buf_size)) > 0) { - if (write_buffer(buf) != -1) { - LOG(LOG_INFO, "Sent PASS authentication to " ADDRFMT, conn->data.address, conn->data.service); - } else { - conn->info.state = CONN_RECONNECTING; - return 0; - } - } - if ((buf->data_size = Assm_mesg(buf->buffer, Assm_cmd_NICK(conn->user.nickname), buf->buf_size)) > 0) { - if (write_buffer(buf) != -1) { - LOG(LOG_INFO, "Sent NICK registration to " ADDRFMT " with nickname \"%s\".", conn->data.address, conn->data.service, conn->user.nickname); - } else { - conn->info.state = CONN_RECONNECTING; - return 0; - } - } - if ((buf->data_size = Assm_mesg(buf->buffer, Assm_cmd_USER(conn->user.username, conn->user.realname, 0), buf->buf_size)) > 0) { - if (write_buffer(buf) != -1) { - LOG(LOG_INFO, - "Sent USER registration to " ADDRFMT " with username \"%s\" and realname \"%s\".", - conn->data.address, - conn->data.service, - conn->user.username, - conn->user.realname); - } else { - conn->info.state = CONN_RECONNECTING; - return 0; - } - } - return 1; -} diff --git a/src/connection.h b/src/connection.h index 0eaa79f..d6b0825 100644 --- a/src/connection.h +++ b/src/connection.h @@ -22,9 +22,6 @@ #include // bool #include // size_t ssize_t socklen_t - -#define UIRC_IRCV3 -#define UIRC_HELPERS #include // Assm_mesg Assm_cmd... #ifndef UIRCD_GUARD_CONNECTION @@ -63,12 +60,9 @@ typedef struct { Connection_Info info; } Connection; -signed int init_connection(Connection* conn); -signed int get_msgchannel(IRC_Message* mesg); -int auto_msg_actions(IRC_Message* mesg, Connection* conn, Buffer_Info* buf); -int commit_channelist(Buffer_Info* buf, Connection* conn); -const char* get_categ(IRC_Message* mesg); -int register_user(Connection* conn, Buffer_Info* buf); +signed int init_connection(Connection* conn); +bool register_user(char* buf, size_t len, int fd, Connection_User* user); +bool commit_channelist(char* buf, size_t len, int fd, Channel* chans); #endif /* UIRCD_GUARD_CONNECTION */ diff --git a/src/filesystem.c b/src/filesystem.c index 6c34b75..09a287d 100644 --- a/src/filesystem.c +++ b/src/filesystem.c @@ -25,7 +25,6 @@ #include // isalpha() isdigit() #include // errno #include // fnctl() -#include // ?? #include // bool #include // fopen() FILE fprintf() #include // strerror() @@ -33,29 +32,6 @@ #include // size_t ssize_t mode_t #include // syslog() -int -mkdir_bottomup(char* path) -{ - if (path == NULL || *path == '\0') return 0; - for (char* x = path; x != NULL && *x;) { - if ((x = strchr(x, '/')) != NULL) { - char save = *(x + 1); - *(x + 1) = '\0'; - if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { - if (errno != EEXIST) { - *(x + 1) = save; - LOG(LOG_ERR, "Could not create directory \"%s\": " ERRNOFMT, path, strerror(errno), errno); - return 0; - } - } else - LOG(LOG_DEBUG, "Created directory at \"%s\"", path); - *(x + 1) = save; - x++; - } - } - return 1; -} - int makeinput(const char* path) { @@ -80,7 +56,7 @@ write_log(const char* path, const char* message) FILE* logfile; if ((logfile = fopen(path, "a")) != NULL) { fprintf(logfile, "%s", message); - LOG(LOG_DEBUG, "Wrote to log %s", path); + LOG(LOG_DEBUG, "Wrote to log \"%s\"", path); fclose(logfile); return 1; } else @@ -88,19 +64,6 @@ write_log(const char* path, const char* message) return 0; } -bool -cleanup_path_names(char* name) -{ - if (name == NULL) return 0; - if (!strcmp("..", name) || !strcmp(".", name)) *name = '_'; - for (; *name; name++) { - if (isalpha(*name)) *name = (char) tolower(*name); - else if (strchr(".+-_#&", *name) == NULL && !isdigit(*name)) - *name = '_'; - } - return 1; -} - bool add_socket_flags(int fd, int flags) { @@ -114,53 +77,3 @@ add_socket_flags(int fd, int flags) LOG(LOG_WARNING, "Failed to get socket flags: " ERRNOFMT, strerror(errno), errno); return 0; } - -ssize_t -get_path(char* buf, size_t lim, Connection* conn, IRC_Message* msg, bool global, bool input, bool output) -{ - if (buf == NULL || conn == NULL) return -1; - ssize_t res; - const char* save = buf; - - int ci = -1; - const char *dir = NULL, *subdir = NULL; - if (global) { - dir = "global"; - } else if (msg != NULL && (ci = get_msgchannel(msg)) != -1 && msg->args[ci] != NULL) { - dir = "channel"; - subdir = msg->args[ci]; - } else if (msg != NULL && msg->name.nick != NULL) { - dir = "user"; - subdir = msg->name.nick; - } else - return -1; - if ((res = snprintf(buf, lim, "%s", dir)) == -1) return -1; - clean_and_push(&lim, (size_t) res, &buf, true); - if (subdir != NULL) { - if ((res = snprintf(buf, lim, "%s", subdir)) == -1) return -1; - clean_and_push(&lim, (size_t) res, &buf, true); - } - - const char* category = NULL; - if (input) { - category = "in"; - } else if (output) { - category = "out"; - } else { - if ((category = get_categ(msg)) == NULL) return -1; - } - if ((res = snprintf(buf, lim, "%s", category)) == -1) return -1; - clean_and_push(&lim, (size_t) res, &buf, false); - - return (buf - save); -} - -void -clean_and_push(size_t* lim, size_t res, char** buf, bool dir) -{ - cleanup_path_names(*buf); - if (dir) *(*(buf) + res) = '/'; - *lim -= (size_t) res + dir; - *buf += (size_t) res + dir; -} - diff --git a/src/filesystem.h b/src/filesystem.h index 2e750b8..665f9e1 100644 --- a/src/filesystem.h +++ b/src/filesystem.h @@ -20,20 +20,14 @@ #include #include - -#define UIRC_IRCV3 #include // IRC_Message #ifndef UIRCD_GUARD_FILESYSTEM #define UIRCD_GUARD_FILESYSTEM -int mkdir_bottomup(char* path); -int makeinput(const char* path); -bool write_log(const char* path, const char* message); -bool cleanup_path_names(char* name); -bool add_socket_flags(int fd, int flags); -ssize_t get_path(char* buf, size_t lim, Connection* conn, IRC_Message* msg, bool global, bool input, bool output); -void clean_and_push(size_t* lim, size_t res, char** buf, bool dir); +int makeinput(const char* path); +bool write_log(const char* path, const char* message); +bool add_socket_flags(int fd, int flags); #endif /* UIRCD_GUARD_FILESYSTEM */ diff --git a/src/logging.c b/src/logging.c deleted file mode 100644 index a1932ef..0000000 --- a/src/logging.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This file is part of uIRCd. (https://git.redxen.eu/caskd/uIRCd) - * Copyright (c) 2019, 2020 Alex-David Denes - * - * uIRCd is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * uIRCd is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with uIRCd. If not, see . - */ - -#include "logging.h" diff --git a/src/logging.h b/src/logging.h index ba07cb1..b703f56 100644 --- a/src/logging.h +++ b/src/logging.h @@ -16,6 +16,8 @@ * along with uIRCd. If not, see . */ +#include + #ifndef UIRCD_GUARD_LOGGING #define UIRCD_GUARD_LOGGING diff --git a/src/main.c b/src/main.c index 688f9ee..1602c6c 100644 --- a/src/main.c +++ b/src/main.c @@ -35,11 +35,8 @@ #include // time_t size_t #include // openlog() syslog() #include // time() -#include // getopt() chdir() close() - -#define UIRC_IRCV3 -#define UIRC_HELPERS #include // IRC_Message +#include // getopt() chdir() close() int parse_args(int argc, char** argv, Connection* conn) @@ -133,18 +130,15 @@ int main(int argc, char* argv[]) { IRC_Message buffer; - Connection connection = { 0 }; - time_t ctime; // Current time - struct timespec sleep = { 1, 0 }; // Interval between loops when idle (network buffer is empty) + Connection connection = { .info.state = CONN_PENDING }; // Start off with a pending connection + time_t ctime; // Current time + struct timespec sleep = { 1, 0 }; // Interval between loops when idle (network buffer is empty) struct { - Buffer_Info recv[2], // Network recv buffer / FIFO read buffer - send[2], // Network send buffer / FIFO send buffer - log; // Log writing buffer - } buf = { - .recv = { { .fd = -1 }, { .fd = -1 } }, - .send = { { .fd = -1 }, { .fd = -1 } }, - .log = { .fd = -1 }, - }; + char buf[UIRCD_LIMITS_LINE + 1]; + size_t pos; + } Buf_FIFO = { .pos = 0 }, Buf_RECV = { .pos = 0 }; // async buffers, these are managed without blocking + int net_fd = -1, fifo_fd = -1; // Network I/O and FIFO I/O (logs are opened and closed on write) + char Buf_INTERNAL[UIRCD_LIMITS_LINE + 1]; // sync internal buffer, must be fully flushed each use /* * Initialisation @@ -152,14 +146,17 @@ main(int argc, char* argv[]) openlog("uIRCd", LOG_CONS | LOG_PID | LOG_PERROR, LOG_DAEMON); setlogmask(LOG_UPTO(LOG_INFO)); init_chanarray(&connection.info.channels); + setup_signals(); if (!set_config_defaults(&connection)) { LOG(LOG_ERR, "Couldn't allocate memory for configuration variables: " ERRNOFMT, strerror(errno), errno); return EXIT_FAILURE; } + switch (parse_args(argc, argv, &connection)) { case 0: return EXIT_SUCCESS; case -1: return EXIT_FAILURE; } + if (connection.data.path != NULL) { int tmpres; // TODO: Add chroot()/jail support by default where supported @@ -167,9 +164,8 @@ main(int argc, char* argv[]) LOG(LOG_ERR, "Couldn't change log directory to %s: " ERRNOFMT, connection.data.path, strerror(errno), errno); return EXIT_FAILURE; } - LOG(LOG_INFO, "Changed root directory to %s", connection.data.path); + LOG(LOG_INFO, "Changed log root directory to %s", connection.data.path); } - setup_signals(); /* * Main loop @@ -177,109 +173,108 @@ main(int argc, char* argv[]) for (;;) { ctime = time(NULL); if (connection.info.state == CONN_CLOSED) { + LOG(LOG_INFO, "Exiting gracefully"); return EXIT_SUCCESS; + } else if (!run || connection.info.state == CONN_CLOSING) { - if ((buf.send[0].data_size = Assm_mesg(buf.send[0].buffer, Assm_cmd_QUIT(connection.data.quitmsg), buf.send[0].buf_size)) > 0) { - if (write_buffer(&buf.send[0]) != -1) + + ssize_t len; + IRC_Message build_buf; + if ((len = Assm_mesg(Buf_INTERNAL, Assm_cmd_QUIT(&build_buf, connection.data.quitmsg), sizeof(Buf_INTERNAL))) > 0) { + ssize_t tmp; + if ((tmp = write_buffer(Buf_INTERNAL, net_fd, (size_t) len, true)) > 0) LOG(LOG_INFO, "Sent a QUIT message to " ADDRFMT " containing \"%s\"", connection.data.address, connection.data.service, connection.data.quitmsg); + else if (tmp == -1) + LOG(LOG_WARNING, "Failed to send a QUIT message to " ADDRFMT " containing \"%s\"", connection.data.address, connection.data.service, connection.data.quitmsg); } - reset_buffer(&buf.recv[0], 2); // net rw/FIFO read fd - reset_buffer(&buf.recv[1], 2); // FIFO read fd + close(net_fd); + close(fifo_fd); connection.info.state = CONN_CLOSED; LOG(LOG_INFO, "Connection to " ADDRFMT " was closed", connection.data.address, connection.data.service); continue; + } else if (connection.info.state == CONN_RECONNECTING) { + + close(net_fd); + close(fifo_fd); + net_fd = -1; + fifo_fd = -1; connection.info.state = CONN_PENDING; if (connection.info.reconinter <= 300) connection.info.reconinter += 5; continue; + } else if (connection.info.state == CONN_PENDING) { // Reconnection throttling if (ctime - connection.info.l_connect < connection.info.reconinter) continue; - - connection.info.l_connect = ctime; - - /* Reset all state-dependent values to empty */ - reset_buffer(&buf.recv[0], 2); - reset_buffer(&buf.recv[1], 2); - reset_buffer(&buf.send[0], 1); - reset_buffer(&buf.send[1], 1); - reset_buffer(&buf.log, 1); - connection.info.l_message = 0; + Buf_RECV.buf[(Buf_RECV.pos = 0)] = '\0'; + Buf_FIFO.buf[(Buf_FIFO.pos = 0)] = '\0'; + connection.info.l_connect = ctime; + connection.info.l_message = 0; // Connection initialisation and registration - if ((buf.send[0].fd = init_connection(&connection)) > 0) { - buf.recv[0].fd = buf.send[1].fd = buf.send[0].fd; - add_socket_flags(buf.send[0].fd, O_NONBLOCK); - if (!register_user(&connection, &buf.send[0])) { + fifo_fd = makeinput("in"); + + signed int tmp; + if ((tmp = init_connection(&connection)) > 0) { + add_socket_flags(tmp, O_NONBLOCK); + net_fd = tmp; + if (register_user(Buf_INTERNAL, sizeof(Buf_INTERNAL), net_fd, &connection.user)) { + LOG(LOG_INFO, "Registered to server " ADDRFMT, connection.data.address, connection.data.service); + connection.info.state = CONN_REGISTERED; + } else { + LOG(LOG_WARNING, "Failed to register to server " ADDRFMT ": " ERRNOFMT, connection.data.address, connection.data.service, strerror(errno), errno); connection.info.state = CONN_RECONNECTING; - continue; } - connection.info.state = CONN_REGISTERED; - } else if (buf.send[0].fd == INIT_SOFTFAIL) { + } else if (tmp == INIT_SOFTFAIL) { connection.info.state = CONN_RECONNECTING; - continue; - } else if (buf.send[0].fd == INIT_HARDFAIL) { + } else if (tmp == INIT_HARDFAIL) { connection.info.state = CONN_CLOSED; - continue; } + continue; + } else if (connection.info.state == CONN_IDLE) { + if (connection.info.state == CONN_IDLE) nanosleep(&sleep, NULL); if (connection.data.timeout > 0 && connection.info.l_message < ctime - connection.data.timeout) { LOG(LOG_WARNING, "Timed out because no message was received since %lu", connection.info.l_message); connection.info.state = CONN_RECONNECTING; continue; } - if (buf.recv[1].fd != -1) { - ssize_t r_bytes, len; - if ((r_bytes = read_buffer(&buf.recv[1])) == -1) { + + if (fifo_fd >= 0) { + /* FIFO input is passthrough, no validation is made, allows raw input */ + ssize_t r_bytes, w_bytes; + + if ((r_bytes = read_buffer(Buf_FIFO.buf + Buf_FIFO.pos, fifo_fd, sizeof(Buf_FIFO.buf) - Buf_FIFO.pos - 1)) == -1) { connection.info.state = CONN_RECONNECTING; continue; - } - - memset((void*) &buffer, '\0', sizeof(IRC_Message)); - if ((len = tok_irc_line(buf.recv[1].buffer)) > 0) { - LOG(LOG_DEBUG, "Got IRC message: %s", buf.recv[1].buffer); - if (Tok_mesg(buf.recv[1].buffer, &buffer) == 1) { - if ((buf.send[1].data_size = Assm_mesg(buf.send[1].buffer, &buffer, buf.send[1].buf_size)) > 0) { - if (write_buffer(&buf.send[1]) == -1) { - connection.info.state = CONN_RECONNECTING; - continue; - } - } - } else - LOG(LOG_WARNING, "Received invalid IRC message on FIFO (see RFC2812)"); - - buffer_offset_move(&buf.recv[1], len); - } else if (buf.recv[1].data_size == buf.recv[1].buf_size - 1) { -#ifdef UIRCD_RELAXED_RFC - // TODO: Add resize here -#else /* UIRCD_RELAXED_RFC */ - LOG(LOG_WARNING, "FIFO buffer is full and no message could be parsed"); - reset_buffer(&buf.recv[1], false); -#endif /* UIRCD_RELAXED_RFC */ - } - } else { - char pathbuf[UIRCD_LIMITS_PATH]; - if (get_path(pathbuf, sizeof(pathbuf), &connection, NULL, true, true, false) >= 0) { - if (mkdir_bottomup(pathbuf)) buf.recv[1].fd = makeinput(pathbuf); - } + } else if (r_bytes > 0) + Buf_FIFO.pos += (size_t) r_bytes; + if ((w_bytes = write_buffer(Buf_FIFO.buf, net_fd, Buf_FIFO.pos, false)) == -1) { + connection.info.state = CONN_RECONNECTING; + continue; + } else if (w_bytes > 0) + Buf_FIFO.pos -= dequeue_bytes(Buf_FIFO.buf, (size_t) w_bytes, Buf_FIFO.pos); } + } else if (connection.info.state == CONN_ACTIVE) { - connection.info.state = CONN_IDLE; + if (Buf_RECV.pos == 0) connection.info.state = CONN_IDLE; } + /* Receive buffer reader */ ssize_t r_bytes, len; - if ((r_bytes = read_buffer(&buf.recv[0])) == -1) { + if ((r_bytes = read_buffer(Buf_RECV.buf + Buf_RECV.pos, net_fd, sizeof(Buf_RECV.buf) - Buf_RECV.pos - 1)) == -1) { connection.info.state = CONN_RECONNECTING; continue; - } + } else + Buf_RECV.pos += (size_t) r_bytes; memset((void*) &buffer, '\0', sizeof(IRC_Message)); - if ((len = tok_irc_line(buf.recv[0].buffer)) > 0) { - LOG(LOG_DEBUG, "Got IRC message on recvbuffer: %s", buf.recv[0].buffer); - if (Tok_mesg(buf.recv[0].buffer, &buffer) == 1) { + if ((len = tok_irc_line(Buf_RECV.buf)) > 0) { + LOG(LOG_DEBUG, "Got IRC message on recvbuffer: %s", Buf_RECV.buf); + if (Tok_mesg(Buf_RECV.buf, &buffer) == 1) { connection.info.l_message = ctime; char datebuf[25]; @@ -288,32 +283,60 @@ main(int argc, char* argv[]) buffer.tags.time.value = datebuf; } - char logpath[UIRCD_LIMITS_PATH]; - if ((buf.log.data_size = Assm_mesg(buf.log.buffer, &buffer, buf.log.buf_size)) > 0) { - printf("%s", buf.log.buffer); - bool match[][3] = { { false, false, false }, { true, false, false }, { true, false, true } }; - for (unsigned long i = 0; i < sizeof(match) / sizeof(*match); i++) { - if (get_path(logpath, sizeof(logpath), &connection, &buffer, match[i][0], match[i][1], match[i][2]) >= 0) { - if (mkdir_bottomup(logpath)) write_log(logpath, buf.log.buffer); - } + if ((Assm_mesg(Buf_INTERNAL, &buffer, sizeof(Buf_INTERNAL))) > 0) { + printf("%s", Buf_INTERNAL); + write_log("out", Buf_INTERNAL); + } + + switch (buffer.cmd) { + case (PING): { + LOG(LOG_DEBUG, "Auto-replying to ping \"%s\"", buffer.args[0]); + ssize_t tmp; + IRC_Message build_buf; + if ((tmp = Assm_mesg(Buf_INTERNAL, Assm_cmd_PONG(&build_buf, buffer.args[0], NULL), sizeof(Buf_INTERNAL))) > 0) + if (write_buffer(Buf_INTERNAL, net_fd, (size_t) tmp, true) == -1) { + LOG(LOG_WARNING, "Couldn't pong " ADDRFMT ". " ERRNOFMT, connection.data.address, connection.data.service, strerror(errno), errno); + connection.info.state = CONN_RECONNECTING; + return 0; + } + break; + } + /* Autojoin channels from current conn on first response from the server */ + case (RPL_WELCOME): { + LOG(LOG_NOTICE, "Connection established to " ADDRFMT, connection.data.address, connection.data.service); + connection.info.state = CONN_ACTIVE; + connection.info.reconinter = 0; + if (!commit_channelist(Buf_INTERNAL, sizeof(Buf_INTERNAL), net_fd, connection.info.channels)) connection.info.state = CONN_RECONNECTING; + break; + } + case (ERROR): { + LOG(LOG_ERR, "Received error: %s", buffer.args[0]); + break; + } + case (JOIN): + case (PART): { + resize_chanarray(&connection.info.channels); + set_channel(&connection.info.channels[get_channelindex(buffer.args[0], connection.info.channels)], buffer.args[0], buffer.args[1], buffer.cmd == JOIN); + break; + } + case (ERR_NICKNAMEINUSE): + case (ERR_NICKCOLLISION): { + LOG(LOG_WARNING, "Nickname \"%s\" is already taken", connection.user.nickname); + connection.info.state = CONN_RECONNECTING; + break; } } - if (!auto_msg_actions(&buffer, &connection, &buf.send[0])) continue; - - buffer_offset_move(&buf.recv[0], len); connection.info.state = CONN_ACTIVE; } else LOG(LOG_WARNING, "Received invalid IRC message (see RFC2812)."); - } else if (len == -1) { + Buf_RECV.pos -= dequeue_bytes(Buf_RECV.buf, (size_t) len, Buf_RECV.pos); + } else { + if (len == 0) { + if (Buf_RECV.pos != sizeof(Buf_RECV.buf) - 1) continue; + LOG(LOG_WARNING, "Receive buffer is full and no message could be parsed"); + } connection.info.state = CONN_RECONNECTING; - } else if (buf.recv[0].data_size == buf.recv[0].buf_size - 1) { -#ifdef UIRCD_RELAXED_RFC - // TODO: Add resize here -#else /* UIRCD_RELAXED_RFC */ - LOG(LOG_WARNING, "Receive buffer is full and no message could be parsed"); - connection.info.state = CONN_RECONNECTING; -#endif /* UIRCD_RELAXED_RFC */ } } } diff --git a/src/misc.c b/src/misc.c index 8abe563..40ebcfc 100644 --- a/src/misc.c +++ b/src/misc.c @@ -23,9 +23,6 @@ #include #include #include - -#define UIRC_IRCV3 -#define UIRC_HELPERS #include ssize_t diff --git a/src/misc.h b/src/misc.h index 7fc81d9..e17291d 100644 --- a/src/misc.h +++ b/src/misc.h @@ -24,3 +24,4 @@ ssize_t tok_irc_line(char* buf); #endif /* UIRCD_GUARD_MISC */ +