Initial commit

This commit is contained in:
Alex 2020-07-17 15:47:25 +02:00
commit dcb642b15e
Signed by: caskd
GPG Key ID: F92BA85F61F4C173
8 changed files with 437 additions and 0 deletions

19
.clang-format Normal file
View File

@ -0,0 +1,19 @@
---
Language: Cpp
Standard: Cpp11
BasedOnStyle: LLVM
TabWidth: 8
IndentWidth: 8
UseTab: ForIndentation
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Linux
IndentCaseLabels: true
PointerAlignment: Left
ColumnLimit: 0
...

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
build/
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
Makefile
.swp

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "lib/uIRC"]
path = lib/uIRC
url = https://git.redxen.eu/caskd/uIRC

16
CMakeLists.txt Normal file
View File

@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.16)
project(microircd LANGUAGES C)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/archive)
set(USE_HELPERS TRUE)
add_subdirectory(lib/uIRC)
add_executable(uircd src/main.c src/net.c)
target_link_libraries(uircd uirc)
set_property(TARGET uircd PROPERTY C_STANDARD 99)
install(TARGETS uircd RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})

1
lib/uIRC Submodule

@ -0,0 +1 @@
Subproject commit 08b6b0f18709b90c291b5dc87f82c57c55c53b82

169
src/main.c Normal file
View File

@ -0,0 +1,169 @@
/*
* 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 "main.h"
sig_atomic_t volatile run =
true; // Used to leave the main loop gracefully without using goto statements
// even when catching signals
char sendbuf[MAXLINE + 1];
int sendbuflen;
int main(int argc, char* argv[])
{
char c;
Connection cons[MAXCONN] = {0};
unsigned int conind = 0;
char* pos;
while ((c = getopt(argc, argv, "c:Vvh")) != -1) {
switch (c) {
case 'c': {
cons[conind].data.chans = point_after(optarg, ',');
if ((cons[conind].data.port = point_after(optarg, '/')) != NULL) {
if (*cons[conind].data.port == '+') {
cons[conind].data.ssl = true;
cons[conind].data.port++;
}
} else
cons[conind].data.port = "6667";
cons[conind].data.addr = ((pos = point_after(optarg, '@')) == NULL) ? "localhost" : pos;
cons[conind].names.real = ((pos = point_after(optarg, ':')) == NULL) ? "uIRC user" : pos;
cons[conind].names.user = ((pos = point_after(optarg, '!')) == NULL) ? "uIRC-user" : pos;
cons[conind++].names.nick = optarg;
break;
}
case 'C': break; // TODO: Reserved for config file path
case 'V': break; // TODO: Reserved for verbose output
case 'v':
printf("uIRCd version %s\n", VERSION);
return EXIT_SUCCESS;
case 'h': return EXIT_SUCCESS; // TODO: Reserved for help
}
}
if (conind < 1) {
fprintf(stderr, "No connection provided.\n");
return EXIT_FAILURE;
}
signal(SIGINT, close_gracefully);
signal(SIGTERM, close_gracefully);
IRC_Message buffer = {0};
int res;
char path[MAXPATH + 1];
CONNECTIONS(i)
{
init_conn(&cons[i]);
cons[i].buf.fd = &cons[i].sockfd[0];
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_NICK(cons[i].names.nick))) > 0)
write(cons[i].sockfd[0], sendbuf, sendbuflen);
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_USER(cons[i].names.user, cons[i].names.real, 0))) > 0)
write(cons[i].sockfd[0], sendbuf, sendbuflen);
}
while (run) {
CONNECTIONS(i)
{
if (cons[i].sockfd[0] < 0)
continue;
switch ((res = readline_mem(&cons[i].buf))) {
case READLINE_LINE_READY: {
printf("%s\n", cons[i].buf.cur_line);
if (Tok_mesg(cons[i].buf.cur_line, &buffer) == 1) {
handle_irc_message(&buffer, cons[i].sockfd[0], &cons[i]);
} else
fprintf(stderr, "Received invalid IRC message (see RFC2812)\n");
break;
}
case READLINE_TRYAGAIN: break;
default: {
if (close(cons[i].sockfd[0]))
fprintf(stderr, "Error on closing socket %i. %s (%i)\n", cons[i].sockfd[0], strerror(errno), errno);
cons[i].sockfd[0] = -1;
char* resp = NULL;
switch (res) {
case READLINE_EOF: resp = "The connection was closed."; break;
case READLINE_BUFINIT_FAIL: resp = "The buffer could not be initialised."; break;
case READLINE_REALLOC_FAIL: resp = "The buffer could not be extended or reallocated."; break;
case READLINE_SOCKET_FAIL: resp = "The socket was invalid or closed."; break;
case READLINE_POLL_FAIL: resp = "The socket could not be polled."; break;
default: resp = "An unknown error happened."; break;
}
fprintf(stderr, "%s Closing socket %i.\n", resp, cons[i].sockfd[0]);
break;
}
}
}
}
CONNECTIONS(i)
{
if (cons[i].sockfd[0] >= 0) {
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_QUIT("Me voy a masturbar chau~~"))) > 0)
write(cons[i].sockfd[0], sendbuf, sendbuflen);
}
for (int s = 0; s <= 1; s++) {
if (cons[i].sockfd[s] >= 0) {
if (close(cons[i].sockfd[s]))
fprintf(stderr, "Error on closing %s socket %i. %s (%i)\n", (s) ? "log" : "connection", i, strerror(errno), errno);
}
}
}
return EXIT_SUCCESS;
}
int handle_irc_message(IRC_Message* mesg, int socket, Connection* conn)
{
switch (mesg->cmd) {
case (PING): {
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_PONG(mesg->trailing, NULL))) > 0)
write(socket, sendbuf, sendbuflen);
break;
}
/* Autojoin channels from current connection on first response from the server */
case (RPL_WELCOME): {
if ((sendbuflen = Assm_mesg(sendbuf, Assm_cmd_JOIN(conn->data.chans, NULL))) > 0)
write(socket, sendbuf, sendbuflen);
break;
}
case (RPL_BOUNCE): {
break; // TODO: Make bounces work
}
default: return 0;
}
return 1;
}
char* point_after(char* string, char point)
/*
* Used to tokenize right to left based on characters
* Replaces point with '\0' if found and points after it
*/
{
char* ret = strchr(string, point);
if (ret != NULL)
*(ret++) = '\0';
return ret;
}
void close_gracefully()
{
run = false;
}
void cleanup_chan_names(char* chan)
{
for (; *chan; chan++) {
if (isalpha(*chan))
*chan = tolower(*chan);
else if (strchr(".&?!*+-", *chan) != NULL && !isdigit(*chan))
*chan = '_';
}
}

80
src/main.h Normal file
View File

@ -0,0 +1,80 @@
/*
* 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/>.
*/
#define UIRC_HELPERS
#include "../lib/uIRC/include/uirc.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#define CONNECTIONS(var) for (unsigned int var = 0; var < conind; var++)
#define MAXLINE 512
#define MAXPATH 1024
#define VERSION "alpha"
#define MAXCONN 64
#define READLINE_LINE_READY 1
#define READLINE_TRYAGAIN 0
#define READLINE_EOF -1
#define READLINE_BUFINIT_FAIL -2
#define READLINE_REALLOC_FAIL -3
#define READLINE_POLL_FAIL -4
#define READLINE_SOCKET_FAIL -5
typedef struct conndata {
char* addr;
char* port;
char* chans;
bool ssl;
} Connection_Data;
typedef struct membuffer_info {
char* buffer;
char* cur_line;
char* nex_line;
char* append_pos;
char* end_pos;
int* fd;
} Buffer_Info;
typedef struct irc_connection {
IRC_User names;
Connection_Data data;
Buffer_Info buf;
int sockfd[2];
} Connection;
signed int init_conn(struct irc_connection* info);
char* point_after(char* string, char point);
int readline_mem(struct membuffer_info* bs);
int init_buff(struct membuffer_info* bs, int size);
int realloc_full(struct membuffer_info* bs, int size);
signed int init_conn(struct irc_connection* info);
int handle_irc_message(IRC_Message* mesg, int socket, Connection* conn);
void close_gracefully();
void cleanup_chan_names(char* chan);

143
src/net.c Normal file
View File

@ -0,0 +1,143 @@
/*
* 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 "main.h"
signed int init_conn(struct irc_connection* info)
{
const struct timespec retry_timeout = {3, 0.0L};
int sockfd, getaddrres, connectres;
if (info->data.addr == NULL) return -1;
struct addrinfo* conn;
for (unsigned int i = 0; i < 3; i++) {
getaddrres = getaddrinfo(info->data.addr, info->data.port, NULL, &conn);
if (getaddrres == 0) {
break;
} else {
fprintf(stderr, "%s %s:%s. %s (%i)\n", "Failed to get the address info for", info->data.addr, info->data.port, gai_strerror(getaddrres), getaddrres);
if (i == 2 || (getaddrres != EAI_AGAIN && getaddrres != EAI_NONAME)) {
freeaddrinfo(conn);
return -1;
} else {
nanosleep(&retry_timeout, NULL); /* Signals don't bother us */
}
}
}
if (getaddrres != 0) {
freeaddrinfo(conn);
return -1;
}
if ((sockfd = socket(conn->ai_family, conn->ai_socktype, conn->ai_protocol)) < 0) {
fprintf(stderr, "%s. %s (%i)\n", "Failed to open the socket", strerror(errno), errno);
return -1;
}
for (unsigned int i = 0; i < 3; i++) {
connectres = connect(sockfd, conn->ai_addr, conn->ai_addrlen);
if (connectres == 0 || connectres == EINTR) { /* TODO: Lookup if EINTR expects async conn OR connects async, assuming connects */
break;
} else {
fprintf(stderr, "%s %s:%s. %s (%i)\n", "Failed to connect to host", info->data.addr, info->data.port, strerror(errno), errno);
if (i == 2 || (connectres != EADDRNOTAVAIL && connectres != ETIMEDOUT && connectres != ECONNRESET && connectres != ECONNREFUSED)) {
close(sockfd);
return -1;
} else {
nanosleep(&retry_timeout, NULL);
}
}
}
if (connectres == -1) {
close(sockfd);
return -1;
}
info->sockfd[0] = sockfd;
return sockfd;
}
int readline_mem(struct membuffer_info* bs)
{
const int step = 513;
bool linefin = false;
if (*bs->fd <= 0)
return READLINE_SOCKET_FAIL;
// Initiate buffer with the step size
if (bs->buffer == NULL) {
if (!init_buff(bs, step))
return READLINE_BUFINIT_FAIL;
}
// Read to the last position in the buffer or at the beginning if buffer was just initiated
int b_read;
char* nl;
while (!linefin) {
if ((nl = strchr(bs->nex_line, '\n')) != NULL) {
*(nl - 1) = '\0';
bs->cur_line = bs->nex_line;
bs->nex_line = nl + 1;
break;
}
if (!realloc_full(bs, step))
return READLINE_REALLOC_FAIL;
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;
}
default: {
if (pollinf[0].revents ^ POLLRDNORM)
return READLINE_TRYAGAIN;
}
}
b_read = read(*bs->fd, bs->append_pos, bs->end_pos - bs->append_pos);
if (b_read == EOF)
return READLINE_EOF;
bs->append_pos += b_read;
}
if (b_read == EOF)
return READLINE_EOF;
return READLINE_LINE_READY;
}
int init_buff(struct membuffer_info* bs, int size)
{
bs->buffer = malloc(size * sizeof(char));
bs->end_pos = bs->buffer + size - 1;
bs->append_pos = bs->buffer;
bs->nex_line = bs->buffer;
if (bs->buffer == NULL)
return 0;
return 1;
}
int realloc_full(struct membuffer_info* bs, int size)
{
if (bs->append_pos == bs->end_pos) {
char* newpoint;
bs->end_pos += size;
newpoint = realloc(bs->buffer, (bs->end_pos - bs->buffer) * sizeof(char));
if (newpoint != NULL) {
int offset = newpoint - bs->buffer;
// Move all pointers or you will have a BAAD time (count: 2)
bs->append_pos += offset;
bs->nex_line += offset;
bs->cur_line += offset;
bs->end_pos += offset;
bs->buffer = newpoint;
return 1;
}
return 0;
}
return 2; // Nothing to do, the buffer is not full yet
}