diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | blabouncer.c | 83 | ||||
-rw-r--r-- | message.c | 2 | ||||
-rw-r--r-- | structures.h | 1 |
4 files changed, 58 insertions, 30 deletions
@@ -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) @@ -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 }; |