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  }; | 
