Rewrite a big chunk of the code, split the code into functions and clean it up, fix undefined behaviours and more

This commit is contained in:
Alex D. 2020-10-02 14:38:11 +02:00
parent f0bfff775d
commit a4d1e501eb
Signed by: caskd
GPG Key ID: F92BA85F61F4C173
16 changed files with 666 additions and 375 deletions

View File

@ -7,7 +7,7 @@ IndentWidth: 8
UseTab: ForIndentation UseTab: ForIndentation
AllowShortBlocksOnASingleLine: true AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All AllowShortFunctionsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: true AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true AllowShortLoopsOnASingleLine: true
BreakBeforeBinaryOperators: NonAssignment BreakBeforeBinaryOperators: NonAssignment

180
.clang-tidy Normal file
View File

@ -0,0 +1,180 @@
---
Checks: 'clang-diagnostic-*,clang-analyzer-*,clang-diagnostic-*,clang-analyzer-*,clang-diagnostic-*,clang-analyzer-*,-*,clang-analyzer-core.*,clang-analyzer-optin.performance.*,clang-analyzer-optin.portability.*,clang-analyzer-nullability.*,clang-analyzer-security.*,clang-analyzer-unix.*,bugprone-*,misc-*,performance-*,readability-*,-*,clang-analyzer-core.*,clang-analyzer-optin.performance.*,clang-analyzer-optin.portability.*,clang-analyzer-nullability.*,clang-analyzer-security.*,clang-analyzer-unix.*,bugprone-*,misc-*,performance-*,readability-*,-readability-isolate-declaration,-readability-else-after-return,-readability-braces-around-statements,-*,clang-analyzer-core.*,clang-analyzer-optin.performance.*,clang-analyzer-optin.portability.*,clang-analyzer-nullability.*,clang-analyzer-security.*,clang-analyzer-unix.*,bugprone-*,misc-*,performance-*,readability-*,-readability-isolate-declaration,-readability-else-after-return,-readability-braces-around-statements,-readability-magic-numbers'
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: none
User: caskd
CheckOptions:
- key: bugprone-argument-comment.CommentBoolLiterals
value: '0'
- key: bugprone-argument-comment.CommentCharacterLiterals
value: '0'
- key: bugprone-argument-comment.CommentFloatLiterals
value: '0'
- key: bugprone-argument-comment.CommentIntegerLiterals
value: '0'
- key: bugprone-argument-comment.CommentNullPtrs
value: '0'
- key: bugprone-argument-comment.CommentStringLiterals
value: '0'
- key: bugprone-argument-comment.CommentUserDefinedLiterals
value: '0'
- key: bugprone-argument-comment.IgnoreSingleArgument
value: '0'
- key: bugprone-argument-comment.StrictMode
value: '0'
- key: bugprone-assert-side-effect.AssertMacros
value: assert
- key: bugprone-assert-side-effect.CheckFunctionCalls
value: '0'
- key: bugprone-dangling-handle.HandleClasses
value: 'std::basic_string_view;std::experimental::basic_string_view'
- key: bugprone-dynamic-static-initializers.HeaderFileExtensions
value: ',h,hh,hpp,hxx'
- key: bugprone-exception-escape.FunctionsThatShouldNotThrow
value: ''
- key: bugprone-exception-escape.IgnoredExceptions
value: ''
- key: bugprone-misplaced-widening-cast.CheckImplicitCasts
value: '0'
- key: bugprone-not-null-terminated-result.WantToUseSafeFunctions
value: '1'
- key: bugprone-signed-char-misuse.CharTypdefsToIgnore
value: ''
- key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant
value: '1'
- key: bugprone-sizeof-expression.WarnOnSizeOfConstant
value: '1'
- key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression
value: '0'
- key: bugprone-sizeof-expression.WarnOnSizeOfThis
value: '1'
- key: bugprone-string-constructor.LargeLengthThreshold
value: '8388608'
- key: bugprone-string-constructor.WarnOnLargeLength
value: '1'
- key: bugprone-suspicious-enum-usage.StrictMode
value: '0'
- key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens
value: '5'
- key: bugprone-suspicious-missing-comma.RatioThreshold
value: '0.200000'
- key: bugprone-suspicious-missing-comma.SizeThreshold
value: '5'
- key: bugprone-suspicious-string-compare.StringCompareLikeFunctions
value: ''
- key: bugprone-suspicious-string-compare.WarnOnImplicitComparison
value: '1'
- key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison
value: '0'
- key: bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit
value: '16'
- key: bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField
value: '1'
- key: bugprone-unused-return-value.CheckedFunctions
value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty'
- key: cert-dcl16-c.NewSuffixes
value: 'L;LL;LU;LLU'
- key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
value: '0'
- key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors
value: '1'
- key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: '1'
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: misc-definitions-in-headers.HeaderFileExtensions
value: ',h,hh,hpp,hxx'
- key: misc-definitions-in-headers.UseHeaderFileExtension
value: '1'
- key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries
value: '1'
- key: misc-unused-parameters.StrictMode
value: '0'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: performance-faster-string-find.StringLikeClasses
value: 'std::basic_string'
- key: performance-for-range-copy.AllowedTypes
value: ''
- key: performance-for-range-copy.WarnOnAllAutoCopies
value: '0'
- key: performance-inefficient-string-concatenation.StrictMode
value: '0'
- key: performance-inefficient-vector-operation.EnableProto
value: '0'
- key: performance-inefficient-vector-operation.VectorLikeClasses
value: '::std::vector'
- key: performance-move-const-arg.CheckTriviallyCopyableMove
value: '1'
- key: performance-move-constructor-init.IncludeStyle
value: llvm
- key: performance-no-automatic-move.AllowedTypes
value: ''
- key: performance-type-promotion-in-math-fn.IncludeStyle
value: llvm
- key: performance-unnecessary-copy-initialization.AllowedTypes
value: ''
- key: performance-unnecessary-value-param.AllowedTypes
value: ''
- key: performance-unnecessary-value-param.IncludeStyle
value: llvm
- key: readability-function-size.BranchThreshold
value: '4294967295'
- key: readability-function-size.LineThreshold
value: '4294967295'
- key: readability-function-size.NestingThreshold
value: '4294967295'
- key: readability-function-size.ParameterThreshold
value: '4294967295'
- key: readability-function-size.StatementThreshold
value: '800'
- key: readability-function-size.VariableThreshold
value: '4294967295'
- key: readability-identifier-naming.IgnoreFailedSplit
value: '0'
- key: readability-implicit-bool-conversion.AllowIntegerConditions
value: '0'
- key: readability-implicit-bool-conversion.AllowPointerConditions
value: '0'
- key: readability-inconsistent-declaration-parameter-name.IgnoreMacros
value: '1'
- key: readability-inconsistent-declaration-parameter-name.Strict
value: '0'
- key: readability-redundant-member-init.IgnoreBaseInCopyConstructors
value: '0'
- key: readability-redundant-smartptr-get.IgnoreMacros
value: '1'
- key: readability-redundant-string-init.StringNames
value: '::std::basic_string'
- key: readability-simplify-boolean-expr.ChainedConditionalAssignment
value: '0'
- key: readability-simplify-boolean-expr.ChainedConditionalReturn
value: '0'
- key: readability-simplify-subscript-expr.Types
value: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array'
- key: readability-static-accessed-through-instance.NameSpecifierNestingThreshold
value: '3'
- key: readability-uppercase-literal-suffix.IgnoreMacros
value: '1'
- key: readability-uppercase-literal-suffix.NewSuffixes
value: ''
...

7
.gitignore vendored
View File

@ -1,6 +1,3 @@
build/ build/
CMakeFiles/ .clangd/
CMakeCache.txt compile_commands.json
cmake_install.cmake
Makefile
.swp

View File

@ -1,10 +1,16 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(microircd LANGUAGES C) project(microircd LANGUAGES C)
set(COMPILE_OPTIONS "-Wall -Wextra -pedantic -Wformat-overflow=2 -Wformat-security -Winit-self -Wstrict-overflow=2 -Wstringop-overflow=2 -Walloc-zero -Wduplicated-branches -Wduplicated-cond -Wtrampolines -Wfloat-equal -Wshadow -Wunsafe-loop-optimizations -Wparentheses -fanalyzer -fstack-protector") if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
add_compile_options(-Wall -Wextra -Wformat-overflow=2 -Wformat-security -Winit-self -Wstrict-overflow=2 -Wstringop-overflow=2 -Walloc-zero -Wduplicated-branches -Wduplicated-cond -Wtrampolines -Wfloat-equal -Wshadow -Wunsafe-loop-optimizations -Wparentheses -pedantic -fanalyzer -fstack-check)
elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
add_compile_options(-Weverything -pedantic -Wno-padded -Wno-disabled-macro-expansion)
endif()
if (NOT CMAKE_BUILD_TYPE) if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug) set(CMAKE_BUILD_TYPE Debug)
endif() endif()
if (CMAKE_BUILD_TYPE EQUAL "Debug") if (CMAKE_BUILD_TYPE EQUAL "Debug")
set(COMPILE_OPTIONS "${COMPILE_OPTIONS} -Og") set(COMPILE_OPTIONS "${COMPILE_OPTIONS} -Og")
elseif (CMAKE_BUILD_TYPE EQUAL "Release") elseif (CMAKE_BUILD_TYPE EQUAL "Release")

19
src/.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
...

View File

@ -14,49 +14,64 @@
* *
* 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/>.
*/ */
#include "filesystem.h" #include "filesystem.h"
int assemble_path(char** path, char* out, int len) int mkdir_bottomup(char* path)
{ {
if (path == NULL || *path == NULL || len <= 0) if (path == NULL || *path == '\0')
return 0; return 0;
char* orig = out; for (char* x = path; x != NULL && *x;) {
int cprint; if ((x = strchr(x, '/')) != NULL) {
for (char** i = path; i != NULL && *i != NULL && out - orig < len; i++) { char save = *(x + 1);
if (i != path && len - (out - orig) > 0) *(x + 1) = '\0';
*(out++) = '/'; if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 && errno != EEXIST) {
if ((cprint = snprintf(out, len - (out - orig), "%s", *i)) > 0) *(x + 1) = save;
out += cprint; LOG(LOG_ERROR, "Could not create directory \"%s\".", path);
else
return 0;
}
return 1;
}
int mkdir_bottomup(char** dir)
{
if (dir == NULL || *dir == NULL)
return -1;
int cprint;
char buf[PATH_MAX], *bufp = buf;
for (char** i = dir; i != NULL && *i != NULL && bufp - buf < sizeof(buf); i++) {
if (i != dir && sizeof(buf) - (bufp - buf) > 0)
*(bufp++) = '/';
if ((cprint = snprintf(bufp, sizeof(buf) - (bufp - buf), "%s", *i)) > 0) {
if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 && errno != EEXIST) {
LOG(LOG_ERROR, "Could not create directory \"%s\".", buf);
return 0; return 0;
} }
bufp += cprint; *(x + 1) = save;
} else { x++;
LOG(LOG_ERROR, "Path limit reached!");
return 0;
} }
} }
LOG(LOG_DEBUG, "Created directory at: %s", path);
return 1; return 1;
} }
int cleanup_path_names(char* name)
int makeinput(char* path)
{
if (path == NULL)
return -1;
int tmp;
if ((tmp = mkfifo(path, S_IRUSR | S_IWUSR | S_IWGRP)) == 0) {
LOG(LOG_VERBOSE, "Created a FIFO pipe for input at %s.", path);
} else if (errno != EEXIST) {
LOG(LOG_WARN, "Couldn't create FIFO at \"%s\" for input. " ERRNOFMT, path, strerror(errno), errno);
return -1;
}
int fd;
if ((fd = open(path, O_RDONLY | O_NONBLOCK)) != -1) {
LOG(LOG_DEBUG, "Opened \"%s\" for reading.", path);
return fd;
} else
LOG(LOG_WARN, "Couldn't open FIFO pipe \"%s\" for reading. " ERRNOFMT, path, strerror(errno), errno);
return -1;
}
bool write_log(char* path, char* message)
{
FILE* logfile;
if ((logfile = fopen(path, "a")) != NULL) {
fprintf(logfile, "%s", message);
fclose(logfile);
return 1;
} else
LOG(LOG_WARN, "Couldn't open file \"%s\" for appending.", path);
return 0;
}
bool cleanup_path_names(char* name)
{ {
if (name == NULL) if (name == NULL)
return 0; return 0;
@ -64,9 +79,103 @@ int cleanup_path_names(char* name)
*name = '_'; *name = '_';
for (; *name; name++) { for (; *name; name++) {
if (isalpha(*name)) if (isalpha(*name))
*name = tolower(*name); *name = (char)tolower(*name);
else if (strchr(".+-_#&", *name) == NULL && !isdigit(*name)) else if (strchr(".+-_#&", *name) == NULL && !isdigit(*name))
*name = '_'; *name = '_';
} }
return 1; return 1;
} }
bool add_socket_flags(int fd, int flags)
{
int cflgs;
if ((cflgs = fcntl(fd, F_GETFL)) != -1) {
if (fcntl(fd, F_SETFL, cflgs | flags) == -1) {
LOG(LOG_WARN, "Couldn't add socket flags. " ERRNOFMT, strerror(errno), errno);
return 0;
}
} else {
LOG(LOG_WARN, "Failed to get socket flags. " ERRNOFMT, strerror(errno), errno);
return 0;
}
return 1;
}
ssize_t set_path_elem(char* content, unsigned int nth_elem, bool isdir, PathBuf* buf)
{
if (buf == NULL || buf->elements[nth_elem].bufpos == NULL)
return -1;
size_t totallen = 0;
for (unsigned long i = 0; i < sizeof(buf->elements) / sizeof(PathBufElem) && buf->elements[i].len > 0; i++)
totallen += buf->elements[i].len;
ssize_t temp;
if ((temp = snprintf(buf->elements[nth_elem].bufpos, sizeof(buf->buf) - totallen, (isdir) ? "%s/" : "%s", content)) == -1)
return -2;
buf->elements[nth_elem].len = (size_t)temp;
if (nth_elem < sizeof(buf->elements) / sizeof(PathBufElem)) {
buf->elements[nth_elem + 1].bufpos = buf->elements[nth_elem].bufpos + temp;
buf->elements[nth_elem + 1].len = 0;
}
return temp;
}
signed int prepare_log_path(IRC_Message* message, PathBuf* pathbuffer)
{
char temp[MAXLINE];
char *type, *bufname;
char* types[] = {"channel", "user"};
switch (message->cmd) {
case JOIN:
case PART:
case MODE:
case TOPIC:
case KICK:
case NAMES:
case RPL_INVITING:
case RPL_BANLIST:
case RPL_ENDOFBANLIST: {
type = types[0];
bufname = message->args[0];
break;
}
case RPL_NOTOPIC:
case RPL_TOPIC: {
bufname = message->args[0];
// NOTE: Some weird servers don't follow the RFC and use args[0] as target nick
if (message->args[2] != NULL)
bufname = message->args[1];
type = types[0];
break;
}
case KILL: {
type = types[1];
bufname = message->args[0];
break;
}
case PRIVMSG:
case NOTICE: {
bufname = message->args[0];
type = types[1];
if (*bufname == '#' || *bufname == '&' || *bufname == '!' || *bufname == '+')
type = types[0];
break;
}
default: return 0;
}
if (set_path_elem(type, 1, true, pathbuffer) <= 0)
return -1;
if (!mkdir_bottomup(pathbuffer->buf))
return -1;
strncpy(temp, bufname, sizeof(temp));
cleanup_path_names(temp);
if (set_path_elem(temp, 2, true, pathbuffer) <= 0)
return -1;
if (!mkdir_bottomup(pathbuffer->buf))
return -1;
if (set_path_elem("out", 3, false, pathbuffer) <= 0)
return -1;
if (!mkdir_bottomup(pathbuffer->buf))
return -1;
return 1;
}

View File

@ -16,23 +16,29 @@
* along with uIRCd. If not, see <https://www.gnu.org/licenses/>. * along with uIRCd. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "uirc/functions.h"
#include "uirc/mappings.h"
#include "global.h" #include "global.h"
#include "log.h" #include "log.h"
#include "structs.h" #include "structs.h"
#define UIRC_IRCV3
#include "uirc/functions.h"
#include "uirc/mappings.h"
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <linux/limits.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.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 mkdir_bottomup(char* path);
int assemble_path(char** path, char* out, int len); int makeinput(char* path);
int mkdir_bottomup(char** dir); bool cleanup_path_names(char* name);
bool write_log(char* path, char* message);
bool add_socket_flags(int fd, int flags);
ssize_t set_path_elem(char* content, unsigned int nth_elem, bool isdir, PathBuf* buf);
int prepare_log_path(IRC_Message* message, PathBuf* pathbuffer);
#endif #endif

View File

@ -16,18 +16,19 @@
* along with uIRCd. If not, see <https://www.gnu.org/licenses/>. * along with uIRCd. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "structs.h"
#include <signal.h> #include <signal.h>
#ifndef _UIRCD_INCLUDED_GLOBAL #ifndef UIRCD_INCLUDED_GLOBAL
#define _UIRCD_INCLUDED_GLOBAL #define UIRCD_INCLUDED_GLOBAL
#define MAXLINE 512 #define MAXLINE 512
#define MAXPATH 1024 #define MAXPATH 1024
#define MAXCONN 64 #define MAXCONN 64
#define MAXNICK 32
#define ERRNOFMT "%s (%i)" #define ERRNOFMT "%s (%i)"
#define ADDRFMT "%s:%s" #define ADDRFMT "%s:%s"
extern sig_atomic_t volatile run; extern sig_atomic_t volatile run;
extern int loglevel; extern int loglevel;
#endif #endif

View File

@ -18,8 +18,8 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#ifndef _UIRCD_INCLUDED_LOG #ifndef UIRCD_INCLUDED_LOG
#define _UIRCD_INCLUDED_LOG #define UIRCD_INCLUDED_LOG
#define LOG_FATAL 0 #define LOG_FATAL 0
#define LOG_ERROR 1 #define LOG_ERROR 1
@ -28,23 +28,23 @@
#define LOG_VERBOSE 4 #define LOG_VERBOSE 4
#define LOG_DEBUG 5 #define LOG_DEBUG 5
#define LOG(LEVEL, ...) \ #define LOG(LEVEL, ...) \
{ \ if (LEVEL <= loglevel) { \
if (LEVEL <= loglevel) { \ char* logchar = "?"; \
char* logchar = "?"; \ switch (LEVEL) { \
switch (LEVEL) { \ case (LOG_DEBUG): logchar = "D"; break; \
case (LOG_DEBUG): logchar = "D"; break; \ case (LOG_VERBOSE): logchar = "V"; break; \
case (LOG_VERBOSE): logchar = "V"; break; \ case (LOG_INFO): logchar = "I"; break; \
case (LOG_INFO): logchar = "I"; break; \ case (LOG_WARN): logchar = "W"; break; \
case (LOG_WARN): logchar = "W"; break; \ case (LOG_ERROR): logchar = "E"; break; \
case (LOG_ERROR): logchar = "E"; break; \ case (LOG_FATAL): logchar = "F"; break; \
case (LOG_FATAL): logchar = "F"; break; \ } \
} \ fprintf(stderr, "[%s] ", logchar); \
fprintf(stderr, "[%s] ", logchar); \ fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, __VA_ARGS__); \ if (loglevel == LOG_DEBUG) \
if (loglevel == LOG_DEBUG) \ fprintf(stderr, " --> LINE: %i -- FILE: %s", __LINE__, __FILE__); \
fprintf(stderr, " --> LINE: %i -- FILE: %s", __LINE__, __FILE__); \ putc('\n', stderr); \
putc('\n', stderr); \
} \
} }
#endif #endif

View File

@ -22,8 +22,9 @@ sig_atomic_t volatile run = true;
int loglevel = LOG_FATAL; int loglevel = LOG_FATAL;
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
char *quitmsg = "uIRC indev beta", c, *pos = NULL; int c;
unsigned int totcon = 0, actcon = 0, delay = 10, timeout = 30; char *quitmsg = "uIRC indev beta", *pos = NULL;
unsigned int totcon = 0, actcon = 0, timeout = 30;
Connection cons[MAXCONN] = {0}; Connection cons[MAXCONN] = {0};
setvbuf(stderr, NULL, _IOLBF, 0); /* Threads may want to print incomplete messages to the log at the same that, avoid that. */ setvbuf(stderr, NULL, _IOLBF, 0); /* Threads may want to print incomplete messages to the log at the same that, avoid that. */
@ -31,7 +32,6 @@ int main(int argc, char* argv[])
* -c Connection in format: nick [ '!' user ] [ '@' host ] [ '/' [ '+' ] port ] [ ',' channel [ ',' channel ... ] ] * -c Connection in format: nick [ '!' user ] [ '@' host ] [ '/' [ '+' ] port ] [ ',' channel [ ',' channel ... ] ]
* -l Starting directory for message tree * -l Starting directory for message tree
* -m Message sent on closing the bouncer gracefully * -m Message sent on closing the bouncer gracefully
* -d Reconnection delay (seconds)
* -t Timeout duration (seconds) * -t Timeout duration (seconds)
* -V Log level: 0 being FATAL and 5 being DEBUG * -V Log level: 0 being FATAL and 5 being DEBUG
* -C Configuration file path * -C Configuration file path
@ -64,8 +64,7 @@ int main(int argc, char* argv[])
break; break;
} }
case 'm': quitmsg = optarg; break; case 'm': quitmsg = optarg; break;
case 'd': delay = atoi(optarg); break; case 't': timeout = (unsigned int)atoi(optarg); break;
case 't': timeout = atoi(optarg); break;
case 'C': break; // TODO: Reserved for config file path case 'C': break; // TODO: Reserved for config file path
case 'V': loglevel = atoi(optarg); break; case 'V': loglevel = atoi(optarg); break;
case 'v': { case 'v': {
@ -92,7 +91,7 @@ int main(int argc, char* argv[])
break; break;
} }
} else if (cpid == 0) { } else if (cpid == 0) {
return run_main(&cons[actcon], delay, quitmsg, timeout); return run_main(&cons[actcon], quitmsg, timeout);
} else { } else {
LOG(LOG_VERBOSE, "Successfully forked for connection " ADDRFMT ".", cons[actcon].data.addr, cons[actcon].data.port); LOG(LOG_VERBOSE, "Successfully forked for connection " ADDRFMT ".", cons[actcon].data.addr, cons[actcon].data.port);
actcon++; actcon++;
@ -106,283 +105,195 @@ int main(int argc, char* argv[])
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
int run_main(Connection* conn, unsigned int recon_inter, char* quitmsg, int timeout) int run_main(Connection* conn, char* quitmsg, unsigned int timeout)
{ {
srand((unsigned int)time(NULL));
IRC_Message buffer; IRC_Message buffer;
char sendbuf[MAXLINE + 1], recvbuf[MAXLINE + 1], fifobuf[MAXLINE + 1], /* Buffers */ Buffer_Info buffers[3]; /* Buffers */
connstr[100], *path[4] = {connstr, NULL, 0}; /* Path buffers */ #define recvbuf buffers[0]
size_t sendbufpos = 0, recvbufpos = 0, fifobufpos = 0; /* Positions */ #define sendbuf buffers[1]
signed int connstate = CONN_PENDING, fds[2] = {-1, -1}; /* Connection state and file descriptors */ #define fifobuf buffers[2]
unsigned int lastping = 0, lastconnect = 0, ctime = 0, lastpong = 0, lastmesg = 0, /* Timestamps */ PathBuf filebuf;
reconinter = (recon_inter) ? recon_inter : 10; /* Intervals */ signed int reconinter = 0;
time_t ctime;
get_connstr(connstr, sizeof(connstr), conn); bool active = true;
srand(time(NULL)); struct timespec sleep = {0, 50000000L};
for (;;) { for (;;) {
struct timespec sleep = {0, 10000000L};
nanosleep(&sleep, NULL);
/* Connection manager */
ctime = time(NULL); ctime = time(NULL);
if (!run || connstate == CONN_CLOSING) { if (!active)
if (fds[0] != -1) { nanosleep(&sleep, NULL);
if ((sendbufpos = Assm_mesg(sendbuf, Assm_cmd_QUIT(quitmsg), sizeof(sendbuf))) > 0) { active = false;
LOG(LOG_VERBOSE, "Sending a QUIT message to " ADDRFMT " containing \"%s\".", conn->data.addr, conn->data.port, quitmsg); if (!run || conn->state == CONN_CLOSING) {
if (flush_buffer(sendbuf, sendbufpos, fds[0]) == -1) signed long temp;
LOG(LOG_WARN, "Couldn't flush send buffer to " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno); if ((temp = Assm_mesg(sendbuf.buffer, Assm_cmd_QUIT(quitmsg), sizeof(sendbuf.buffer))) > 0) {
} sendbuf.append_pos = (unsigned long)temp;
close(fds[0]); LOG(LOG_VERBOSE, "Sending a QUIT message to " ADDRFMT " containing \"%s\".", conn->data.addr, conn->data.port, quitmsg);
if (flush_buffer(sendbuf.buffer, sendbuf.append_pos, sendbuf.fd) == -1)
LOG(LOG_WARN, "Couldn't flush send buffer to " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno);
} }
if (fds[1] == -1) close(sendbuf.fd);
close(fds[1]); close(fifobuf.fd);
connstate = CONN_CLOSED; conn->state = CONN_CLOSED;
LOG(LOG_VERBOSE, "Connection to " ADDRFMT " was closed.", conn->data.addr, conn->data.port); LOG(LOG_VERBOSE, "Connection to " ADDRFMT " was closed.", conn->data.addr, conn->data.port);
break; break;
} else if (connstate == CONN_CLOSED) } else if (conn->state == CONN_CLOSED)
break; break;
else if (connstate == CONN_PENDING) { else if (conn->state == CONN_RECONNECTING) {
if (ctime - lastconnect < recon_inter) close(sendbuf.fd);
close(fifobuf.fd);
conn->state = CONN_PENDING;
continue;
} else if (conn->state == CONN_PENDING) {
if (ctime - conn->lastconnect < reconinter)
continue; continue;
if (fds[0] != -1) conn->lastconnect = ctime;
close(fds[0]); filebuf.elements[0].bufpos = filebuf.buf;
lastconnect = ctime;
int tmp;
if ((tmp = init_conn(conn)) > 0) {
fds[0] = tmp;
memset(recvbuf, '\0', sizeof(recvbuf));
memset(sendbuf, '\0', sizeof(sendbuf));
memset(fifobuf, '\0', sizeof(fifobuf));
lastpong = ctime;
lastping = ctime;
int flags; /* Reset all state-dependent values to empty */
if ((flags = fcntl(fds[0], F_GETFL)) != -1) { memset(&recvbuf, '\0', sizeof(recvbuf));
if (fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) == -1) memset(&sendbuf, '\0', sizeof(sendbuf));
LOG(LOG_WARN, "Couldn't set socket to be non-blocking. Messages may be delayed as a result. " ERRNOFMT, strerror(errno), errno); memset(&fifobuf, '\0', sizeof(fifobuf));
} else conn->lastping = 0;
LOG(LOG_WARN, "Failed to get socket flags. Messages may be delayed as sockets will block. " ERRNOFMT, strerror(errno), errno); conn->lastpong = 0;
conn->lastmessage = 0;
/* Create server.port/global for FIFO and open it */ /* Prepare first part of path */
path[1] = category[CAT_GLOB]; char tempath[MAXPATH];
path[2] = NULL; if ((get_connstr(tempath, sizeof(tempath), conn)) >= 0) {
if (mkdir_bottomup(path)) { cleanup_path_names(tempath);
path[2] = "in"; if (set_path_elem(tempath, 0, true, &filebuf) > 0 && set_path_elem("global", 1, true, &filebuf) > 0) {
path[3] = NULL; if (!mkdir_bottomup(filebuf.buf))
char pbuf[PATH_MAX]; break;
int tmp; if (set_path_elem("in", 2, false, &filebuf)) {
if (assemble_path(path, pbuf, sizeof(pbuf))) { if ((fifobuf.fd = makeinput(filebuf.buf)) == -1)
if ((mkfifo(pbuf, S_IRUSR | S_IWUSR | S_IWGRP)) != 0 && errno != EEXIST) { break;
LOG(LOG_WARN, "Couldn't create FIFO at \"%s\" for input. " ERRNOFMT, pbuf, strerror(errno), errno); }
} else {
LOG(LOG_VERBOSE, "Created a FIFO pipe for input at %s.", pbuf);
ssize_t fd;
if ((fd = open(pbuf, O_RDONLY | O_NONBLOCK)) != -1)
fds[1] = fd;
else
LOG(LOG_WARN, "Couldn't open FIFO pipe \"%s\" for reading. " ERRNOFMT, pbuf, strerror(errno), errno);
}
} else
LOG(LOG_WARN, "Couldn't assemble path for FIFO.");
} }
}
if ((sendbufpos = Assm_mesg(sendbuf, Assm_cmd_NICK(conn->names.nick), sizeof(sendbuf))) > 0) { if ((sendbuf.fd = init_conn(conn)) > 0) {
recvbuf.fd = sendbuf.fd;
add_socket_flags(sendbuf.fd, O_NONBLOCK);
/* Send NICK and USER registration */
// TODO: PASS
signed long temp;
if ((temp = Assm_mesg(sendbuf.buffer, Assm_cmd_NICK(conn->names.nick), sizeof(sendbuf.buffer))) > 0) {
sendbuf.append_pos = (size_t)temp;
LOG(LOG_VERBOSE, "Sending a NICK registration to " ADDRFMT " containing \"%s\".", conn->data.addr, conn->data.port, conn->names.nick); LOG(LOG_VERBOSE, "Sending a NICK registration to " ADDRFMT " containing \"%s\".", conn->data.addr, conn->data.port, conn->names.nick);
if (flush_buffer(sendbuf, sendbufpos, fds[0]) == -1) { if (flush_buffer(sendbuf.buffer, sendbuf.append_pos, sendbuf.fd) == -1) {
LOG(LOG_WARN, "Couldn't register nickname on " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno); LOG(LOG_WARN, "Couldn't register nickname on " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno);
connstate = CONN_PENDING; conn->state = CONN_RECONNECTING;
continue; continue;
} }
} }
if ((sendbufpos = Assm_mesg(sendbuf, Assm_cmd_USER(conn->names.user, conn->names.real, 0), sizeof(sendbuf))) > 0) { if ((temp = Assm_mesg(sendbuf.buffer, Assm_cmd_USER(conn->names.user, conn->names.real, 0), sizeof(sendbuf.buffer))) > 0) {
sendbuf.append_pos = (size_t)temp;
LOG(LOG_VERBOSE, "Sending a USER registration to " ADDRFMT " containing \"%s\".", conn->data.addr, conn->data.port, conn->names.real); LOG(LOG_VERBOSE, "Sending a USER registration to " ADDRFMT " containing \"%s\".", conn->data.addr, conn->data.port, conn->names.real);
if (flush_buffer(sendbuf, sendbufpos, fds[0]) == -1) { if (flush_buffer(sendbuf.buffer, sendbuf.append_pos, sendbuf.fd) == -1) {
LOG(LOG_WARN, "Couldn't register user and real name on " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno); LOG(LOG_WARN, "Couldn't register user and real name on " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno);
connstate = CONN_PENDING; conn->state = CONN_RECONNECTING;
continue; continue;
} }
} }
connstate = CONN_REGISTERED; conn->state = CONN_REGISTERED;
} else if (tmp == INIT_SOFTFAIL) { } else if (sendbuf.fd == INIT_SOFTFAIL) {
connstate = CONN_PENDING; conn->state = CONN_RECONNECTING;
continue; continue;
} else if (tmp == INIT_HARDFAIL) { } else if (sendbuf.fd == INIT_HARDFAIL) {
connstate = CONN_CLOSED; conn->state = CONN_CLOSED;
continue; continue;
} }
} else if (connstate == CONN_ACTIVE) { } else if (conn->state == CONN_ACTIVE) {
if (lastmesg < ctime - timeout && lastpong < lastping - timeout) { if (conn->lastmessage < ctime - timeout && conn->lastpong < conn->lastping - timeout) {
LOG(LOG_WARN, "Server " ADDRFMT " didn't respond to a PING in time.", conn->data.addr, conn->data.port); LOG(LOG_WARN, "Server " ADDRFMT " didn't respond to a PING in time.", conn->data.addr, conn->data.port);
connstate = CONN_PENDING; conn->state = CONN_RECONNECTING;
continue; continue;
} }
if (ctime - lastmesg >= timeout / 4 && ctime - lastping >= timeout / 4) { if (ctime - conn->lastmessage >= timeout / 4 && ctime - conn->lastping >= timeout / 4) {
char mesg[] = "uIRC PING -- XXXXXX"; char mesg[] = "uIRC PING -- XXXXXX";
snprintf(mesg + sizeof(mesg) - 7, 7, "%.6i", rand()); snprintf(mesg + sizeof(mesg) - 7, 7, "%.6i", rand());
if ((sendbufpos = Assm_mesg(sendbuf, Assm_cmd_PING(mesg, NULL), sizeof(sendbuf))) > 0) { signed long temp;
if ((temp = Assm_mesg(sendbuf.buffer, Assm_cmd_PING(mesg, NULL), sizeof(sendbuf.buffer))) > 0) {
sendbuf.append_pos = (size_t)temp;
LOG(LOG_DEBUG, "Sending ping to " ADDRFMT " with message \"%s\"", conn->data.addr, conn->data.port, mesg); LOG(LOG_DEBUG, "Sending ping to " ADDRFMT " with message \"%s\"", conn->data.addr, conn->data.port, mesg);
if (flush_buffer(sendbuf, sendbufpos, fds[0]) == -1) { if (flush_buffer(sendbuf.buffer, sendbuf.append_pos, sendbuf.fd) == -1) {
LOG(LOG_WARN, "Couldn't ping " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno); LOG(LOG_WARN, "Couldn't ping " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno);
connstate = CONN_PENDING; conn->state = CONN_RECONNECTING;
continue; continue;
} }
lastping = ctime; conn->lastping = ctime;
} }
} }
} }
/* Buffer reader */
ssize_t brd; ssize_t brd;
if ((brd = read(fds[0], recvbuf + recvbufpos, sizeof(recvbuf) - recvbufpos - 1)) > 0) {
LOG(LOG_DEBUG, "Read %li bytes.", brd); /* Buffer reader */
*(recvbuf + (recvbufpos += brd)) = '\0'; if ((brd = read(recvbuf.fd, recvbuf.buffer + recvbuf.append_pos, sizeof(recvbuf.buffer) - recvbuf.append_pos - 1)) > 0) {
} else if (brd == 0) { *(recvbuf.buffer + (recvbuf.append_pos += (size_t)brd)) = '\0';
connstate = CONN_PENDING; LOG(LOG_DEBUG, "Read %li bytes from socket buffer. Now appending at %li", brd, recvbuf.append_pos);
active = true;
} else if (brd == -1 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { } else if (brd == -1 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
LOG(LOG_ERROR, "Failed to read inbound traffic. " ERRNOFMT, strerror(errno), errno); LOG(LOG_ERROR, "Failed to read inbound traffic. " ERRNOFMT, strerror(errno), errno);
connstate = CONN_PENDING; conn->state = CONN_RECONNECTING;
continue; continue;
} }
char* ppoi; ssize_t len;
if ((ppoi = strchr(recvbuf, '\n')) != NULL) { memset((void*)&buffer, '\0', sizeof(IRC_Message));
*ppoi = '\0'; if ((len = get_buffer_line(recvbuf.buffer, &buffer)) > 0) {
if (ppoi > recvbuf && *(ppoi - 1) == '\r') conn->lastmessage = ctime;
*(ppoi - 1) = '\0'; if (set_path_elem("global", 1, true, &filebuf) > 0 && set_path_elem("out", 2, false, &filebuf) > 0) {
LOG(LOG_DEBUG, "Recieved line: %s", recvbuf); LOG(LOG_DEBUG, "Writing message to global path %s.", filebuf.buf);
lastmesg = ctime; signed long temp;
memset((void*)&buffer, '\0', sizeof(IRC_Message)); if ((temp = Assm_mesg(sendbuf.buffer, &buffer, sizeof(sendbuf.buffer))) > 0)
if (Tok_mesg(recvbuf, &buffer) == 1) { write_log(filebuf.buf, sendbuf.buffer);
char bufname[MAXPATH + 1], logpath[PATH_MAX], *bprint = "out"; }
FILE* logfile; if (prepare_log_path(&buffer, &filebuf) == 1) {
LOG(LOG_DEBUG, "Tokenized message successfully."); LOG(LOG_DEBUG, "Writing message to path %s.", filebuf.buf);
signed long temp;
/* Path Assembly and Filtering */ if ((temp = Assm_mesg(sendbuf.buffer, &buffer, sizeof(sendbuf.buffer))) > 0)
if (buffer.cmd == PRIVMSG || buffer.cmd == NOTICE) { write_log(filebuf.buf, sendbuf.buffer);
if (strchr("&!#+", *buffer.args[0])) { }
path[1] = category[CAT_CHAN]; if (!auto_msg_actions(&buffer, conn, &sendbuf))
bprint = buffer.args[0]; continue;
} else if (buffer.name.nick != NULL) { for (long unsigned int x = 0; x < sizeof(recvbuf.buffer) && *(recvbuf.buffer + len + x); x++)
path[1] = category[CAT_USER]; *(recvbuf.buffer + x) = *(recvbuf.buffer + x + len);
bprint = buffer.name.nick; recvbuf.append_pos -= (unsigned long)len;
} *(recvbuf.buffer + recvbuf.append_pos) = '\0';
} else if (buffer.cmd == RPL_BANLIST || buffer.cmd == RPL_ENDOFBANLIST) { active = true;
path[1] = category[CAT_CHAN]; } else if (len == -1)
bprint = buffer.args[0]; conn->state = CONN_RECONNECTING;
} else if (buffer.cmd == KILL || buffer.cmd == RPL_AWAY) {
path[1] = category[CAT_USER];
bprint = buffer.name.nick;
} else if (buffer.cmd == JOIN || buffer.cmd == PART) {
path[1] = category[CAT_CHAN];
bprint = buffer.args[0];
} else {
path[1] = category[CAT_GLOB];
}
path[2] = NULL;
if (mkdir_bottomup(path)) {
snprintf(bufname, sizeof(bufname), "%s", bprint);
cleanup_path_names(bufname);
path[2] = bufname;
path[3] = NULL;
if (assemble_path(path, logpath, sizeof(logpath))) {
char linebuf[513];
if (Assm_mesg(sendbuf, &buffer, sizeof(sendbuf)) > 0) {
LOG(LOG_DEBUG, "Assembled line: \"%s\" to write to %s.", sendbuf, bufname);
if ((logfile = fopen(logpath, "a")) != NULL) {
fprintf(logfile, "%s", sendbuf);
fclose(logfile);
} else
LOG(LOG_WARN, "Couldn't open file \"%s\" for appending.", logpath);
}
}
} else
LOG(LOG_WARN, "Log directory couldn't be created. " ERRNOFMT, strerror(errno), errno);
/* Automatic message handling */
switch (buffer.cmd) {
case (PING): {
LOG(LOG_DEBUG, "Auto-replying to ping \"%s\".", buffer.args[0]);
if ((sendbufpos = Assm_mesg(sendbuf, Assm_cmd_PONG(buffer.args[0], NULL), sizeof(sendbuf))) > 0)
if (flush_buffer(sendbuf, sendbufpos, fds[0]) == -1) {
LOG(LOG_WARN, "Couldn't pong " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno);
connstate = CONN_PENDING;
continue;
}
break;
}
case (PONG): {
if (buffer.trailing && buffer.args[1] != NULL) {
LOG(LOG_DEBUG, "Got PONG back with message \"%s\".", buffer.args[1]);
lastpong = ctime;
}
break;
}
/* Autojoin channels from current connection on first response from the server
* TODO? Split channels and join X channels each
* This most likely is not required unless the user has loooots of channels.
* If you are that type of user, you are free to add that.
*/
case (RPL_WELCOME): {
LOG(LOG_INFO, "Connection established to " ADDRFMT ".", conn->data.addr, conn->data.port);
connstate = CONN_ACTIVE;
if (conn->data.chans != NULL) {
LOG(LOG_VERBOSE, "Auto-joining channels \"%s\" on " ADDRFMT ".", conn->data.chans, conn->data.addr, conn->data.port);
if ((sendbufpos = Assm_mesg(sendbuf, Assm_cmd_JOIN(conn->data.chans, NULL), sizeof(sendbuf))) > 0) {
if (flush_buffer(sendbuf, sendbufpos, fds[0]) == -1) {
LOG(LOG_WARN, "Couldn't auto-join channels \"%s\" " ADDRFMT ". " ERRNOFMT, conn->data.chans, conn->data.addr, conn->data.port, strerror(errno), errno);
connstate = CONN_PENDING;
continue;
}
}
}
break;
}
case (ERROR): {
LOG(LOG_ERROR, "Received error on connection " ADDRFMT " with the message \"%s\".", conn->data.addr, conn->data.port, buffer.args[0]);
break;
}
}
} else
LOG(LOG_WARN, "Received invalid IRC message (see RFC2812).");
for (char* x = (ppoi + 1); *x; x++)
*(recvbuf + (x - (ppoi + 1))) = *x;
recvbufpos -= (ppoi + 1) - recvbuf;
*(recvbuf + recvbufpos) = '\0';
}
/* Buffer writer */ /* Buffer writer */
if ((brd = read(fds[1], fifobuf + fifobufpos, sizeof(fifobuf) - fifobufpos - 1)) > 0) { if ((brd = read(fifobuf.fd, fifobuf.buffer + fifobuf.append_pos, sizeof(fifobuf.buffer) - fifobuf.append_pos - 1)) > 0) {
LOG(LOG_DEBUG, "Read %li bytes from FIFO.", brd); *(fifobuf.buffer + (fifobuf.append_pos += (size_t)brd)) = '\0';
*(fifobuf + (fifobufpos += brd)) = '\0'; LOG(LOG_DEBUG, "Read %li bytes from FIFO. Now appending at %li", brd, fifobuf.append_pos);
} else if (brd == -1 && errno != EAGAIN && errno != EINTR) { active = true;
LOG(LOG_ERROR, "Failed to read FIFO input for connection " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno); } else if (brd == -1 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
connstate = CONN_PENDING; LOG(LOG_ERROR, "Failed to read FIFO input. " ERRNOFMT, strerror(errno), errno);
conn->state = CONN_RECONNECTING;
continue; continue;
} }
if ((ppoi = strchr(fifobuf, '\n')) != NULL) { memset((void*)&buffer, '\0', sizeof(IRC_Message));
*ppoi = '\0'; if ((len = get_buffer_line(fifobuf.buffer, &buffer)) > 0) {
if (ppoi > fifobuf && *(ppoi - 1) == '\r') LOG(LOG_DEBUG, "Tokenized FIFO message successfully.");
*(ppoi - 1) = '\0'; signed long temp;
LOG(LOG_DEBUG, "Recieved FIFO line: %s", fifobuf); if ((temp = Assm_mesg(fifobuf.buffer, &buffer, sizeof(fifobuf.buffer))) > 0) {
memset((void*)&buffer, '\0', sizeof(IRC_Message)); if (flush_buffer(fifobuf.buffer, (size_t)temp, sendbuf.fd) == -1) {
if (Tok_mesg(fifobuf, &buffer) == 1) { LOG(LOG_WARN, "Couldn't send FIFO input to " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno);
LOG(LOG_DEBUG, "Tokenized FIFO message successfully."); conn->state = CONN_RECONNECTING;
if ((sendbufpos = Assm_mesg(sendbuf, &buffer, sizeof(sendbuf))) > 0) { continue;
if (flush_buffer(sendbuf, sendbufpos, fds[0]) == -1) {
LOG(LOG_WARN, "Couldn't send FIFO input to " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno);
connstate = CONN_PENDING;
continue;
}
} }
} else }
LOG(LOG_WARN, "Received invalid IRC message on FIFO (see RFC2812)."); for (long unsigned int x = 0; x < sizeof(fifobuf.buffer) && *(fifobuf.buffer + len + x); x++)
for (char* x = (ppoi + 1); *x; x++) *(fifobuf.buffer + x) = *(fifobuf.buffer + x + len);
*(fifobuf + (x - (ppoi + 1))) = *x; fifobuf.append_pos -= (unsigned long)len;
fifobufpos -= ppoi + 1 - fifobuf; *(fifobuf.buffer + fifobuf.append_pos) = '\0';
*(fifobuf + fifobufpos) = '\0'; active = true;
} } else if (len == -1)
conn->state = CONN_RECONNECTING;
} }
LOG(LOG_VERBOSE, "Exiting thread with connection " ADDRFMT " gracefully.", conn->data.addr, conn->data.port); LOG(LOG_VERBOSE, "Exiting thread with connection " ADDRFMT " gracefully.", conn->data.addr, conn->data.port);
return EXIT_SUCCESS; return EXIT_SUCCESS;
@ -394,20 +305,22 @@ void print_help(void)
char arg; char arg;
char* desc; char* desc;
char* def; char* def;
} arg_list[] = {{'c', "Define connection in format \"nick!user:real name@host/+port,#channel1,#channel2\""}, } arg_list[] = {{'c', "Define connection in format \"nick!user:real name@host/+port,#channel1,#channel2\"", NULL},
{'l', "Directory for logs", "current dir"}, {'l', "Directory for logs", "current dir"},
{'m', "Quit message", "uIRC indev beta"}, {'m', "Quit message", "uIRC indev beta"},
{'d', "Reconnection delay", "10 seconds"},
{'t', "Timeout duration", "30 seconds"}, {'t', "Timeout duration", "30 seconds"},
{'V', "Log level (0-5)", "0 [LOG_FATAL]"}, {'V', "Log level (0-5)", "0 [LOG_FATAL]"},
/* {'C', "Configuration path", "~/.config/uircd/main.conf"}, */ /* {'C', "Configuration path", "~/.config/uircd/main.conf"}, */
{'v', "Print version information", NULL}, {'v', "Print version information", NULL},
{'h', "Print this help message", NULL}}; {'h', "Print this help message", NULL}};
printf("usage: uirc -c [connection] [options...]\n"); printf("usage: uirc -c [connection] [options...]\n");
for (int i = 0; i < sizeof(arg_list) / sizeof(*arg_list); i++) { for (size_t i = 0; i < sizeof(arg_list) / sizeof(*arg_list); i++) {
printf("\t-%c\t%s", arg_list[i].arg, arg_list[i].desc); printf("\t-%c\t%s", arg_list[i].arg, arg_list[i].desc);
if (arg_list[i].def != NULL) if (arg_list[i].def != NULL)
printf(" (default: %s)", arg_list[i].def); printf(" (default: %s)", arg_list[i].def);
putchar('\n'); putchar('\n');
} }
} }
void stop_loop() { run = 0; }

View File

@ -29,6 +29,12 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#ifndef UIRCD_INCLUDED_MAIN
#define UIRCD_INCLUDED_MAIN
#define VERSION "indev - testing" #define VERSION "indev - testing"
int run_main(Connection* conn, unsigned int recon_inter, char* quitmsg, int timeout);
void stop_loop(void);
int run_main(Connection* conn, char* quitmsg, unsigned int timeout);
void print_help(void); void print_help(void);
#endif

View File

@ -18,15 +18,6 @@
#include "misc.h" #include "misc.h"
char* category[] = {
[CAT_CHAN] = "channel",
[CAT_USER] = "user",
[CAT_GLOB] = "global"};
void stop_loop()
{
run = 0;
}
char* point_after(char* string, char point) char* point_after(char* string, char point)
/* /*
* Used to tokenize right to left based on characters * Used to tokenize right to left based on characters
@ -39,10 +30,79 @@ char* point_after(char* string, char point)
return ret; return ret;
} }
int get_connstr(char* buf, size_t maxlen, Connection* conn) ssize_t get_connstr(char* buf, size_t maxlen, Connection* conn)
{ {
int len = 0; int len = 0;
if ((len = snprintf(buf, maxlen, "%s.%s", conn->data.addr, conn->data.port)) <= 0) if ((len = snprintf(buf, maxlen, "%s.%s", conn->data.addr, conn->data.port)) <= 0)
return 0; return -1;
return len; return len;
} }
ssize_t get_buffer_line(char* buf, IRC_Message* parsed)
{
if (buf == NULL || parsed == NULL)
return -1;
char* ppoi;
if ((ppoi = strchr(buf, '\n')) != NULL) {
*ppoi = '\0';
if (ppoi > buf && *(ppoi - 1) == '\r')
*(ppoi - 1) = '\0';
LOG(LOG_DEBUG, "Got message %s", buf);
if (Tok_mesg(buf, parsed) == 1)
return ++ppoi - buf;
LOG(LOG_WARN, "Received invalid IRC message (see RFC2812).");
return -1;
}
return 0;
}
int auto_msg_actions(IRC_Message* message, Connection* conn, Buffer_Info* buf)
{
signed long len;
time_t ctime = time(NULL);
switch (message->cmd) {
case (PING): {
LOG(LOG_DEBUG, "Auto-replying to ping \"%s\".", message->args[0]);
if ((len = Assm_mesg(buf->buffer, Assm_cmd_PONG(message->args[0], NULL), sizeof(buf->buffer))) > 0)
if (flush_buffer(buf->buffer, (size_t)len, buf->fd) == -1) {
LOG(LOG_WARN, "Couldn't pong " ADDRFMT ". " ERRNOFMT, conn->data.addr, conn->data.port, strerror(errno), errno);
conn->state = CONN_RECONNECTING;
return 0;
}
break;
}
case (PONG): {
if (message->trailing && message->args[1] != NULL) {
LOG(LOG_DEBUG, "Got PONG back with message \"%s\".", message->args[1]);
conn->lastpong = ctime;
}
break;
}
/* Autojoin channels from current connection on first response from the server
* TODO: Split channels and join X channels each
* This most likely is not required unless the user has loooots of channels.
* If you are that type of user, you are free to add that case
*/
case (RPL_WELCOME): {
LOG(LOG_INFO, "Connection established to " ADDRFMT ".", conn->data.addr, conn->data.port);
conn->state = CONN_ACTIVE;
if (conn->data.chans != NULL) {
LOG(LOG_VERBOSE, "Auto-joining channels \"%s\" on " ADDRFMT ".", conn->data.chans, conn->data.addr, conn->data.port);
if ((len = Assm_mesg(buf->buffer, Assm_cmd_JOIN(conn->data.chans, NULL), sizeof(buf->buffer))) > 0) {
if (flush_buffer(buf->buffer, (size_t)len, buf->fd) == -1) {
LOG(LOG_WARN, "Couldn't auto-join channels \"%s\" " ADDRFMT ". " ERRNOFMT, conn->data.chans, conn->data.addr, conn->data.port, strerror(errno), errno);
conn->state = CONN_RECONNECTING;
return 0;
}
}
}
break;
}
case (ERROR): {
LOG(LOG_ERROR, "Received error on connection " ADDRFMT " with the message \"%s\".", conn->data.addr, conn->data.port, message->args[0]);
break;
}
}
return 1;
}

View File

@ -1,44 +1,22 @@
/*
* 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 "uirc/functions.h"
#include "uirc/mappings.h"
#include "global.h" #include "global.h"
#include "log.h"
#include "net.h"
#include "structs.h" #include "structs.h"
#define UIRC_IRCV3
#include "uirc/functions.h"
#include "uirc/helpers.h"
#include "uirc/mappings.h"
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#define UIRC_HELPERS
#include "uirc/helpers.h"
#ifndef _UIRCD_INCLUDED_MISC #ifndef UIRCD_INCLUDED_MISC
#define _UIRCD_INCLUDED_MISC #define UIRCD_INCLUDED_MISC
extern char* category[];
enum categories {
CAT_CHAN = 1,
CAT_USER,
CAT_GLOB
};
char* point_after(char* string, char point); char* point_after(char* string, char point);
void stop_loop(); ssize_t get_connstr(char* buf, size_t maxlen, Connection* conn);
int get_connstr(char* buf, size_t maxlen, Connection* conn); ssize_t get_buffer_line(char* buf, IRC_Message* parsed);
int get_category(IRC_Message* mesg); int auto_msg_actions(IRC_Message* message, Connection* conn, Buffer_Info* buf);
#endif #endif

View File

@ -19,7 +19,6 @@
signed int init_conn(Connection* info) signed int init_conn(Connection* info)
{ {
const struct timespec retry_timeout = {3, 0.0L};
int sockfd, getaddrres, connectres; int sockfd, getaddrres, connectres;
if (info->data.addr == NULL) return INIT_HARDFAIL; if (info->data.addr == NULL) return INIT_HARDFAIL;
struct addrinfo* conn; struct addrinfo* conn;
@ -28,7 +27,7 @@ signed int init_conn(Connection* info)
freeaddrinfo(conn); freeaddrinfo(conn);
if (getaddrres != EAI_AGAIN && getaddrres != EAI_NONAME) { if (getaddrres != EAI_AGAIN && getaddrres != EAI_NONAME) {
return INIT_HARDFAIL; return INIT_HARDFAIL;
} else } else
return INIT_SOFTFAIL; return INIT_SOFTFAIL;
} }
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) {
@ -42,26 +41,25 @@ signed int init_conn(Connection* info)
freeaddrinfo(conn); freeaddrinfo(conn);
if (errno != EADDRNOTAVAIL && errno != ETIMEDOUT && errno != ECONNRESET && errno != ECONNREFUSED) { if (errno != EADDRNOTAVAIL && errno != ETIMEDOUT && errno != ECONNRESET && errno != ECONNREFUSED) {
return INIT_HARDFAIL; return INIT_HARDFAIL;
} else } else
return INIT_SOFTFAIL; return INIT_SOFTFAIL;
} }
freeaddrinfo(conn); freeaddrinfo(conn);
return sockfd; return sockfd;
} }
int flush_buffer(char* buf, int buflen, int fd) ssize_t flush_buffer(char* buf, size_t buflen, int fd)
{ {
int res; ssize_t res;
char* pos = buf; char* pos = buf;
for (;;) { for (;;) {
if ((res = write(fd, pos, buflen - (pos - buf))) != buflen - (pos - buf)) { if ((size_t)(res = write(fd, pos, buflen - (size_t)(pos - buf))) != buflen - (size_t)(pos - buf)) {
if (res == -1) { if (res == -1)
return -1; return -1;
} else { else
pos += res; pos += res;
}
} else } else
return res; return res;
} }
return pos - buf;
} }

View File

@ -17,6 +17,7 @@
*/ */
#define UIRC_HELPERS #define UIRC_HELPERS
#define UIRC_IRCV3
#include "uirc/functions.h" #include "uirc/functions.h"
#include "uirc/helpers.h" #include "uirc/helpers.h"
#include "uirc/mappings.h" #include "uirc/mappings.h"
@ -36,18 +37,20 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#ifndef _UIRCD_INCLUDED_NETWORK #ifndef UIRCD_INCLUDED_NETWORK
#define _UIRCD_INCLUDED_NETWORK #define UIRCD_INCLUDED_NETWORK
#define CONN_ACTIVE 2 #define CONN_ACTIVE 2
#define CONN_REGISTERED 1 #define CONN_REGISTERED 1
#define CONN_PENDING 0 #define CONN_PENDING 0
#define CONN_CLOSING -1 #define CONN_RECONNECTING -1
#define CONN_CLOSED -2 #define CONN_CLOSING -2
#define CONN_CLOSED -3
#define INIT_HARDFAIL -1 #define INIT_HARDFAIL -1
#define INIT_SOFTFAIL -2 #define INIT_SOFTFAIL -2
signed int init_conn(Connection* info); signed int init_conn(Connection* info);
int flush_buffer(char* buf, int buflen, int fd); ssize_t flush_buffer(char* buf, size_t buflen, int fd);
#endif #endif

View File

@ -16,26 +16,41 @@
* along with uIRCd. If not, see <https://www.gnu.org/licenses/>. * along with uIRCd. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include "global.h"
#define UIRC_IRCV3
#include "uirc/types.h" #include "uirc/types.h"
#include <time.h>
#include <unistd.h> #include <unistd.h>
#ifndef _UIRCD_INCLUDED_STRUCTS #ifndef UIRCD_INCLUDED_STRUCTS
#define _UIRCD_INCLUDED_STRUCTS #define UIRCD_INCLUDED_STRUCTS
typedef struct { typedef struct {
char* addr; char *addr, *port, *chans;
char* port;
char* chans;
bool ssl; bool ssl;
} Connection_Data; } Connection_Data;
typedef struct { typedef struct {
char buffer[513]; char buffer[513];
char* append_pos; size_t append_pos;
char* nex_line; int fd;
int* fd;
} Buffer_Info; } Buffer_Info;
typedef struct { typedef struct {
IRC_User names; IRC_User names;
Connection_Data data; Connection_Data data;
time_t lastping, lastpong, lastconnect, lastmessage;
signed short state;
pid_t pid; pid_t pid;
} Connection; } Connection;
typedef struct {
char* bufpos;
size_t len;
} PathBufElem;
typedef struct {
char buf[MAXPATH];
PathBufElem elements[5];
} PathBuf;
#endif #endif