From 3c60bbdb928da1ebcec9153fb199ad740ad41856 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Mon, 27 May 2019 17:14:42 +0100 Subject: Allow the BLABOUNCER REPLAY command time to be specified with days:hours:minutes:seconds. --- README | 2 +- TODO | 4 +-- blabouncer.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/README b/README index ccb7596..7528ecf 100644 --- a/README +++ b/README @@ -18,7 +18,7 @@ If you don't specify one using "-c /path/to/configuration/file" then the example Once connected to blabouncer with a client, you can use the following commands: -"BLABOUNCER REPLAY [seconds]" (Where [seconds] is the number of seconds of replay log to replay.) +"BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]" (To replay a given length of time of replay log.) Blabouncer commands are all prefixed with BLABOUNCER which you can usually send using "/QUOTE BLABOUNCER". diff --git a/TODO b/TODO index 0eee5d6..c9b87a3 100644 --- a/TODO +++ b/TODO @@ -7,8 +7,8 @@ Add various auto replay options: - All logs since X seconds ago (already implemented) - All logs since the current client last disconnected (track clients with some special token the client auto sends on connect) -Allow log replay time to be specified with days:hours:minutes:seconds. - Might need to #include in blabouncer.c to make some operating systems and/or compilers happy. Reconnect server if we get disconnected for some reason. + +Make sure MAXTOKENS being exceeded can't cause a buffer overflow. diff --git a/blabouncer.c b/blabouncer.c index e837977..f8671f5 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -1479,7 +1479,7 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Valid blabouncer commands are:", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); - snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [seconds]\" (Where [seconds] is the number of seconds of replay log to replay.)", ircdstrings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]\" (To replay a given length of time of replay log.)", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); // Get the channel count so we can enumerate over all channels. @@ -1747,19 +1747,88 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli if (strncasecmp(tokens[0], "BLABOUNCER", strlen(tokens[0])) == 0) { char outgoingmsg[MAXDATASIZE]; printf("Client BLABOUNCER found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1])); - // REPLAY received, send the requested number of seconds of replay to the client + // REPLAY received, send the requested length of replay time to the client if (strncasecmp(tokens[1], "REPLAY", strlen("REPLAY")) == 0) { printf("Client BLABOUNCER REPLAY (custom blabouncer command) found and it is: %s with length %zd!\n", tokens[1], strlen(tokens[1])); - int replayseconds; - if ((replayseconds = strtol(tokens[2], NULL, 10)) == 0) { - printf("Invalid number of replay seconds provided by REPLAY. Telling client.\n"); - snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Invalid number of seconds of replay requested by REPLAY command.", ircdstrings->ircnick); + + // Split the request into days:hours:minutes:seconds + + // Track which colon-separated token within this request we're on + int timecounter = 0; + + // Build array of colon-separated tokens + char timetokens[MAXTOKENS][MAXDATASIZE]; + // Copy to a temporary string so we still have the original in case it's not processed + char *timestrcopy = strdup(tokens[2]); + // Keep track of initial pointer for free()ing later + char *timestrcopyPtr = timestrcopy; + + char *timetoken; + while ((timetoken = strsep(×trcopy, ":")) != NULL) { + if (*timetoken == '\0') continue; // Skip consecutive matches + if (timecounter >= MAXTOKENS) break; // Too many tokens + printf("Time token: \"%s\", length %zd.\n", timetoken, strlen(timetoken)); + // Copy into the token array (strlen + 1 to get the NULL terminator) + strncpy(timetokens[timecounter], timetoken, strlen(timetoken) + 1); + timecounter++; + } + + // Make sure we don't have more than four (d:h:m:s) components + if (timecounter > 4) { + printf("Too many time components requested by REPLAY command. Telling client.\n"); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Too many time components requestd by REPLAY command. Expected up to four (days:hours:minutes:seconds).", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); + free(timestrcopyPtr); free(strcopyPtr); return 1; } + // Make sure all the components are numbers + for (int i = 0; i < timecounter; i++) { + int check; + if ((check = strtol(timetokens[i], NULL, 10)) == 0) { + printf("Invalid number '%s' requested by REPLAY command. Telling client.\n", timetokens[i]); + if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Invalid number '%s' requested by REPLAY command.", ircdstrings->ircnick, timetokens[i])) { + fprintf(stderr, "Error while preparing REPLAY invalid number response!\n"); + exit(1); + } + sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); + free(timestrcopyPtr); + free(strcopyPtr); + return 1; + } + } + + // How many seconds we're going to replay + int replayseconds = 0; + + // If d:h:m:s provided + if (timecounter == 4) { + replayseconds += 86400 * strtol(timetokens[0], NULL, 10); + replayseconds += 3600 * strtol(timetokens[1], NULL, 10); + replayseconds += 60 * strtol(timetokens[2], NULL, 10); + replayseconds += strtol(timetokens[3], NULL, 10); + } + // If h:m:s provided + if (timecounter == 3) { + replayseconds += 3600 * strtol(timetokens[0], NULL, 10); + replayseconds += 60 * strtol(timetokens[1], NULL, 10); + replayseconds += strtol(timetokens[2], NULL, 10); + } + // If m:s provided + if (timecounter == 2) { + replayseconds += 60 * strtol(timetokens[0], NULL, 10); + replayseconds += strtol(timetokens[1], NULL, 10); + } + // If s provided + if (timecounter == 1) { + replayseconds += strtol(timetokens[0], NULL, 10); + } + + printf("Replaying '%s' which is '%d' seconds.\n", tokens[2], replayseconds); + doreplay(sourcefd, replayseconds, clients, settings, ircdstrings, channels); + free(timestrcopyPtr); free(strcopyPtr); return 1; // Unrecognised BLABOUNCER command received, send some help instructions @@ -1767,7 +1836,7 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli printf("Client BLABOUNCER unrecognised command found and it is: %s with length %zd! Sending a help message.\n", tokens[1], strlen(tokens[1])); snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unrecognised BLABOUNCER command received. Valid commands are:", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); - snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [seconds]\" (Where [seconds] is the number of seconds of replay log to replay.)", ircdstrings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[[days:]hours:]minutes:]seconds]\" (To replay a given length of time of replay log.)", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); free(strcopyPtr); return 1; -- cgit v1.2.3