From 7b9b39e1a91d0e45b31ac4cd5330c637156a6a2b Mon Sep 17 00:00:00 2001 From: Alex Denes Date: Tue, 12 Jan 2021 17:04:21 +0000 Subject: [PATCH] Prepare features that require breaking RFC2812 --- .clang-format | 2 +- CMakeLists.txt | 9 +- README.md | 3 +- src/buffer.c | 49 +++++++- src/buffer.h | 21 ++++ src/channels.c | 8 +- src/channels.h | 2 + src/configuration.c | 16 +-- src/connection.c | 96 ++++++++++----- src/connection.h | 10 +- src/main.c | 293 ++++++++++++++++---------------------------- 11 files changed, 256 insertions(+), 253 deletions(-) diff --git a/.clang-format b/.clang-format index 07327a4..8d9f2a6 100644 --- a/.clang-format +++ b/.clang-format @@ -17,7 +17,7 @@ BinPackParameters: 'false' BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Linux BreakStringLiterals: 'false' -ColumnLimit: '150' +ColumnLimit: '200' ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' Cpp11BracedListStyle: 'false' IncludeBlocks: Regroup diff --git a/CMakeLists.txt b/CMakeLists.txt index 249ec14..9108736 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) project( uIRCd - VERSION 2021.01.09 + VERSION 2021.01.12 DESCRIPTION "High performance IRC daemon based on uIRC" LANGUAGES C ) @@ -9,9 +9,10 @@ project( set(UIRCD_VERSION "${PROJECT_VERSION}") add_compile_definitions(UIRCD_VERSION="${UIRCD_VERSION}") -OPTION(BUILD_LIBCONFIG "Build support for configurations" ON ) -OPTION(CODE_ANALYZER "Analyze the code statically" OFF) -OPTION(CODE_COVERAGE "Build with coverage tools" OFF) +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) add_executable(uircd src/buffer.c diff --git a/README.md b/README.md index b77b926..5adfd3b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ cmake -B build/ -DCMAKE_BUILD_TYPE=Release . ``` | Option | Description | Type | Default | Supported since | |:------------------:|:----------------------------------------------------------------------:|:--------:|:-------:|:---------------:| -| BUILD_LIBCONFIG | Build and link against libconfig | boolean | true | 2020.01.04 | +| 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 4081451..96c41cb 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -20,8 +20,9 @@ #include "logging.h" -#include // errno -#include // fprintf() +#include // errno +#include // fprintf() +#include #include // strerror() #include // size_t ssize_t #include // write() @@ -48,14 +49,50 @@ flush_buffer(char* buf, size_t buflen, int fd) { ssize_t res; char* pos = buf; - LOG(LOG_DEBUG, "Will write %s to socket %i", buf, fd); for (;;) { if ((size_t)(res = write(fd, pos, buflen - (size_t)(pos - buf))) != buflen - (size_t)(pos - buf)) { - if (res == -1 && errno != EINTR) return -1; - else - pos += res; + if (res == -1 && errno != EINTR) { + LOG(LOG_WARN, "Couldn't flush buffer to fd %i. " ERRNOFMT, fd, strerror(errno), errno); + return -1; + } + pos += res; + LOG(LOG_DEBUG, "Wrote %lu bytes to fd %i.", res, fd); } else return res; } } +ssize_t +read_buffer(Buffer_Info* buf) +{ + ssize_t brd; + if ((brd = read(buf->fd, buf->buffer + buf->append_pos, buf->csize - buf->append_pos - 1)) > 0) { + *(buf->buffer + (buf->append_pos += (size_t) brd)) = '\0'; + LOG(LOG_DEBUG, "Read %li bytes from socket buffer. Now appending at %li with \"%s\" so far.", brd, buf->append_pos, buf->buffer); + } else if (brd == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) return 0; + LOG(LOG_WARN, "Failed to read inbound traffic on fd %i. " ERRNOFMT, buf->fd, strerror(errno), errno); + } + return brd; +} + +int +reset_buffer(Buffer_Info* buf, short clr_fd) +{ + if (clr_fd) { + if (clr_fd == 2) close(buf->fd); + buf->fd = -1; + } +#ifdef UIRCD_RELAXED_RFC + free(buf->buffer); + buf->buffer = NULL; + buf->csize = 0; +#else /* UIRCD_RELAXED_RFC */ + *buf->buffer = '\0'; + buf->csize = UIRCD_LIMITS_LINE + 1; +#endif /* UIRCD_RELAXED_RFC */ + buf->append_pos = 0; + return 1; +} + +// TODO: Buffer allocators and resizers diff --git a/src/buffer.h b/src/buffer.h index 2090178..619da1f 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -16,8 +16,29 @@ * along with uIRCd. If not, see . */ +#include "limits.h" + +#include // bool #include // size_t ssize_t +#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 append_pos, csize; + int fd; +} Buffer_Info; + ssize_t get_buffer_line(char* buf); ssize_t flush_buffer(char* buf, size_t buflen, int fd); +ssize_t read_buffer(Buffer_Info* buf); +int reset_buffer(Buffer_Info* buf, short clr_fd); +#endif /* UIRCD_GUARD_BUFFER */ diff --git a/src/channels.c b/src/channels.c index 7c32d9b..060bf3a 100644 --- a/src/channels.c +++ b/src/channels.c @@ -24,10 +24,10 @@ #include // errno #include // bool #include // snprintf() +#include // malloc() realloc() #include // strerror() #include // sockaddr connect() #include // size_t ssize_t socklen_t -#include // malloc() realloc() int init_chanarray(Channel** chans) @@ -73,11 +73,7 @@ set_channel(Channel* chan, const char* name, const char* key, bool joined) } if (key != NULL) { if (!allocate_copy(&chan->key, key)) { - LOG(LOG_WARN, - "Couldn't allocate memory for the channel %s key. " ERRNOFMT, - (name == NULL) ? chan->name : name, - strerror(errno), - errno); + LOG(LOG_WARN, "Couldn't allocate memory for the channel %s key. " ERRNOFMT, (name == NULL) ? chan->name : name, strerror(errno), errno); return 0; } } diff --git a/src/channels.h b/src/channels.h index df0d11c..3778445 100644 --- a/src/channels.h +++ b/src/channels.h @@ -21,6 +21,8 @@ #ifndef UIRCD_GUARD_CHANNELS #define UIRCD_GUARD_CHANNELS +#define CHANNELMASK(STR) (strchr("#&+!", *STR) != NULL) + typedef struct { char *name, *key; bool joined; diff --git a/src/configuration.c b/src/configuration.c index 6fdf93a..2ee989d 100644 --- a/src/configuration.c +++ b/src/configuration.c @@ -59,9 +59,7 @@ parse_configfile(char* config_path, Connection* conn) config_setting_lookup_string(chanelem, "key", &key); LOG(LOG_DEBUG, "Got channel #%i: %s", i, name); resize_chanarray(&conn->info.channels); - if (name != NULL) { - set_channel(&conn->info.channels[get_channelindex(name, conn->info.channels)], name, key, true); - } + if (name != NULL) set_channel(&conn->info.channels[get_channelindex(name, conn->info.channels)], name, key, true); } } @@ -75,14 +73,11 @@ parse_configfile(char* config_path, Connection* conn) }; mapconf(user, usermaps, sizeof(usermaps) / sizeof(*usermaps)); } + LOG(LOG_DEBUG, "%s.", "Parsed configuration file successfully"); res = 1; } } else - LOG(LOG_WARN, - "Encountered a error while reading the config file. %s in %s line %d", - config_error_text(&conf), - config_error_file(&conf), - config_error_line(&conf)); + LOG(LOG_WARN, "Encountered a error while reading the config file. %s in %s line %d", config_error_text(&conf), config_error_file(&conf), config_error_line(&conf)); config_destroy(&conf); return res; } @@ -109,9 +104,8 @@ int set_config_defaults(Connection* conn) { Mapping defsalloc[] = { - { UIRCD_DEFAULT_ADDR, &conn->data.address }, { UIRCD_DEFAULT_PORT, &conn->data.service }, - { UIRCD_DEFAULT_QUITMSG, &conn->data.quitmsg }, { UIRCD_DEFAULT_NICK, &conn->user.nickname }, - { UIRCD_DEFAULT_USER, &conn->user.username }, { UIRCD_DEFAULT_REAL, &conn->user.realname }, + { UIRCD_DEFAULT_ADDR, &conn->data.address }, { UIRCD_DEFAULT_PORT, &conn->data.service }, { UIRCD_DEFAULT_QUITMSG, &conn->data.quitmsg }, + { UIRCD_DEFAULT_NICK, &conn->user.nickname }, { UIRCD_DEFAULT_USER, &conn->user.username }, { UIRCD_DEFAULT_REAL, &conn->user.realname }, }; for (unsigned int i = 0; i < sizeof(defsalloc) / sizeof(*defsalloc); i++) { if (!allocate_copy(defsalloc[i].save, defsalloc[i].str)) return 0; diff --git a/src/connection.c b/src/connection.c index e8045a2..22013da 100644 --- a/src/connection.c +++ b/src/connection.c @@ -45,12 +45,7 @@ init_connection(Connection* conn) struct addrinfo* addr_info; if ((getaddrres = getaddrinfo(conn->data.address, conn->data.service, NULL, &addr_info)) != 0) { freeaddrinfo(addr_info); - LOG(LOG_WARN, - "Failed to get address info for " ADDRFMT ". " ERRNOFMT, - conn->data.address, - conn->data.service, - gai_strerror(getaddrres), - getaddrres); + LOG(LOG_WARN, "Failed to get address info for " ADDRFMT ". " ERRNOFMT, conn->data.address, conn->data.service, gai_strerror(getaddrres), getaddrres); if (getaddrres != EAI_AGAIN && getaddrres != EAI_NONAME) return INIT_HARDFAIL; else return INIT_SOFTFAIL; @@ -142,14 +137,9 @@ auto_msg_actions(IRC_Message* mesg, Connection* conn, Buffer_Info* buf) switch (mesg->cmd) { case (PING): { LOG(LOG_DEBUG, "Auto-replying to ping \"%s\".", mesg->args[0]); - if ((len = Assm_mesg(buf->buffer, Assm_cmd_PONG(mesg->args[0], NULL), sizeof(buf->buffer))) > 0) + if ((len = Assm_mesg(buf->buffer, Assm_cmd_PONG(mesg->args[0], NULL), buf->csize)) > 0) if (flush_buffer(buf->buffer, (size_t) len, buf->fd) == -1) { - LOG(LOG_WARN, - "Couldn't pong " ADDRFMT ". " ERRNOFMT, - conn->data.address, - conn->data.service, - strerror(errno), - errno); + LOG(LOG_WARN, "Couldn't pong " ADDRFMT ". " ERRNOFMT, conn->data.address, conn->data.service, strerror(errno), errno); conn->info.state = CONN_RECONNECTING; return 0; } @@ -162,7 +152,8 @@ auto_msg_actions(IRC_Message* mesg, Connection* conn, Buffer_Info* buf) /* Autojoin channels from current conn on first response from the server */ case (RPL_WELCOME): { LOG(LOG_INFO, "Connection established to " ADDRFMT ".", conn->data.address, conn->data.service); - conn->info.state = CONN_ACTIVE; + conn->info.state = CONN_ACTIVE; + conn->info.reconinter = 0; if (!commit_channelist(buf, conn)) return 0; break; } @@ -175,7 +166,7 @@ auto_msg_actions(IRC_Message* mesg, Connection* conn, Buffer_Info* buf) mesg->args[2], conn->data.address, conn->data.service); - if ((len = Assm_mesg(buf->buffer, Assm_cmd_CAP_REQ(mesg->args[2]), sizeof(buf->buffer))) > 0) { + 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_WARN, "Couldn't request capabilities \"%s\" on " ADDRFMT ". " ERRNOFMT, @@ -190,7 +181,7 @@ auto_msg_actions(IRC_Message* mesg, Connection* conn, Buffer_Info* buf) } } else if (strcmp(mesg->args[1], "ACK") == 0) { LOG(LOG_VERBOSE, "Ending capability negotiation on " ADDRFMT ".", conn->data.address, conn->data.service); - if ((len = Assm_mesg(buf->buffer, Assm_cmd_CAP_END(), sizeof(buf->buffer))) > 0) { + 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_WARN, "Couldn't end capability negotiation on " ADDRFMT ". " ERRNOFMT, @@ -214,10 +205,7 @@ auto_msg_actions(IRC_Message* mesg, Connection* conn, Buffer_Info* buf) 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); + 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): @@ -235,18 +223,10 @@ 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 ((len = Assm_mesg(buf->buffer, &msg, sizeof(buf->buffer))) > 0) { + 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 ((len = Assm_mesg(buf->buffer, &msg, buf->csize)) > 0) { if (flush_buffer(buf->buffer, (size_t) len, buf->fd) == -1) { - LOG(LOG_WARN, - "Couldn't auto-join channels \"%s\" " ADDRFMT ". " ERRNOFMT, - conn->info.channels[i].name, - conn->data.address, - conn->data.service, - strerror(errno), - errno); + LOG(LOG_WARN, "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; } @@ -255,3 +235,57 @@ commit_channelist(Buffer_Info* buf, Connection* conn) return 1; } +int +register_user(Connection* conn, Buffer_Info* buf) +{ + signed long temp; + /* 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].append_pos = (size_t) temp; + LOG(LOG_VERBOSE, "Sending a CAP LS to " ADDRFMT ".", connection.data.address, connection.data.service); + if (flush_buffer(buf.send[0].buffer, buf.send[0].append_pos, buf.send[0].fd) == -1) { + LOG(LOG_WARN, + "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 && (temp = Assm_mesg(buf->buffer, Assm_cmd_PASS(conn->user.password), buf->csize)) > 0) { + buf->append_pos = (size_t) temp; + if (flush_buffer(buf->buffer, buf->append_pos, buf->fd) != -1) { + LOG(LOG_VERBOSE, "Sent PASS authentication to " ADDRFMT, conn->data.address, conn->data.service); + } else { + conn->info.state = CONN_RECONNECTING; + return 0; + } + } + if ((temp = Assm_mesg(buf->buffer, Assm_cmd_NICK(conn->user.nickname), buf->csize)) > 0) { + buf->append_pos = (size_t) temp; + if (flush_buffer(buf->buffer, buf->append_pos, buf->fd) != -1) { + LOG(LOG_VERBOSE, "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 ((temp = Assm_mesg(buf->buffer, Assm_cmd_USER(conn->user.username, conn->user.realname, 0), buf->csize)) > 0) { + buf->append_pos = (size_t) temp; + if (flush_buffer(buf->buffer, buf->append_pos, buf->fd) != -1) { + LOG(LOG_VERBOSE, + "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 83a9ea5..0eaa79f 100644 --- a/src/connection.h +++ b/src/connection.h @@ -16,6 +16,7 @@ * along with uIRCd. If not, see . */ +#include "buffer.h" #include "channels.h" #include "limits.h" @@ -40,8 +41,6 @@ #define CONN_CLOSING -2 #define CONN_CLOSED -3 -#define CHANNELMASK(STR) (strchr("#&+!", *STR) != NULL) - typedef struct { char * address, *service, *quitmsg, *path; time_t timeout; @@ -58,12 +57,6 @@ typedef struct { signed int reconinter; } Connection_Info; // Connection information [temporary] -typedef struct { - char buffer[UIRCD_LIMITS_LINE + 1]; - size_t append_pos; - int fd; -} Buffer_Info; - typedef struct { Connection_User user; Connection_Data data; @@ -75,6 +68,7 @@ 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); #endif /* UIRCD_GUARD_CONNECTION */ diff --git a/src/main.c b/src/main.c index f96196d..16fe3ea 100644 --- a/src/main.c +++ b/src/main.c @@ -29,7 +29,7 @@ #include // errno #include // O_NONBLOCK #include // printf() -#include // srand() +#include // EXIT_SUCCESS EXIT_FAILURE #include // strerror() #include // time_t size_t #include // time() @@ -53,11 +53,19 @@ parse_args(int argc, char** argv, Connection* conn) "vh")) != -1) { switch (c) { - case 'V': loglevel = atoi(optarg); break; + case 'V': { + loglevel = atoi(optarg); + LOG(LOG_DEBUG, "Log level was set to %i", loglevel); + break; + } case 'a': allocate_copy(&conn->data.address, optarg); break; case 'p': allocate_copy(&conn->data.service, optarg); break; case 'd': allocate_copy(&conn->data.path, optarg); break; - case 't': conn->data.timeout = (unsigned int) atoi(optarg); break; + case 't': { + conn->data.timeout = (unsigned int) atoi(optarg); + LOG(LOG_DEBUG, "Timeout was set to %li", conn->data.timeout); + break; + } case 'N': allocate_copy(&conn->user.nickname, optarg); break; case 'U': allocate_copy(&conn->user.username, optarg); break; case 'R': allocate_copy(&conn->user.realname, optarg); break; @@ -66,13 +74,11 @@ parse_args(int argc, char** argv, Connection* conn) case 'c': { resize_chanarray(&conn->info.channels); chanindex = get_channelindex(optarg, conn->info.channels); - if (!set_channel(&conn->info.channels[chanindex], optarg, NULL, true)) - LOG(LOG_WARN, "Couldn't set channel %s as chan #%lu.", optarg, chanindex); + if (!set_channel(&conn->info.channels[chanindex], optarg, NULL, true)) LOG(LOG_WARN, "Couldn't set channel %s as chan #%lu.", optarg, chanindex); break; } case 'k': { - if (!set_channel(&conn->info.channels[chanindex], NULL, optarg, true)) - LOG(LOG_WARN, "Couldn't set key for channel #%lu.", chanindex); + if (!set_channel(&conn->info.channels[chanindex], NULL, optarg, true)) LOG(LOG_WARN, "Couldn't set key for channel #%lu.", chanindex); break; } case 'v': printf("uIRCd version " UIRCD_VERSION); return 0; @@ -125,16 +131,22 @@ main(int argc, char* argv[]) { IRC_Message buffer; Connection connection = { 0 }; - time_t ctime; - struct timespec sleep = { 0, 50000000L }; + time_t ctime; // Current time + struct timespec sleep = { 0, 50000000L }; // Interval between loops when idle (network buffer is empty) struct { - Buffer_Info recv, send, // Network receive and send (automatic) - fiforecv, fifosend, // FIFO receive and send (manual) - log; // Log writing buffer (automatic) - } buf; + 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 }, + }; + /* + * Initialisation + */ init_chanarray(&connection.info.channels); - srand((unsigned int) time(NULL)); if (!set_config_defaults(&connection)) { LOG(LOG_FATAL, "Couldn't allocate memory for configuration variables. " ERRNOFMT, strerror(errno), errno); return EXIT_FAILURE; @@ -150,252 +162,163 @@ main(int argc, char* argv[]) LOG(LOG_ERROR, "Couldn't change log directory to %s. " ERRNOFMT, connection.data.path, strerror(errno), errno); return EXIT_FAILURE; } - LOG(LOG_VERBOSE, "Changed directory to %s.", connection.data.path); + LOG(LOG_VERBOSE, "Changed root directory to %s.", connection.data.path); } setup_signals(); + + /* + * Main loop + */ for (;;) { ctime = time(NULL); - if (connection.info.state == CONN_IDLE) nanosleep(&sleep, NULL); - if (!run || connection.info.state == CONN_CLOSING) { + if (connection.info.state == CONN_CLOSED) { + LOG(LOG_VERBOSE, "%s", "Exiting gracefully."); + return EXIT_SUCCESS; + } else if (!run || connection.info.state == CONN_CLOSING) { signed long temp; - if ((temp = Assm_mesg(buf.send.buffer, Assm_cmd_QUIT(connection.data.quitmsg), sizeof(buf.send.buffer))) > 0) { - buf.send.append_pos = (unsigned long) temp; - LOG(LOG_VERBOSE, - "Sending a QUIT message to " ADDRFMT " containing \"%s\".", - connection.data.address, - connection.data.service, - connection.data.quitmsg); - if (flush_buffer(buf.send.buffer, buf.send.append_pos, buf.send.fd) == -1) - LOG(LOG_WARN, - "Couldn't flush send buffer to " ADDRFMT ". " ERRNOFMT, - connection.data.address, - connection.data.service, - strerror(errno), - errno); + if ((temp = Assm_mesg(buf.send[0].buffer, Assm_cmd_QUIT(connection.data.quitmsg), buf.send[0].csize)) > 0) { + buf.send[0].append_pos = (unsigned long) temp; + if (flush_buffer(buf.send[0].buffer, buf.send[0].append_pos, buf.send[0].fd) != -1) + LOG(LOG_VERBOSE, "Sent a QUIT message to " ADDRFMT " containing \"%s\".", connection.data.address, connection.data.service, connection.data.quitmsg); } - close(buf.send.fd); - close(buf.fiforecv.fd); + reset_buffer(&buf.recv[0], 2); // net rw/FIFO read fd + reset_buffer(&buf.recv[1], 2); // FIFO read fd connection.info.state = CONN_CLOSED; LOG(LOG_VERBOSE, "Connection to " ADDRFMT " was closed.", connection.data.address, connection.data.service); - break; - } else if (connection.info.state == CONN_CLOSED) { - break; + continue; } else if (connection.info.state == CONN_RECONNECTING) { - close(buf.send.fd); - close(buf.fiforecv.fd); 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 */ - memset(&buf, '\0', sizeof(buf)); - buf.recv.fd = buf.send.fd = buf.fiforecv.fd = buf.fifosend.fd = -1; - connection.info.l_message = 0; + 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; - if ((buf.send.fd = init_connection(&connection)) > 0) { - buf.recv.fd = buf.fifosend.fd = buf.send.fd; - add_socket_flags(buf.send.fd, O_NONBLOCK); - - signed long temp; - /* Registration process and CAP negotiation (TODO) - if ((temp = Assm_mesg(buf.send.buffer, Assm_cmd_CAP_LS("302"), sizeof(buf.send.buffer))) > 0) { - buf.send.append_pos = (size_t) temp; - LOG(LOG_VERBOSE, "Sending a CAP LS to " ADDRFMT ".", connection.data.address, connection.data.service); - if (flush_buffer(buf.send.buffer, buf.send.append_pos, buf.send.fd) == -1) { - LOG(LOG_WARN, - "Couldn't send CAP LS to " ADDRFMT ". " ERRNOFMT, - connection.data.address, - connection.data.service, - strerror(errno), - errno); - connection.info.state = CONN_RECONNECTING; - continue; - } - } - */ - if (connection.user.password != NULL - && (temp = Assm_mesg(buf.send.buffer, Assm_cmd_PASS(connection.user.password), sizeof(buf.send.buffer))) > 0) { - buf.send.append_pos = (size_t) temp; - LOG(LOG_VERBOSE, "Sending PASS authentication to " ADDRFMT, connection.data.address, connection.data.service); - if (flush_buffer(buf.send.buffer, buf.send.append_pos, buf.send.fd) == -1) { - LOG(LOG_WARN, - "Couldn't send PASS authentication on " ADDRFMT ". " ERRNOFMT, - connection.data.address, - connection.data.service, - strerror(errno), - errno); - connection.info.state = CONN_RECONNECTING; - continue; - } - } - if ((temp = Assm_mesg(buf.send.buffer, Assm_cmd_NICK(connection.user.nickname), sizeof(buf.send.buffer))) > 0) { - buf.send.append_pos = (size_t) temp; - LOG(LOG_VERBOSE, - "Sending a NICK registration to " ADDRFMT " containing \"%s\".", - connection.data.address, - connection.data.service, - connection.user.nickname); - if (flush_buffer(buf.send.buffer, buf.send.append_pos, buf.send.fd) == -1) { - LOG(LOG_WARN, - "Couldn't register nickname on " ADDRFMT ". " ERRNOFMT, - connection.data.address, - connection.data.service, - strerror(errno), - errno); - connection.info.state = CONN_RECONNECTING; - continue; - } - } - if ((temp = Assm_mesg(buf.send.buffer, - Assm_cmd_USER(connection.user.username, connection.user.realname, 0), - sizeof(buf.send.buffer))) - > 0) { - buf.send.append_pos = (size_t) temp; - LOG(LOG_VERBOSE, - "Sending a USER registration to " ADDRFMT " containing \"%s\".", - connection.data.address, - connection.data.service, - connection.user.realname); - if (flush_buffer(buf.send.buffer, buf.send.append_pos, buf.send.fd) == -1) { - LOG(LOG_WARN, - "Couldn't register user and real name on " ADDRFMT ". " ERRNOFMT, - connection.data.address, - connection.data.service, - strerror(errno), - errno); - connection.info.state = CONN_RECONNECTING; - continue; - } + // 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])) { + connection.info.state = CONN_RECONNECTING; + continue; } connection.info.state = CONN_REGISTERED; - } else if (buf.send.fd == INIT_SOFTFAIL) { + } else if (buf.send[0].fd == INIT_SOFTFAIL) { connection.info.state = CONN_RECONNECTING; continue; - } else if (buf.send.fd == INIT_HARDFAIL) { + } else if (buf.send[0].fd == INIT_HARDFAIL) { connection.info.state = CONN_CLOSED; continue; } - } else if (connection.info.state == CONN_ACTIVE || connection.info.state == CONN_IDLE) { - connection.info.reconinter = 0; - connection.info.state = CONN_IDLE; + } 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_WARN, "Timed out because no message was received since %lu.", connection.info.l_message); connection.info.state = CONN_RECONNECTING; continue; } - if (buf.fiforecv.fd != -1) { - ssize_t brd, len; - /* FIFO reader */ - if ((brd = read(buf.fiforecv.fd, - buf.fiforecv.buffer + buf.fiforecv.append_pos, - sizeof(buf.fiforecv.buffer) - buf.fiforecv.append_pos - 1)) - > 0) { - *(buf.fiforecv.buffer + (buf.fiforecv.append_pos += (size_t) brd)) = '\0'; - LOG(LOG_DEBUG, - "Read %li bytes from FIFO buffer. Now appending at %li with \"%s\" so far.", - brd, - buf.fiforecv.append_pos, - buf.fiforecv.buffer); - connection.info.state = CONN_ACTIVE; - } else if (brd == -1 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { - LOG(LOG_ERROR, "Failed to read FIFO buffer. " ERRNOFMT, strerror(errno), errno); + if (buf.recv[1].fd != -1) { + ssize_t r_bytes, len; + if ((r_bytes = read_buffer(&buf.recv[1])) == -1) { connection.info.state = CONN_RECONNECTING; continue; } memset((void*) &buffer, '\0', sizeof(IRC_Message)); - if ((len = get_buffer_line(buf.fiforecv.buffer)) > 0) { - LOG(LOG_DEBUG, "Got IRC message: %s", buf.fiforecv.buffer); - if (Tok_mesg(buf.fiforecv.buffer, &buffer) == 1) { + if ((len = get_buffer_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) { signed long temp; - if ((temp = Assm_mesg(buf.fifosend.buffer, &buffer, sizeof(buf.fifosend.buffer))) > 0) { - if (flush_buffer(buf.fifosend.buffer, (size_t) temp, buf.fifosend.fd) == -1) { - LOG(LOG_WARN, - "Couldn't send FIFO contents to " ADDRFMT ". " ERRNOFMT, - connection.data.address, - connection.data.service, - strerror(errno), - errno); + if ((temp = Assm_mesg(buf.send[1].buffer, &buffer, buf.send[1].csize)) > 0) { + if (flush_buffer(buf.send[1].buffer, (size_t) temp, buf.send[1].fd) == -1) { connection.info.state = CONN_RECONNECTING; continue; } } - } else { + } else LOG(LOG_WARN, "%s", "Received invalid IRC message on FIFO (see RFC2812)."); - } - for (long unsigned int x = 0; x < sizeof(buf.fiforecv.buffer) && *(buf.fiforecv.buffer + len + x); x++) - *(buf.fiforecv.buffer + x) = *(buf.fiforecv.buffer + x + len); - buf.fiforecv.append_pos -= (unsigned long) len; - *(buf.fiforecv.buffer + buf.fiforecv.append_pos) = '\0'; - } else if (buf.fiforecv.append_pos == sizeof(buf.fiforecv.fd) - 1) { + + for (long unsigned int x = 0; x < buf.recv[1].csize && *(buf.recv[1].buffer + len + x); x++) *(buf.recv[1].buffer + x) = *(buf.recv[1].buffer + x + len); + buf.recv[1].append_pos -= (unsigned long) len; + *(buf.recv[1].buffer + buf.recv[1].append_pos) = '\0'; + } else if (buf.recv[1].append_pos == buf.recv[1].csize - 1) { +#ifdef UIRCD_RELAXED_RFC + // TODO: Add resize here +#else /* UIRCD_RELAXED_RFC */ LOG(LOG_WARN, "%s.", "FIFO buffer is full and no message could be parsed. Cleared buffer."); - memset(buf.fiforecv.buffer, '\0', sizeof(buf.fiforecv)); - buf.fiforecv.append_pos = 0; + 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.fiforecv.fd = makeinput(pathbuf); + if (mkdir_bottomup(pathbuf)) buf.recv[1].fd = makeinput(pathbuf); } } + } else if (connection.info.state == CONN_ACTIVE) { + connection.info.state = CONN_IDLE; } - ssize_t brd, len; - /* Buffer reader */ - if ((brd = read(buf.recv.fd, buf.recv.buffer + buf.recv.append_pos, sizeof(buf.recv.buffer) - buf.recv.append_pos - 1)) > 0) { - *(buf.recv.buffer + (buf.recv.append_pos += (size_t) brd)) = '\0'; - LOG(LOG_DEBUG, - "Read %li bytes from socket buffer. Now appending at %li with \"%s\" so far.", - brd, - buf.recv.append_pos, - buf.recv.buffer); - connection.info.state = CONN_ACTIVE; - } else if (brd == -1 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { - LOG(LOG_ERROR, "Failed to read inbound traffic. " ERRNOFMT, strerror(errno), errno); + ssize_t r_bytes, len; + if ((r_bytes = read_buffer(&buf.recv[0])) == -1) { connection.info.state = CONN_RECONNECTING; continue; } memset((void*) &buffer, '\0', sizeof(IRC_Message)); - if ((len = get_buffer_line(buf.recv.buffer)) > 0) { - LOG(LOG_DEBUG, "Got IRC message: %s", buf.recv.buffer); - if (Tok_mesg(buf.recv.buffer, &buffer) == 1) { + if ((len = get_buffer_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) { connection.info.l_message = ctime; + char datebuf[25]; if (buffer.tags.time.value == NULL) { Assm_tag_timestamp(datebuf, sizeof(datebuf), ctime); buffer.tags.time.value = datebuf; } + char logpath[UIRCD_LIMITS_PATH]; - if (Assm_mesg(buf.log.buffer, &buffer, sizeof(buf.log.buffer)) > 0) { + if (Assm_mesg(buf.log.buffer, &buffer, buf.log.csize) > 0) { printf("%s\r\n", 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 (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 (!auto_msg_actions(&buffer, &connection, &buf.send)) continue; - for (long unsigned int x = 0; x < sizeof(buf.recv.buffer) && *(buf.recv.buffer + len + x); x++) - *(buf.recv.buffer + x) = *(buf.recv.buffer + x + len); - buf.recv.append_pos -= (unsigned long) len; - *(buf.recv.buffer + buf.recv.append_pos) = '\0'; - connection.info.state = CONN_ACTIVE; + + if (!auto_msg_actions(&buffer, &connection, &buf.send[0])) continue; + + for (long unsigned int x = 0; x < buf.recv[0].csize && *(buf.recv[0].buffer + len + x); x++) *(buf.recv[0].buffer + x) = *(buf.recv[0].buffer + x + len); + buf.recv[0].append_pos -= (unsigned long) len; + *(buf.recv[0].buffer + buf.recv[0].append_pos) = '\0'; + connection.info.state = CONN_ACTIVE; } else LOG(LOG_WARN, "%s", "Received invalid IRC message (see RFC2812)."); } else if (len == -1) { connection.info.state = CONN_RECONNECTING; - } else if (buf.recv.append_pos == sizeof(buf.recv.fd) - 1) { - LOG(LOG_WARN, "%s.", "Read buffer is full and no message could be parsed"); + } else if (buf.recv[0].append_pos == buf.recv[0].csize - 1) { +#ifdef UIRCD_RELAXED_RFC + // TODO: Add resize here +#else /* UIRCD_RELAXED_RFC */ + LOG(LOG_WARN, "%s.", "Receive buffer is full and no message could be parsed."); connection.info.state = CONN_RECONNECTING; +#endif /* UIRCD_RELAXED_RFC */ } } - LOG(LOG_VERBOSE, "%s", "Exiting gracefully."); - return EXIT_SUCCESS; + LOG(LOG_FATAL, "%s", "We shouldn't be here."); + return EXIT_FAILURE; }