summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-06-11 19:30:19 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-06-11 19:30:19 +0100
commit1979b37e4fa3c9b7ed8784cf8b52cfae74edd9a4 (patch)
tree1c71fc8430b4bb49503aaf91b1d738717c19a47e
parentc80ce9eceb6412225bfa3a62bc557b17147119f7 (diff)
Send a PING to the server before assuming a timeout is definite.
-rw-r--r--TODO2
-rw-r--r--blabouncer.c83
-rw-r--r--message.c2
-rw-r--r--structures.h1
4 files changed, 58 insertions, 30 deletions
diff --git a/TODO b/TODO
index ead2645..618b2b9 100644
--- a/TODO
+++ b/TODO
@@ -7,4 +7,4 @@ Add various auto replay options:
Might need to #include <limits.h> in blabouncer.c to make some operating systems and/or compilers happy.
-Send a PING to the server before assuming a timeout is definite.
+Handle connecting to the server failing.
diff --git a/blabouncer.c b/blabouncer.c
index d14223e..4986551 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -70,6 +70,7 @@
#define MAXRCVSIZE 2417
#define MAXTOKENS 100 // maximum number of (CRLF or space) separated tokens per server response we expect (TODO - check this is reasonable) (CRLF and spaces just grouped here due to laziness)
#define SERVERTIMEOUT 300 // How many seconds to wait without hearing from the server before assuming a timeout
+#define SELECTTIMEOUT 60 // How many seconds to wait before our pselect() times out, useful for detecting server timeouts
// Global debug control
int debug = 0; // Debug verbosity ("0" for critical only, "1" for some extra info, "2" for full debug mode)
@@ -126,6 +127,7 @@ int connecttoircserver(SSL_CTX **serverctx, SSL **server_ssl, int *serversockfd,
ircdstrings->capmultiprefix = 0;
ircdstrings->autonicknum = 0;
ircdstrings->lastmessagetime = time(NULL);
+ ircdstrings->timeoutcheck = 0;
// ircdstrings.reconnecting is not set here as we want to track reconnections separately
// Populate nick and username from our configuration file for now, real IRCd may change them later (TODO - Is this true of username?)
@@ -449,37 +451,12 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
}
}
- // Before we wait for a message, let's make sure the server is still responding
- // TODO - Code duplication, make a function and share with socket error code below
- if (ircdstrings.lastmessagetime < time(NULL) - SERVERTIMEOUT) {
- debugprint(DEBUG_CRIT, "Server has timed out (%ld seconds), reconnecting!\n", time(NULL) - ircdstrings.lastmessagetime);
- // Tell all clients if we timed out
- char alertmsg[MAXDATASIZE];
- snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :Server has timed out (%ld seconds), reconnecting!", ircdstrings.ircnick, time(NULL) - ircdstrings.lastmessagetime);
- sendtoallclients(clients, alertmsg, 0, settings);
- if (settings->servertls) {
- // Finish up with OpenSSL if using server TLS
- SSL_free(server_ssl);
- }
- // Close the socket
- close(*serversockfd);
- // Make a new one
- *serversockfd = createserversocket(settings->ircserver, settings->ircserverport);
-
- // Set reconnection marker for other functions to know we're reconnecting
- ircdstrings.reconnecting = 1;
- // Set oldnick in case we change nick when reconnecting so we can inform existing clients
- strcpy(ircdstrings.oldnick, ircdstrings.ircnick);
- connecttoircserver(&serverctx, &server_ssl, serversockfd, &ircdstrings, settings, clients);
-
- // Back to top of loop
- continue;
- }
-
debugprint(DEBUG_FULL, "pselect()ing...\n");
// Check to see if any fd in the fd_set is waiting or a signal happened - blocks here until one one of those things happens
- // (pselect() to do signal handling in additiona to fd monitoring)
- if (pselect(fdmax + 1, &rfds, NULL, NULL, NULL, &oldset) < 0) { // network socket + 1, rfds, no writes, no exceptions/errors, no timeout, original sigmask
+ // (pselect() to do signal handling in addition to fd monitoring)
+ struct timespec timeout = {SELECTTIMEOUT, 0}; // pselect() should timeout after SELECTTIMEOUT seconds
+ int selret = pselect(fdmax + 1, &rfds, NULL, NULL, &timeout, &oldset); // network socket + 1, rfds, no writes, no exceptions/errors, no timeout, original sigmask
+ if (selret < 0) { // Error or signal interrupt
if (errno == EINTR) {
// Signal caught, do signal handling
debugprint(DEBUG_CRIT, "signal '%d' happened, exiting!\n", signum);
@@ -498,6 +475,54 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
}
}
+ // pselect() timed out, let's see if a server timeout might be happening, if not, just continue to the top of the loop again
+ if (selret == 0) {
+ debugprint(DEBUG_CRIT, "pselect() timed out.\n", errno);
+
+ // SERVERTIMEOUT seconds have expired...
+ if (ircdstrings.lastmessagetime < time(NULL) - SERVERTIMEOUT && !FD_ISSET(*serversockfd, &rfds)) {
+ if (ircdstrings.timeoutcheck == 0) {
+ // ...and we haven't tried a PING yet, so let's PING the server to see if things are still working
+ debugprint(DEBUG_CRIT, "Server might have timed out after %ld seconds, PINGing it...\n", time(NULL) - ircdstrings.lastmessagetime);
+ ircdstrings.timeoutcheck = 1;
+ char outgoingmsg[MAXDATASIZE];
+ if (!snprintf(outgoingmsg, MAXDATASIZE, "PING %s", ircdstrings.ircdname)) {
+ fprintf(stderr, "Error while preparing timeout testing PING message!\n");
+ debugprint(DEBUG_CRIT, "Error while preparing timeout testing PING message\n");
+ snprintf(outgoingmsg, MAXDATASIZE, "PING timeouttest");
+ }
+ sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, clients, settings);
+ } else {
+ // ...and we've already PINGed the server and haven't heard back yet, so let's assume we've timed out
+ // TODO - Code duplication, make a function and share with socket error code below
+ debugprint(DEBUG_CRIT, "Server has timed out (%ld seconds), reconnecting!\n", time(NULL) - ircdstrings.lastmessagetime);
+ // Tell all clients if we timed out
+ char alertmsg[MAXDATASIZE];
+ snprintf(alertmsg, MAXDATASIZE, "NOTICE %s :Server has timed out (%ld seconds), reconnecting!", ircdstrings.ircnick, time(NULL) - ircdstrings.lastmessagetime);
+ sendtoallclients(clients, alertmsg, 0, settings);
+ if (settings->servertls) {
+ // Finish up with OpenSSL if using server TLS
+ SSL_free(server_ssl);
+ }
+ // Close the socket
+ close(*serversockfd);
+ // Make a new one
+ *serversockfd = createserversocket(settings->ircserver, settings->ircserverport);
+
+ // Set reconnection marker for other functions to know we're reconnecting
+ ircdstrings.reconnecting = 1;
+ // Set oldnick in case we change nick when reconnecting so we can inform existing clients
+ strcpy(ircdstrings.oldnick, ircdstrings.ircnick);
+ connecttoircserver(&serverctx, &server_ssl, serversockfd, &ircdstrings, settings, clients);
+ }
+ // Back to top of loop
+ continue;
+ } else {
+ // No timeout is occuring, back to the top of the loop
+ continue;
+ }
+ }
+
// TODO - switch around the serversockfd and STDIN FD_ISSET if-statements? They feel the wrong way round. Are they like this on purpose? I can't remember.
// (although actually stdin may go once its not wanted for possible direct interaction for debugging)
diff --git a/message.c b/message.c
index f837b83..b021347 100644
--- a/message.c
+++ b/message.c
@@ -23,6 +23,8 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int
struct channel *channels, struct settings *settings, char tokens[MAXTOKENS][MAXDATASIZE], int counter) {
// Record that we received something from the server for timeout checking purposes
ircdstrings->lastmessagetime = time(NULL); // snprintf(NULL, 0, "%ld", timenow);
+ // And we can't be timing out
+ ircdstrings->timeoutcheck = 0;
// Server PING received? If so, send a PONG back with the next element as the argument.
if (strncmp(tokens[0], "PING", strlen(tokens[0])) == 0) {
diff --git a/structures.h b/structures.h
index 1de8fc8..5b9c2c0 100644
--- a/structures.h
+++ b/structures.h
@@ -46,6 +46,7 @@ struct ircdstrings {
int capmultiprefix; // Whether the server approved our CAP multi-prefix request
int autonicknum; // Number of attempts made at automatically setting a nick if all configured nicks were in use
int lastmessagetime; // The last time we heard from the server
+ int timeoutcheck; // Whether we're checking to see if we've timed out from the server
int reconnecting; // Whether or not we're reconnecting due to an earlier disconnection
char oldnick[MAXNICKLENGTH]; // Set temporarily if we end up reconnecting in case we need to tell existing clients about a nick change
};