summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README1
-rw-r--r--TODO2
-rw-r--r--blabouncer.c22
-rw-r--r--blabouncer.conf.example4
-rw-r--r--functions.c116
-rw-r--r--functions.h13
-rw-r--r--message.c54
-rw-r--r--structures.h1
9 files changed, 212 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index baa334c..7b02278 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ ifeq ($(PREFIX),)
endif
blabouncer: blabouncer.c functions.c sockets.c config.c replay.c logging.c message.c config.h functions.h logging.h message.h replay.h sockets.h structures.h
- $(CC) -D_DEFAULT_SOURCE -D_BSD_SOURCE -DVERSION=\"$(GIT_VERSION)\" -std=gnu99 -Wall -Wextra -lssl -lcrypto -o $(BINARY) blabouncer.c functions.c sockets.c config.c replay.c logging.c message.c
+ $(CC) -D_DEFAULT_SOURCE -D_BSD_SOURCE -DVERSION=\"$(GIT_VERSION)\" -std=gnu99 -Wall -Wextra -lssl -lcrypto -lresolv -o $(BINARY) blabouncer.c functions.c sockets.c config.c replay.c logging.c message.c
.PHONY: clean
clean:
diff --git a/README b/README
index e3e5c7b..d03330d 100644
--- a/README
+++ b/README
@@ -56,6 +56,7 @@ what this client has missed.)
"BLABOUNCER DISCONNECT [FD]" (To disconnect a client with file descriptor number [FD] (see LISTCLIENTS
output).)
"BLABOUNCER VERSION" (To show the current blabouncer version.)
+"BLABOUNCER UPDATECHECK" (To check for blabouncer updates. Set checkupdates = "0" to stop this happening automatically.)
Blabouncer commands are all prefixed with BLABOUNCER which you can usually send using "/QUOTE BLABOUNCER".
diff --git a/TODO b/TODO
index 895e3c2..eb7bf2b 100644
--- a/TODO
+++ b/TODO
@@ -52,8 +52,6 @@ QUIT not logged in all channels a person was in? (e.g. Joey Mon 1 Apr 20:49:14
"/whois" with no nick - "No nickname given" goes to all clients - fixable?
-Ability to check for updates (and optional at startup?).
-
Absurd CPU usage and duration doing "/BLABOUNCER REPLAY 24:0" approx. 14/09/2024 17:35.
Custom OpenSSL protocols/ciphers?
diff --git a/blabouncer.c b/blabouncer.c
index b3e239d..443c75c 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -1331,6 +1331,13 @@ int main(int argc, char *argv[]) {
strncat(conffailmsg, "Error getting 'alertautheddisconnect' from configuration file.\n", sizeof conffailmsg - strlen(conffailmsg) - 1);
}
+ // Is check for updates upon startup and successful client authentication enabled?
+ settings.checkupdates = getconfint("checkupdates", settings.conffile);
+ if (errno == ECONFINT) {
+ conffail = 1;
+ strncat(conffailmsg, "Error getting 'checkupdates' from configuration file.\n", sizeof conffailmsg - strlen(conffailmsg) - 1);
+ }
+
// How many debug logs should we keep?
settings.debugkeep = getconfint("debugkeep", settings.conffile);
if (errno == ECONFINT) {
@@ -1439,6 +1446,21 @@ int main(int argc, char *argv[]) {
debugprint(DEBUG_SOME, "Using configuration file '%s'.\n", settings.conffile);
+ // Check for updates (if enabled in configuration file with checkupdates = "1")
+ if (settings.checkupdates) {
+ debugprint(DEBUG_SOME, "Checking for blabouncer updates (checkupdates = \"0\" to disable)...\n");
+ char version[MAXDNSTXTLEN];
+ version[0] = '\0';
+ int ret = checkversion(version);
+ if (ret == 1) {
+ debugprint(DEBUG_SOME, "main(): blabouncer appears to be up to date.\n");
+ } else if (ret == -1) {
+ debugprint(DEBUG_SOME, "main(): blabouncer appears to be out of date, latest version is %s.\n", version);
+ } else {
+ debugprint(DEBUG_CRIT, "main(): Version check failed!\n");
+ }
+ }
+
// Unless specified otherwise on the command line, fork to background
if (settings.background) {
pid_t pid, sid;
diff --git a/blabouncer.conf.example b/blabouncer.conf.example
index 2281806..4e8e7f1 100644
--- a/blabouncer.conf.example
+++ b/blabouncer.conf.example
@@ -123,3 +123,7 @@ alertunautheddisconnect = "1"
# Send NOTICE to all other clients upon authenticated client disconnections ("1" for yes or "0" for no)
alertautheddisconnect = "1"
+
+# Check for updates upon startup and successful client authentication ("1" for yes or "0" for no)
+# This sends a DNS TXT request to blatech.net, disable this if you do not wish for that to happen
+checkupdates = "1"
diff --git a/functions.c b/functions.c
index 54e2663..35be8e4 100644
--- a/functions.c
+++ b/functions.c
@@ -1710,3 +1710,119 @@ void strlower(char *string) {
string[i] = tolower(string[i]);
}
}
+
+// Gets a single TXT record from DNS for "dnsname" and stores the result in "record"
+// Returns 1 on success or 0 on failure
+// TODO: If this is ever used for more than just version checks, then it needs to do more than a single record.
+int gettxtrecordsingle(char *dnsname, char *record) {
+ debugprint(DEBUG_FULL, "gettxtrecordsingle(): given '%s'.\n", dnsname);
+
+ unsigned char buffer[8000] = {0};
+ char result[MAXDNSTXTLEN] = {0};
+ const unsigned char *presult = NULL;
+ struct __res_state resState = {0};
+ ns_msg nsMsg = {0};
+ ns_rr rr;
+
+ int type = 0;
+ int ret = 0;
+ int size = 0;
+ int count = 0;
+ long unsigned int len = 0;
+ int res_init = 0;
+
+ ret = res_ninit(&resState);
+
+ if (ret) {
+ debugprint(DEBUG_CRIT, "gettxtrecordsingle(): res_ninit() returned failure (it returned %d)! Returning 0.\n", ret);
+ return 0;
+ } else {
+ res_init = 1;
+ }
+
+ memset(buffer, 0, sizeof (buffer));
+ size = res_nquery(&resState, dnsname, C_IN, T_TXT, buffer, sizeof(buffer) - 1);
+
+ if (size < 1) {
+ res_nclose(&resState);
+ debugprint(DEBUG_CRIT, "gettxtrecordsingle(): res_nquery() returned zero-length or failure (it returend %d)! Returning 0.\n", size);
+ return 0;
+ }
+
+ ret = ns_initparse(buffer, size, &nsMsg);
+
+ if (ret) {
+ res_nclose(&resState);
+ debugprint(DEBUG_CRIT, "gettxtrecordsingle(): ns_initparse() returned failure (failed to parse the buffer buffer?) (it returned %d)! Returning 0.\n", ret);
+ return 0;
+ }
+
+ count = ns_msg_count(nsMsg, ns_s_an);
+
+ for (int i = 0; i < count; i++) {
+ ret = ns_parserr(&nsMsg, ns_s_an, i , &rr);
+
+ if (ret) {
+ res_nclose(&resState);
+ return 0;
+ }
+
+ type = ns_rr_type(rr);
+ if (ns_t_txt == type) {
+ len = ns_rr_rdlen(rr);
+ presult = ns_rr_rdata(rr);
+
+ if ((len > 1) && (len < sizeof(result))) {
+ len--;
+ memcpy (result, presult+1, len);
+ result[len] = '\0';
+ debugprint(DEBUG_FULL, "gettxtrecordsingle(): record '%d' from DNS is '%s'.\n", i, result);
+
+ // Only using the first result
+ if (i == 0) {
+ strncpy(record, result, sizeof(result) - 1);
+ }
+ }
+ }
+ }
+
+ debugprint(DEBUG_FULL, "gettxtrecordsingle(): using record '%s'.\n", result);
+
+ if (res_init) {
+ res_nclose (&resState);
+ }
+
+ return 1;
+}
+
+// Gets the latest blabouncer version string from DNS and stores it in "version"
+// Ignores the Git version portion (everything after the first '-') of VERSION, if present
+// Returns 1 if local VERSION is current, -1 if local VERSION is not current, or 0 on failure
+int checkversion(char *version) {
+ if (!gettxtrecordsingle(VERSIONTXTNAME, version)) {
+ debugprint(DEBUG_CRIT, "checkversion(): gettxtrecordsingle() failure and the buffer contains '%s', returning 0.\n", version);
+ return 0;
+ } else {
+ debugprint(DEBUG_FULL, "checkversion(): gettxtrecordsingle() success and the returned record is '%s'.\n", version);
+ }
+
+ // Ignore the Git version portion (everything after the first '-') of VERSION, if present
+ char localversion[MAXDNSTXTLEN];
+ strncpy(localversion, VERSION, sizeof(localversion) - 1);
+
+ for (size_t i = 0; i < strlen(localversion); i++) {
+ if (localversion[i] == '-') {
+ localversion[i] = '\0';
+ debugprint(DEBUG_FULL, "checkversion(): Trimmed local VERSION string to '%s'.\n", localversion);
+ break;
+ }
+ }
+
+ if (strncmp(localversion, version, sizeof(localversion))) {
+ debugprint(DEBUG_SOME, "checkversion(): Returned version '%s' doesn't match expected '%s', returning -1.\n", version, localversion);
+ return -1;
+ } else {
+ debugprint(DEBUG_FULL, "checkversion(): Returned version '%s' matches expected '%s', returning 1.\n", version, localversion);
+ return 1;
+ }
+}
diff --git a/functions.h b/functions.h
index ff9d656..72a2129 100644
--- a/functions.h
+++ b/functions.h
@@ -28,6 +28,7 @@
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
+#include <resolv.h>
#include <sys/select.h>
#include <stdarg.h>
#include <limits.h>
@@ -52,6 +53,8 @@
#define MAXCLIENTS 32 // Maximum number of clients that can connect to the bouncer at a time
#define MAXCHANNELS 1024 // Let's assume 1024 is reasonable for now (it's configured per IRCd)
#define MAXRFCNICKLEN 9 // From RFC 1459
+#define MAXDNSTXTLEN 2048 // Maximum size to store in a DNS TXT record response
+#define VERSIONTXTNAME "version.blabouncer.blatech.net" // DNS name of blabouncer latest version check TXT record
#define MAXTOKENS 100 // For strsep string splitting
@@ -220,4 +223,14 @@ void stripprefixesfromnick(char *nick);
// Convert the given 'string' into lowercase
void strlower(char *string);
+
+// Gets a single TXT record from DNS for "dnsname" and stores the result in "record"
+// Returns 1 on success or 0 on failure
+// TODO: If this is ever used for more than just version checks, then it needs to do more than a single record.
+int gettxtrecordsingle(char *dnsname, char *record);
+
+// Gets the latest blabouncer version string from DNS and stores it in "version"
+// Ignores the Git version portion (everything after the first '-') of VERSION, if present
+// Returns 1 if local VERSION is current, -1 if local VERSION is not current, or 0 on failure
+int checkversion(char *version);
#endif
diff --git a/message.c b/message.c
index f8e14cb..f580419 100644
--- a/message.c
+++ b/message.c
@@ -1033,6 +1033,31 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
// Send our own greeting message
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Welcome to blabouncer version %s!", ircdstate->ircnick, VERSION);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+
+ // Check for updates (if enabled in configuration file with checkupdates = "1")
+ if (settings->checkupdates) {
+ debugprint(DEBUG_FULL, "Checking for blabouncer updates on client connection.\n");
+ char version[MAXDNSTXTLEN];
+ version[0] = '\0';
+ int ret = checkversion(version);
+ if (ret == 1) {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer appears to be up to date.", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ } else if (ret == -1) {
+
+ // Next prepare the topic who/when message...
+ if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer appears to be out of date, latest version is %s.", ircdstate->ircnick, version)) {
+ fprintf(stderr, "processclientmessage() Error while preparing out of date version warning after client connected (version string too long?)!\n");
+ debugprint(DEBUG_CRIT, "processclientmessage() Error while preparing out of date version warning after client connected (version string too long?)!\n");
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer appears to be out of date, but couldn't print latest version in this NOTICE.", ircdstate->ircnick);
+ }
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ } else {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Version check failed!", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+ }
+
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer commands are all prefixed with BLABOUNCER which you can usually send using \"/QUOTE BLABOUNCER\"", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Valid blabouncer commands are:", ircdstate->ircnick);
@@ -1051,6 +1076,8 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER VERSION\" (To show the current blabouncer version.)", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER UPDATECHECK\" (To check for blabouncer version updates.)", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
// Get the channel count so we can iterate over all channels.
int channelcount = getchannelcount(channels, ircdstate->maxchannelcount);
@@ -1528,6 +1555,31 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :This is blabouncer version %s!", ircdstate->ircnick, VERSION);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
return 1;
+ // UPDATECHECK received, send current blabouncer version
+ } else if (strncasecmp(tokens[1], "UPDATECHECK", strlen("UPDATECHECK")) == 0) {
+ debugprint(DEBUG_SOME, "Client BLABOUNCER UPDATECHECK found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1]));
+
+ char version[MAXDNSTXTLEN];
+ version[0] = '\0';
+ int ret = checkversion(version);
+ if (ret == 1) {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer appears to be up to date.", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ } else if (ret == -1) {
+
+ // Next prepare the topic who/when message...
+ if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer appears to be out of date, latest version is %s.", ircdstate->ircnick, version)) {
+ fprintf(stderr, "processclientmessage() Error while preparing out of date version warning after client connected (version string too long?)!\n");
+ debugprint(DEBUG_CRIT, "processclientmessage() Error while preparing out of date version warning after client connected (version string too long?)!\n");
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer appears to be out of date, but couldn't print latest version in this NOTICE.", ircdstate->ircnick);
+ }
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ } else {
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Version check failed!", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ }
+
+ return 1;
// LISTCLIENTS received, send list of connected clients and their authentication status
} else if (strncasecmp(tokens[1], "LISTCLIENTS", strlen("LISTCLIENTS")) == 0) {
debugprint(DEBUG_SOME, "Client BLABOUNCER LISTCLIENTS found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1]));
@@ -1610,6 +1662,8 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER VERSION\" (To show the current blabouncer version.)", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER UPDATECHECK\" (To check for blabouncer version updates.)", ircdstate->ircnick);
+ sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
return 1;
}
}
diff --git a/structures.h b/structures.h
index c4d20e0..86f83b7 100644
--- a/structures.h
+++ b/structures.h
@@ -90,6 +90,7 @@ struct settings {
int alertauthsuccess;
int alertunautheddisconnect;
int alertautheddisconnect;
+ int checkupdates;
};
// Structure of a connected client, their socket/file descriptors, their authentication status, and their OpenSSL structures