summaryrefslogtreecommitdiff
path: root/blabouncer.c
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 /blabouncer.c
parentc80ce9eceb6412225bfa3a62bc557b17147119f7 (diff)
Send a PING to the server before assuming a timeout is definite.
Diffstat (limited to 'blabouncer.c')
-rw-r--r--blabouncer.c83
1 files changed, 54 insertions, 29 deletions
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)