Prepare features that require breaking RFC2812

This commit is contained in:
Alex D. 2021-01-12 17:04:21 +00:00
parent f4b2cb90f3
commit 7b9b39e1a9
Signed by: caskd
GPG Key ID: F92BA85F61F4C173
11 changed files with 256 additions and 253 deletions

View File

@ -17,7 +17,7 @@ BinPackParameters: 'false'
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Linux
BreakStringLiterals: 'false'
ColumnLimit: '150'
ColumnLimit: '200'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
Cpp11BracedListStyle: 'false'
IncludeBlocks: Regroup

View File

@ -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

View File

@ -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 |

View File

@ -20,8 +20,9 @@
#include "logging.h"
#include <errno.h> // errno
#include <stdio.h> // fprintf()
#include <errno.h> // errno
#include <stdio.h> // fprintf()
#include <stdlib.h>
#include <string.h> // strerror()
#include <sys/types.h> // size_t ssize_t
#include <unistd.h> // 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

View File

@ -16,8 +16,29 @@
* along with uIRCd. If not, see <https://www.gnu.org/licenses/>.
*/
#include "limits.h"
#include <stdbool.h> // bool
#include <sys/types.h> // 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 */

View File

@ -24,10 +24,10 @@
#include <errno.h> // errno
#include <stdbool.h> // bool
#include <stdio.h> // snprintf()
#include <stdlib.h> // malloc() realloc()
#include <string.h> // strerror()
#include <sys/socket.h> // sockaddr connect()
#include <sys/types.h> // size_t ssize_t socklen_t
#include <stdlib.h> // 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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -16,6 +16,7 @@
* along with uIRCd. If not, see <https://www.gnu.org/licenses/>.
*/
#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 */

View File

@ -29,7 +29,7 @@
#include <errno.h> // errno
#include <fcntl.h> // O_NONBLOCK
#include <stdio.h> // printf()
#include <stdlib.h> // srand()
#include <stdlib.h> // EXIT_SUCCESS EXIT_FAILURE
#include <string.h> // strerror()
#include <sys/types.h> // time_t size_t
#include <time.h> // 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;
}