Rewrite lots of things, reorganize stuff, add reconnect delays and fix most reconnect hangs or bugs

This commit is contained in:
Alex 2020-08-01 19:45:14 +02:00
parent 70439434b4
commit 77635c120b
Signed by: caskd
GPG Key ID: F92BA85F61F4C173
15 changed files with 228 additions and 272 deletions

View File

@ -16,7 +16,6 @@ add_subdirectory(lib/uIRC)
add_executable(uircd add_executable(uircd
src/main.c src/main.c
src/filesystem.c src/filesystem.c
src/memory.c
src/misc.c src/misc.c
src/net.c src/net.c
src/log.c src/log.c

@ -1 +1 @@
Subproject commit 0f73ea68d8defe4af5fc861e69d9c314c56c80ee Subproject commit 56df6c09d649ef2ab791da21ca706b7ba350e29f

View File

@ -17,42 +17,22 @@
*/ */
#include "filesystem.h" #include "filesystem.h"
int write_log(char* line, char* root, IRC_Message* mesg, Connection* conn)
int create_dirtree(Connection* conn, char* root)
{ {
char path[MAXPATH], bufs[2][MAXPATH], *arrpath[6] = {0}, **ppos = arrpath; char buf[MAXPATH], *arrpath[3] = {0}, **ppos = arrpath;
if (root != NULL) if (root != NULL)
*(ppos++) = root; *(ppos++) = root;
if (snprintf(bufs[0], MAXPATH, "%s.%s", conn->data.addr, conn->data.port) <= 0) if (snprintf(buf, MAXPATH, "%s.%s", conn->data.addr, conn->data.port) <= 0)
return 0; return 0;
cleanup_path_names(bufs[0]); cleanup_path_names(buf);
*(ppos++) = bufs[0]; *(ppos++) = buf;
if ((mesg->cmd == NOTICE || mesg->cmd == PRIVMSG) && mesg->name.nick != NULL && *mesg->name.nick != '\0') { char* dirs[] = {"channel", "user", "global"};
if (strchr("#!&+", *mesg->args[0]) != NULL) for (unsigned int i = 0; i < sizeof(dirs) / sizeof(char*); i++) {
*(ppos++) = "channel"; *ppos = dirs[i];
else if (!mkdir_bottomup(arrpath, NULL))
*(ppos++) = "user";
if (snprintf(bufs[1], MAXPATH, "%s", strcmp(conn->names.nick, mesg->args[0]) ? mesg->args[0] : mesg->name.nick) <= 0)
return 0; return 0;
cleanup_path_names(bufs[1]);
*(ppos++) = bufs[1];
} else
*(ppos++) = "global";
if (!mkdir_bottomup(arrpath))
return 0;
*(ppos++) = "out.log";
if (!assemble_path(arrpath, path, MAXPATH))
return 0;
FILE* logfile;
if ((logfile = fopen(path, "a")) == NULL) {
LOG_ERRNO(LOG_ERROR, "Error on opening log file.", path, conn);
return 0;
} else {
if (line == NULL && mesg != NULL)
Assm_mesg(bufs[0], mesg, MAXPATH);
if (fprintf(logfile, "%s\n", (line != NULL) ? line : bufs[0]) <= 0)
LOG_ERRNO(LOG_ERROR, "Error on writing to the log file", path, conn);
} }
fclose(logfile);
return 1; return 1;
} }
int assemble_path(char** path, char* out, int len) int assemble_path(char** path, char* out, int len)
@ -71,7 +51,7 @@ int assemble_path(char** path, char* out, int len)
} }
return 1; return 1;
} }
int mkdir_bottomup(char** dir) int mkdir_bottomup(char** dir, char* start)
{ {
if (dir == NULL || *dir == NULL) if (dir == NULL || *dir == NULL)
return -1; return -1;
@ -81,8 +61,12 @@ int mkdir_bottomup(char** dir)
if (i != dir && len - (bufp - buf) > 0) if (i != dir && len - (bufp - buf) > 0)
*(bufp++) = '/'; *(bufp++) = '/';
if ((cprint = snprintf(bufp, len - (bufp - buf), "%s", *i)) > 0) { if ((cprint = snprintf(bufp, len - (bufp - buf), "%s", *i)) > 0) {
if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 && errno != EEXIST) if (start == NULL || *i >= start) {
return 0; if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 && errno != EEXIST) {
LOG_ERRNO(LOG_ERROR, "Could not create directory.", buf);
return 0;
}
}
bufp += cprint; bufp += cprint;
} else } else
return 0; return 0;

View File

@ -19,19 +19,19 @@
#include "../lib/uIRC/include/functions.h" #include "../lib/uIRC/include/functions.h"
#include "../lib/uIRC/include/mappings.h" #include "../lib/uIRC/include/mappings.h"
#include "global.h" #include "global.h"
#include "structs.h"
#include "log.h" #include "log.h"
#include "structs.h"
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h>
#ifndef _UIRCD_INCLUDED_FS #ifndef _UIRCD_INCLUDED_FS
#define _UIRCD_INCLUDED_FS #define _UIRCD_INCLUDED_FS
int cleanup_path_names(char* name); int cleanup_path_names(char* name);
int assemble_path(char** path, char* out, int len); int assemble_path(char** path, char* out, int len);
int write_log(char* line, char* root, IRC_Message* mesg, Connection* conn); int mkdir_bottomup(char** dir, char* start);
int mkdir_bottomup(char** dir);
#endif #endif

View File

@ -26,10 +26,6 @@
#define MAXPATH 1024 #define MAXPATH 1024
#define MAXCONN 64 #define MAXCONN 64
extern sig_atomic_t volatile actcon; extern sig_atomic_t volatile run;
extern int totcon;
extern int loglevel; extern int loglevel;
extern Connection cons[MAXCONN];
extern char sendbuf[MAXLINE + 1];
extern int sendbuflen;
#endif #endif

View File

@ -31,8 +31,10 @@
#define LOG_INFO 3 #define LOG_INFO 3
#define LOG_DEBUG 4 #define LOG_DEBUG 4
#define LOG(level, mesg, subj, conn) plog(level, mesg, subj, conn, __LINE__, __FILE__, false) #define LOG(level, mesg, subj) plog(level, mesg, subj, NULL, __LINE__, __FILE__, false)
#define LOG_ERRNO(level, mesg, subj, conn) plog(level, mesg, subj, conn, __LINE__, __FILE__, true) #define LOG_ERRNO(level, mesg, subj) plog(level, mesg, subj, NULL, __LINE__, __FILE__, true)
#define LOG_CONN(level, mesg, subj, conn) plog(level, mesg, subj, conn, __LINE__, __FILE__, false)
#define LOG_CONN_ERRNO(level, mesg, subj, conn) plog(level, mesg, subj, conn, __LINE__, __FILE__, true)
int plog(int level, char* mesg, char* subject, Connection* conn, int line, char* file, bool use_errno); int plog(int level, char* mesg, char* subject, Connection* conn, int line, char* file, bool use_errno);
#endif #endif

View File

@ -18,16 +18,13 @@
#include "main.h" #include "main.h"
sig_atomic_t volatile actcon = 0; sig_atomic_t volatile run = true;
int totcon = 0;
Connection cons[MAXCONN] = {0};
char sendbuf[MAXLINE + 1];
int sendbuflen;
int loglevel = LOG_FATAL; int loglevel = LOG_FATAL;
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
char c, *pos, *root = NULL; char sendbuf[MAXLINE + 1], *quitmsg = "uIRC indev beta", *root = NULL, c, *pos;
int totcon = 0, actcon = 0, sendbuflen, delay = 10;
Connection cons[MAXCONN] = {0};
while ((c = getopt(argc, argv, "C:m:c:l:j:V:vh")) != -1) { while ((c = getopt(argc, argv, "C:m:c:l:j:V:vh")) != -1) {
switch (c) { switch (c) {
case 'c': { case 'c': {
@ -43,11 +40,13 @@ int main(int argc, char* argv[])
cons[totcon].names.real = ((pos = point_after(optarg, ':')) == NULL) ? "uIRC user" : pos; cons[totcon].names.real = ((pos = point_after(optarg, ':')) == NULL) ? "uIRC user" : pos;
cons[totcon].names.user = ((pos = point_after(optarg, '!')) == NULL) ? "uIRC-user" : pos; cons[totcon].names.user = ((pos = point_after(optarg, '!')) == NULL) ? "uIRC-user" : pos;
cons[totcon].names.nick = optarg; cons[totcon].names.nick = optarg;
cons[totcon].fds[0] = -1;
cons[totcon++].connstate = CONN_PENDING; cons[totcon++].connstate = CONN_PENDING;
break; break;
} }
case 'l': root = optarg; break; case 'l': root = optarg; break;
case 'm': break; // TODO: Reserved for quit message case 'm': quitmsg = optarg; break;
case 'd': delay = atoi(optarg); break;
case 'C': break; // TODO: Reserved for config file path case 'C': break; // TODO: Reserved for config file path
case 'V': case 'V':
loglevel = atoi(optarg); loglevel = atoi(optarg);
@ -59,76 +58,123 @@ int main(int argc, char* argv[])
} }
} }
if (totcon < 1) { if (totcon < 1) {
LOG(LOG_FATAL, "No connection was provided.", NULL, NULL); LOG(LOG_FATAL, "No connection was provided.", NULL);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
signal(SIGINT, stop_loop); signal(SIGINT, stop_loop);
signal(SIGTERM, stop_loop); signal(SIGTERM, stop_loop);
IRC_Message buffer = {0}; IRC_Message buffer = {0};
int res; int res;
time_t tmp; int tmp;
time_t ctime;
do { do {
actcon = 0;
for (int t = 0; t < totcon; t++) { for (int t = 0; t < totcon; t++) {
if (cons[t].connstate == CONN_ACTIVE && (tmp = time(NULL)) - cons[t].lastping >= 10) { ctime = time(NULL);
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_PING("UIRC PONG", NULL), sizeof(sendbuf))) > 0) { if (cons[t].connstate == CONN_CLOSING || !run) {
LOG(LOG_DEBUG, "Sending ping...", NULL, &cons[t]); if (cons[t].fds[0] != -1) {
flush_buffer(&cons[t]); if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_QUIT(quitmsg), sizeof(sendbuf))) > 0)
cons[t].lastping = tmp; flush_buffer(sendbuf, sendbuflen, &cons[t]);
} }
} LOG_CONN(LOG_INFO, "Closing connection.", NULL, &cons[t]);
if (!connection_manager(&cons[t])) cons[t].connstate = CONN_CLOSED;
close_sockets(cons[t].fds, 2);
continue;
} else if (cons[t].connstate == CONN_ACTIVE) {
actcon++;
if (ctime - cons[t].lastping >= 10) {
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_PING("UIRC PONG", NULL), sizeof(sendbuf))) > 0) {
LOG_CONN(LOG_DEBUG, "Sending ping...", sendbuf, &cons[t]);
flush_buffer(sendbuf, sendbuflen, &cons[t]);
cons[t].lastping = ctime;
}
}
} else if (cons[t].connstate == CONN_PENDING) {
actcon++;
if (ctime - cons[t].lastconnect < delay)
continue;
cons[t].connstate = CONN_CLOSED;
close_sockets(cons[t].fds, 2);
memset(cons[t].buf.buffer, '\0', 513);
cons[t].buf.nex_line = NULL;
cons[t].buf.append_pos = NULL;
if ((tmp = init_conn(&cons[t])) > 0) {
cons[t].fds[0] = tmp;
cons[t].buf.fd = &cons[t].fds[0];
cons[t].connstate = CONN_ACTIVE;
cons[t].lastconnect = ctime;
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_NICK(cons[t].names.nick), sizeof(sendbuf))) > 0)
flush_buffer(sendbuf, sendbuflen, &cons[t]);
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_USER(cons[t].names.user, cons[t].names.real, 0), sizeof(sendbuf))) > 0)
flush_buffer(sendbuf, sendbuflen, &cons[t]);
LOG_CONN(LOG_INFO, "Connection established!", NULL, &cons[t]);
}
} else if (cons[t].connstate == CONN_CLOSED)
continue; continue;
switch ((res = readline_mem(&cons[t].buf))) { switch ((res = readline_mem(&cons[t].buf))) {
case READLINE_LINE_READY: { case READLINE_LINE_READY: {
LOG(LOG_DEBUG, "Line ready.", cons[t].buf.buffer, &cons[t]); LOG_CONN(LOG_DEBUG, "Line ready.", cons[t].buf.buffer, &cons[t]);
char origmsg[513]; char origmsg[513];
strncpy(origmsg, cons[t].buf.buffer, 512); strncpy(origmsg, cons[t].buf.buffer, 512);
if (Tok_mesg(cons[t].buf.buffer, &buffer) == 1) { if (Tok_mesg(cons[t].buf.buffer, &buffer) == 1) {
LOG(LOG_DEBUG, "Tokenized message successfully.", NULL, &cons[t]); LOG(LOG_DEBUG, "Tokenized message successfully.", NULL);
handle_irc_message(&buffer, &cons[t]); switch (buffer.cmd) {
write_log(origmsg, root, &buffer, &cons[t]); case (PING): {
LOG_CONN(LOG_DEBUG, "Auto-replying to ping.", buffer.args[0], &cons[t]);
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_PONG(buffer.args[0], NULL), sizeof(sendbuf))) > 0)
flush_buffer(sendbuf, sendbuflen, &cons[t]);
break;
}
/* Autojoin channels from current connection on first response from the server */
case (RPL_WELCOME): {
LOG_CONN(LOG_INFO, "Auto-joining channels.", cons[t].data.chans, &cons[t]);
IRC_Message* mesg = Assm_cmd_JOIN(cons[t].data.chans, NULL);
if ((sendbuflen = Assm_mesg(sendbuf, mesg, sizeof(sendbuf))) > 0)
flush_buffer(sendbuf, sendbuflen, &cons[t]);
break;
}
case (RPL_BOUNCE): {
break; // TODO: Make bounces work
}
}
} else } else
LOG(LOG_WARN, "Received invalid IRC message (see RFC2812)", origmsg, &cons[t]); LOG_CONN(LOG_WARN, "Received invalid IRC message (see RFC2812)", origmsg, &cons[t]);
break; break;
} }
case READLINE_TRYAGAIN: break; case READLINE_TRYAGAIN: break;
case READLINE_INTERRUPTED: { case READLINE_INTERRUPTED: {
LOG(LOG_DEBUG, "Caught interrupt. Shutting down.", NULL, &cons[t]); LOG(LOG_DEBUG, "Caught interrupt. Shutting down.", NULL);
actcon = 0; run = false;
break; break;
} }
default: { default: {
switch (res) { switch (res) {
case READLINE_EOF: case READLINE_EOF:
cons[t].connstate = CONN_PENDING; cons[t].connstate = CONN_PENDING;
LOG(LOG_INFO, "Encountered EOF. Reconnecting...", NULL, &cons[t]); LOG_CONN(LOG_INFO, "Encountered EOF. Reconnecting...", NULL, &cons[t]);
break; break;
case READLINE_FULL: case READLINE_FULL:
cons[t].connstate = CONN_PENDING; cons[t].connstate = CONN_PENDING;
LOG_ERRNO(LOG_ERROR, "Buffer is full and no line can be found. Reinitializing...", NULL, &cons[t]); LOG_CONN_ERRNO(LOG_ERROR, "Buffer is full and no line can be found. Reinitializing...", NULL, &cons[t]);
break; break;
case READLINE_SOCKET_FAIL: case READLINE_SOCKET_FAIL:
cons[t].connstate = CONN_PENDING; cons[t].connstate = CONN_PENDING;
LOG_ERRNO(LOG_WARN, "Socket was invalid or closed. Reconnecting...", NULL, &cons[t]); LOG_CONN_ERRNO(LOG_WARN, "Socket was invalid or closed. Reconnecting...", NULL, &cons[t]);
break; break;
case READLINE_POLL_FAIL: case READLINE_POLL_FAIL:
cons[t].connstate = CONN_PENDING; cons[t].connstate = CONN_PENDING;
LOG_ERRNO(LOG_WARN, "Failed to poll the socket. Reconnecting...", NULL, &cons[t]); LOG_CONN_ERRNO(LOG_WARN, "Failed to poll the socket. Reconnecting...", NULL, &cons[t]);
break; break;
case READLINE_UNKNOWN_ERR:
default: default:
cons[t].connstate = CONN_CLOSING; cons[t].connstate = CONN_CLOSING;
LOG_ERRNO(LOG_ERROR, "An unknown error happened. Disabling connection.", NULL, &cons[t]); LOG_CONN_ERRNO(LOG_ERROR, "An unknown error happened. Disabling connection.", NULL, &cons[t]);
break; break;
} }
break; break;
} }
} }
if (actcon <= 0) {
LOG(LOG_INFO, "No more active connections. Closing.", NULL, &cons[t]);
break;
}
} }
} while (actcon > 0); } while (actcon > 0);
close_gracefully();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -11,19 +11,17 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with uIRCd. If not, see <https://www.gnu.org/licenses/>. * along with uIRCd. If not, see <https://www.gnu.org/licenses/>.
*/ */
#define UIRC_HELPERS
#include "../lib/uIRC/include/uirc.h"
#include "filesystem.h"
#include "global.h" #include "global.h"
#include "log.h" #include "log.h"
#include "memory.h"
#include "misc.h" #include "misc.h"
#include "net.h" #include "net.h"
#include "memory.h"
#include "structs.h"
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
@ -31,5 +29,7 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#define UIRC_HELPERS
#include "../lib/uIRC/include/uirc.h"
#define VERSION "alpha" #define VERSION "alpha"

View File

@ -1,65 +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 <https://www.gnu.org/licenses/>.
*/
#include "memory.h"
int readline_mem(Buffer_Info* bs)
{
if (bs->fd == NULL || *(bs->fd) < 0)
return READLINE_SOCKET_FAIL;
// Read to the last position in the buffer or at the beginning if buffer was just initiated
int b_read = 0;
char* nl = NULL;
if (bs->append_pos == NULL)
bs->append_pos = bs->buffer;
for (;;) {
char tmpcpy[513];
if (bs->nex_line != NULL) {
strcpy(tmpcpy, bs->nex_line);
strcpy(bs->buffer, tmpcpy);
bs->append_pos -= (bs->nex_line - bs->buffer);
bs->nex_line = NULL;
}
if ((nl = strchr(bs->buffer, '\n')) != NULL) {
if (nl > bs->buffer && *(nl - 1) == '\r')
*(nl - 1) = '\0';
*nl = '\0';
bs->nex_line = nl + 1;
break;
} else if (bs->append_pos == bs->buffer + 512)
return READLINE_FULL;
struct pollfd pollinf[] = {{.fd = *bs->fd, .events = POLLRDNORM}};
switch (poll(pollinf, 1, 10)) {
case -1: {
if (errno != EINTR) // We don't throw a error on a damn ^C
return READLINE_POLL_FAIL;
return READLINE_INTERRUPTED;
}
default: {
if (pollinf[0].revents ^ POLLRDNORM)
return READLINE_TRYAGAIN;
break;
}
}
if ((b_read = read(*bs->fd, bs->append_pos, 512 - (bs->append_pos - bs->buffer))) == -1)
return READLINE_EOF;
bs->append_pos += b_read;
*bs->append_pos = '\0';
}
return READLINE_LINE_READY;
}

View File

@ -1,40 +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 <https://www.gnu.org/licenses/>.
*/
#include "structs.h"
#include <errno.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef _UIRCD_INCLUDED_MEM
#define _UIRCD_INCLUDED_MEM
#define READLINE_LINE_READY 1
#define READLINE_TRYAGAIN 0
#define READLINE_EOF -1
#define READLINE_FULL -2
#define READLINE_POLL_FAIL -4
#define READLINE_SOCKET_FAIL -5
#define READLINE_INTERRUPTED -6
int readline_mem(Buffer_Info* bs);
#endif

View File

@ -20,7 +20,7 @@
void stop_loop() void stop_loop()
{ {
actcon = 0; run = 0;
} }
char* point_after(char* string, char point) char* point_after(char* string, char point)
/* /*
@ -33,26 +33,3 @@ char* point_after(char* string, char point)
*(ret++) = '\0'; *(ret++) = '\0';
return ret; return ret;
} }
int handle_irc_message(IRC_Message* mesg, Connection* conn)
{
switch (mesg->cmd) {
case (PING): {
LOG(LOG_DEBUG, "Responding to ping.", mesg->trailing, conn);
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_PONG(mesg->trailing, NULL), sizeof(sendbuf))) > 0)
flush_buffer(conn);
break;
}
/* Autojoin channels from current connection on first response from the server */
case (RPL_WELCOME): {
LOG(LOG_DEBUG, "Auto-joining channels.", conn->data.chans, conn);
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_JOIN(conn->data.chans, NULL), sizeof(sendbuf))) > 0)
flush_buffer(conn);
break;
}
case (RPL_BOUNCE): {
break; // TODO: Make bounces work
}
default: return 0;
}
return 1;
}

View File

@ -19,7 +19,6 @@
#include "../lib/uIRC/include/functions.h" #include "../lib/uIRC/include/functions.h"
#include "../lib/uIRC/include/mappings.h" #include "../lib/uIRC/include/mappings.h"
#include "global.h" #include "global.h"
#include "net.h"
#include "structs.h" #include "structs.h"
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
@ -33,5 +32,4 @@
char* point_after(char* string, char point); char* point_after(char* string, char point);
void stop_loop(); void stop_loop();
int handle_irc_message(IRC_Message* mesg, Connection* conn);
#endif #endif

136
src/net.c
View File

@ -27,7 +27,7 @@ signed int init_conn(Connection* info)
if ((getaddrres = getaddrinfo(info->data.addr, info->data.port, NULL, &conn)) == 0) { if ((getaddrres = getaddrinfo(info->data.addr, info->data.port, NULL, &conn)) == 0) {
break; break;
} else { } else {
LOG_ERRNO(LOG_ERROR, "Failed to get address info.", NULL, info); LOG_CONN_ERRNO(LOG_ERROR, "Failed to get address info.", NULL, info);
if (i == 2 || (getaddrres != EAI_AGAIN && getaddrres != EAI_NONAME)) { if (i == 2 || (getaddrres != EAI_AGAIN && getaddrres != EAI_NONAME)) {
freeaddrinfo(conn); freeaddrinfo(conn);
return -1; return -1;
@ -41,7 +41,7 @@ signed int init_conn(Connection* info)
return -1; return -1;
} }
if ((sockfd = socket(conn->ai_family, conn->ai_socktype, conn->ai_protocol)) < 0) { if ((sockfd = socket(conn->ai_family, conn->ai_socktype, conn->ai_protocol)) < 0) {
LOG_ERRNO(LOG_ERROR, "Failed to open a socket.", NULL, info); LOG_CONN_ERRNO(LOG_ERROR, "Failed to open a socket.", NULL, info);
return -1; return -1;
} }
for (unsigned int i = 0; i < 3; i++) { for (unsigned int i = 0; i < 3; i++) {
@ -49,7 +49,7 @@ signed int init_conn(Connection* info)
if (connectres == 0 || (connectres == -1 && errno == EINTR)) { if (connectres == 0 || (connectres == -1 && errno == EINTR)) {
break; break;
} else { } else {
LOG_ERRNO(LOG_ERROR, "Failed to connect to host.", NULL, info); LOG_CONN_ERRNO(LOG_ERROR, "Failed to connect to host.", NULL, info);
if (i == 2 || (connectres != EADDRNOTAVAIL && connectres != ETIMEDOUT && connectres != ECONNRESET && connectres != ECONNREFUSED)) { if (i == 2 || (connectres != EADDRNOTAVAIL && connectres != ETIMEDOUT && connectres != ECONNRESET && connectres != ECONNREFUSED)) {
close(sockfd); close(sockfd);
return -1; return -1;
@ -64,64 +64,104 @@ signed int init_conn(Connection* info)
} }
return sockfd; return sockfd;
} }
int connection_manager(Connection* conn)
int readline_mem(Buffer_Info* bs)
{ {
switch (conn->connstate) { if (bs->fd == NULL || *(bs->fd) < 0)
case CONN_PENDING: { return READLINE_SOCKET_FAIL;
int tmp; // Read to the last position in the buffer or at the beginning if buffer was just initiated
conn->connstate = CONN_CLOSED; int b_read = 0;
conn->fds[0] = -1; char* nl = NULL;
memset(conn->buf.buffer, '\0', 513); if (bs->append_pos == NULL)
conn->buf.nex_line = NULL; bs->append_pos = bs->buffer;
conn->buf.append_pos = NULL; for (;;) {
if ((tmp = init_conn(conn)) > 0) { char tmpcpy[513];
conn->fds[0] = tmp; if (!run)
conn->buf.fd = &conn->fds[0]; return READLINE_INTERRUPTED;
conn->connstate = CONN_ACTIVE; if (bs->nex_line != NULL) {
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_NICK(conn->names.nick), sizeof(sendbuf))) > 0) strcpy(tmpcpy, bs->nex_line);
flush_buffer(conn); strcpy(bs->buffer, tmpcpy);
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_USER(conn->names.user, conn->names.real, 0), sizeof(sendbuf))) > 0) bs->append_pos -= (bs->nex_line - bs->buffer);
flush_buffer(conn); bs->nex_line = NULL;
actcon++;
LOG(LOG_INFO, "Connection established!", NULL, conn);
return 1;
}
return 0;
} }
case CONN_CLOSING: { if ((nl = strchr(bs->buffer, '\n')) != NULL) {
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_QUIT(">> uIRC beta"), sizeof(sendbuf))) > 0) if (nl > bs->buffer && *(nl - 1) == '\r')
flush_buffer(conn); *(nl - 1) = '\0';
LOG(LOG_INFO, "Closing connection.", NULL, conn); *nl = '\0';
conn->connstate = CONN_CLOSED; bs->nex_line = nl + 1;
close(conn->fds[0]); break;
conn->fds[0] = -1; } else if (bs->append_pos == bs->buffer + 512)
return 0; return READLINE_FULL;
switch (poll_conn(*bs->fd)) {
case PCON_ERR: return READLINE_POLL_FAIL;
case PCON_INTER: return READLINE_INTERRUPTED;
case PCON_NREADY: return READLINE_TRYAGAIN;
case PCON_READY: break;
default: return READLINE_POLL_FAIL;
} }
case CONN_CLOSED: return 0; if ((b_read = read(*bs->fd, bs->append_pos, 512 - (bs->append_pos - bs->buffer))) == -1) {
default: return 1; if (errno == EINTR)
return READLINE_INTERRUPTED;
else if (errno == ENOTCONN || errno == ETIMEDOUT || errno == ECONNRESET)
return READLINE_EOF;
else
return READLINE_UNKNOWN_ERR;
}
bs->append_pos += b_read;
*bs->append_pos = '\0';
if (b_read == 0)
return READLINE_EOF;
} }
return READLINE_LINE_READY;
} }
void close_gracefully()
{ void flush_buffer(char* send_buf, int send_buflen, Connection* conn)
for (int x = 0; x < totcon; x++) {
if (cons[x].connstate >= 0)
cons[x].connstate = CONN_CLOSING;
connection_manager(&cons[x]);
}
}
void flush_buffer(Connection* conn)
{ {
int res; int res;
char* pos = sendbuf; char* pos = send_buf;
for (;;) { for (;;) {
if ((res = write(conn->fds[0], pos, sendbuflen - (pos - sendbuf))) != sendbuflen - (pos - sendbuf)) { if ((res = write(conn->fds[0], pos, send_buflen - (pos - send_buf))) != send_buflen - (pos - send_buf)) {
if (res == -1) { if (res == -1) {
conn->connstate = CONN_PENDING; conn->connstate = CONN_PENDING;
LOG(LOG_ERROR, "Couldn't flush send buffer over network. Will attempt to reconnect.", NULL, conn); LOG_ERRNO(LOG_ERROR, "Couldn't flush send buffer over network. Will attempt to reconnect.", NULL);
break; break;
} else } else {
LOG_CONN(LOG_DEBUG, "Sent a chunked message.", NULL, conn);
pos += res; pos += res;
}
} else } else
break; break;
} }
LOG_CONN(LOG_DEBUG, "Message sent.", send_buf, conn);
}
int poll_conn(int fd)
{
struct pollfd pollinf[] = {{.fd = fd, .events = POLLIN}};
switch (poll(pollinf, 1, 10)) {
case -1: {
if (errno != EINTR) // We don't throw a error on a damn ^C
return PCON_ERR;
return PCON_INTER;
}
default: {
if (pollinf[0].revents & POLLIN)
return PCON_READY;
else if (pollinf[0].revents == 0)
return PCON_NREADY;
return PCON_ERR;
}
}
return PCON_ERR;
}
int close_sockets(int* fds, int cnt)
{
int res;
for (int i = 0; i < cnt; i++) {
if ((res = close(fds[i])) == -1)
return 0;
fds[i] = -1;
}
return 1;
} }

View File

@ -16,33 +16,51 @@
* along with uIRCd. If not, see <https://www.gnu.org/licenses/>. * along with uIRCd. If not, see <https://www.gnu.org/licenses/>.
*/ */
#define UIRC_HELPERS
#include "../lib/uIRC/include/functions.h" #include "../lib/uIRC/include/functions.h"
#include "../lib/uIRC/include/helpers.h"
#include "../lib/uIRC/include/mappings.h"
#include "global.h" #include "global.h"
#include "log.h" #include "log.h"
#include "structs.h" #include "structs.h"
#include <errno.h> #include <errno.h>
#include <netdb.h> #include <netdb.h>
#include <poll.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#define UIRC_HELPERS
#include "../lib/uIRC/include/helpers.h"
#ifndef _UIRCD_INCLUDED_NETWORK #ifndef _UIRCD_INCLUDED_NETWORK
#define _UIRCD_INCLUDED_NETWORK #define _UIRCD_INCLUDED_NETWORK
#define CONN_ACTIVE 1 #define CONN_ACTIVE 1
#define CONN_PENDING -1 #define CONN_PENDING 0
#define CONN_CLOSING -2 #define CONN_CLOSING -1
#define CONN_CLOSED -3 #define CONN_CLOSED -2
#define PCON_READY 1
#define PCON_NREADY 0
#define PCON_INTER -1
#define PCON_ERR -2
#define READLINE_LINE_READY 1
#define READLINE_TRYAGAIN 0
#define READLINE_EOF -1
#define READLINE_FULL -2
#define READLINE_POLL_FAIL -4
#define READLINE_SOCKET_FAIL -5
#define READLINE_INTERRUPTED -6
#define READLINE_UNKNOWN_ERR -7
signed int init_conn(Connection* info); signed int init_conn(Connection* info);
int connection_manager(Connection* conn); int readline_mem(Buffer_Info* bs);
void flush_buffer(Connection* conn); void flush_buffer(char* send_buf, int send_buflen, Connection* conn);
void close_gracefully(); int poll_conn(int fd);
int close_sockets(int* fds, int cnt);
#endif #endif

View File

@ -40,5 +40,6 @@ typedef struct {
signed int fds[2]; signed int fds[2];
signed int connstate; signed int connstate;
time_t lastping; time_t lastping;
time_t lastconnect;
} Connection; } Connection;
#endif #endif