From c70cd5cccc966a35f175913f2281ce251fd62425 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Mon, 17 Jun 2019 01:46:28 +0100 Subject: Implement a per-client identifier so auto replay can replay everything a given client has missed. --- blabouncer.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'blabouncer.c') diff --git a/blabouncer.c b/blabouncer.c index 95521f1..73bef8c 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -134,6 +134,7 @@ int connecttoircserver(SSL_CTX **serverctx, SSL **server_ssl, int *serversockfd, ircdstate->timeoutcheck = 0; // ircdstate.reconnecting is not set here as we want to track reconnections separately // ircdstate.clientchangetime and ircdstate.clientsnonetime not set here as they are set at startup and only changed when clients connect/disconnect + // ircdstate.clientcodes not set here, set on startup and whenever a client sets one // Populate nick and username from our configuration file for now, real IRCd may change them later (TODO - Is this true of username?) strcpy(ircdstate->ircnick, settings->ircnick); @@ -176,7 +177,8 @@ int connecttoircserver(SSL_CTX **serverctx, SSL **server_ssl, int *serversockfd, // Return 1 if we processed something and expect the caller to not need to do anything more // Return 0 if we didn't process it and the caller might want to do something //int processircmessage(int *serversockfd, int *clientsockfd, char *str, int source) { -int processircmessage(SSL *server_ssl, char *str, int source, struct client *clients, int sourcefd, struct ircdstate *ircdstate, struct channel *channels, struct settings *settings) { +int processircmessage(SSL *server_ssl, char *str, int source, struct client *clients, int sourcefd, struct ircdstate *ircdstate, struct channel *channels, + struct settings *settings, struct clientcodes *clientcodes) { // Track which space-separated token within this response we're on int counter = 0; @@ -215,7 +217,7 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli // Don't return if we got here because this means we didn't process something in processservermessage() break; case SOURCE_CLIENT: // If message(s) were from a real IRC client - if (processclientmessage(server_ssl, str, clients, sourcefd, ircdstate, channels, settings, tokens, counter)) { + if (processclientmessage(server_ssl, str, clients, sourcefd, ircdstate, channels, settings, tokens, counter, clientcodes)) { // We processed something so return true free(strcopyPtr); return 1; @@ -254,7 +256,8 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli // // Return 0 if something went wrong // Return 1 if everything OK -int processrawstring(SSL *server_ssl, char *str, int source, struct client *clients, int sourcefd, struct ircdstate *ircdstate, struct channel *channels, struct settings *settings) { +int processrawstring(SSL *server_ssl, char *str, int source, struct client *clients, int sourcefd, struct ircdstate *ircdstate, struct channel *channels, + struct settings *settings, struct clientcodes *clientcodes) { // Copy to a temporary string so we still have the original in case it's not processed char *strcopy = strdup(str); // Keep track of initial pointer for free()ing later @@ -318,7 +321,7 @@ int processrawstring(SSL *server_ssl, char *str, int source, struct client *clie for (int i = 0; i < messagecount; i++) { // Copy to a temporary string so we still have the original in case it's not processed char *messagecopy = strdup(messages[i]); - if (processircmessage(server_ssl, messagecopy, source, clients, sourcefd, ircdstate, channels, settings)) { + if (processircmessage(server_ssl, messagecopy, source, clients, sourcefd, ircdstate, channels, settings, clientcodes)) { debugprint(DEBUG_FULL, "Message processed: \"%s\", NULLing...\n", messages[i]); messages[i][0] = '\0'; } @@ -392,11 +395,25 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { clients[i].pendingwhowas = 0; clients[i].pendingnames = 0; clients[i].pendingcap = 0; + clients[i].clientcode[0] = '\0'; } // Struct of various strings from and for the real IRCd (such as the greeting strings, the real IRCd's name, // our nick!user@host string, our nick, username, real name, etc.) struct ircdstate ircdstate; + // Set reconnection and other things to null/zero for now (not used unless reconnecting to server) + ircdstate.oldnick[0] = '\0'; + ircdstate.reconnecting = 0; + ircdstate.clientchangetime = time(NULL); + ircdstate.clientsnonetime = time(NULL); + + // Struct of client codes + // Used to track the last time a client identifying as a given client connected to handle auto replay for a known client. + struct clientcodes clientcodes[MAXCLIENTCODES]; + for (int i = 0; i < MAXCLIENTCODES; i++) { + clientcodes[i].code[0] = '\0'; + clientcodes[i].lastdisconnected = 0; + } // Struct of channels we're in struct channel *channels; @@ -413,11 +430,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { SSL_CTX *serverctx = NULL; SSL *server_ssl = NULL; // Need to create this either way as referenced later - // Set reconnection and other things to null/zero for now (not used unless reconnecting to server) - ircdstate.oldnick[0] = '\0'; - ircdstate.reconnecting = 0; - ircdstate.clientchangetime = time(NULL); - ircdstate.clientsnonetime = time(NULL); + // Try to connect to IRC! if (!connecttoircserver(&serverctx, &server_ssl, serversockfd, &ircdstate, settings, clients)) { fprintf(stderr, "Failed to connect to IRC server, exiting.\n"); debugprint(DEBUG_CRIT, "Failed to connect to IRC server, exiting.\n"); @@ -648,7 +661,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { // Try to process received string (which should contain one or more server responses/commands) // TODO - What if there were two server respones/commands and only one didn't need relaying? - if (!processrawstring(server_ssl, serverbuf, SOURCE_SERVER, clients, EXCEPT_NONE, &ircdstate, channels, settings)) { + if (!processrawstring(server_ssl, serverbuf, SOURCE_SERVER, clients, EXCEPT_NONE, &ircdstate, channels, settings, clientcodes)) { fprintf(stderr, "Error: bouncer-server failed to process raw string.\n"); debugprint(DEBUG_CRIT, "Error: bouncer-server failed to process raw string.\n"); } @@ -713,7 +726,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { if (numclients(clients) >= MAXCLIENTS) { fprintf(stderr, "too many clients, disconnecting and skipping loop iteration!\n"); debugprint(DEBUG_CRIT, "too many clients, disconnecting and skipping loop iteration!\n"); - disconnectclient(i, clients, &ircdstate, settings); + disconnectclient(i, clients, &ircdstate, settings, clientcodes); continue; } addrlen = sizeof remoteaddr; @@ -779,7 +792,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { debugprint(DEBUG_CRIT, "dochat(): client sockread() error fd '%d'.\n", i); } // Disconnect the client - disconnectclient(i, clients, &ircdstate, settings); + disconnectclient(i, clients, &ircdstate, settings, clientcodes); FD_CLR(i, &rfds); // remove from master set - TODO is this needed at the moment since we just add everything from *clientsockfd to fdmax to rfds // TODO - Handle the "remove the client" loop not finding the old fd debugprint(DEBUG_FULL, "bouncer-client: total client connections: %d\n", numclients(clients)); @@ -805,7 +818,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { // Try to process received string (which should contain one or more client responses/commands) // TODO - What if there were two server respones/commands and only one didn't need relaying? - if (!processrawstring(server_ssl, clientbuf, SOURCE_CLIENT, clients, i, &ircdstate, channels, settings)) { + if (!processrawstring(server_ssl, clientbuf, SOURCE_CLIENT, clients, i, &ircdstate, channels, settings, clientcodes)) { fprintf(stderr, "Error: bouncer-client failed to process raw string.\n"); debugprint(DEBUG_CRIT, "Error: bouncer-client failed to process raw string.\n"); } @@ -881,8 +894,8 @@ int main(int argc, char *argv[]) { exit(1); } else { if (strcmp(settings.replaymode, "none") && strcmp(settings.replaymode, "time") && strcmp(settings.replaymode, "lastspoke") && - strcmp(settings.replaymode, "noclients") && strcmp(settings.replaymode, "lastchange")) { - printf("main(): replaymode in configuration file must be one of \"none\", \"time\", \"lastspoke\", \"noclients\", or \"lastchange\".\n"); + strcmp(settings.replaymode, "noclients") && strcmp(settings.replaymode, "lastchange") && strcmp(settings.replaymode, "perclient")) { + printf("main(): replaymode in configuration file must be one of \"none\", \"time\", \"lastspoke\", \"noclients\", \"lastchange\" or \"perclient\".\n"); exit(1); } } -- cgit v1.2.3