diff options
author | Luke Bratch <luke@bratch.co.uk> | 2019-05-17 22:18:28 +0100 |
---|---|---|
committer | Luke Bratch <luke@bratch.co.uk> | 2019-05-17 22:18:28 +0100 |
commit | 6c4c42c68c53091a0ca04ae6885ed3cc24216348 (patch) | |
tree | 8223b896b5da3f8f40cffe4970edf7e85c379cff | |
parent | 751bee2c2ceb7e9426bf00a669da5679c20ee41a (diff) |
Properly support changing our nick while connecting by updating it everywhere including initial server welcome strings.
Make sure channels are cleared when PARTing them.
Ignore most commands from clients until they are registered with us.
Send the correct current nick when sending NOTICEs.
-rw-r--r-- | blabouncer.c | 57 | ||||
-rw-r--r-- | functions.c | 94 | ||||
-rw-r--r-- | functions.h | 4 |
3 files changed, 139 insertions, 16 deletions
diff --git a/blabouncer.c b/blabouncer.c index 559e2b1..c4396f4 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -114,6 +114,7 @@ struct client { int fd; // Client socket fd - 0 means not connected, greater than 0 means connected and the value is the fd number (so we know which ones to try to read and send to) int authed; // Client authentication status - 0 means not authenticated, 1 means authenticated. SSL *ssl; // OpenSSL structures when using TLS, or faked by casting fd ints to SSL* if not. - TODO - Can we drop one of either "int fd" or "SSL *ssl" now? + int registered; // Whether the client has finished registering with the bouncer }; // Return index of requested client FD within the clients array. @@ -159,8 +160,8 @@ int sendtoclient(int fd, char *str, struct client *clients, struct settings *set } // Disconnect the client fd "fd" by close()ing it and remove -// it from the arr_clients array of clients. -// Also set its authentication status to 0. +// it from the array of clients. +// Also set its authentication and registration statuses to 0. int disconnectclient(int fd, struct client *clients) { printf("disconnectclient(): disconnecting client fd '%d'\n", fd); close(fd); // bye! @@ -170,6 +171,7 @@ int disconnectclient(int fd, struct client *clients) { printf("found and clearing fd %d from clients[%d]\n", fd, i); clients[i].fd = 0; clients[i].authed = 0; + clients[i].registered = 0; return 1; } } @@ -476,7 +478,12 @@ int removechannel(struct channel *channels, char *name) { if (strncmp(channels[i].name, name, strlen(name)) == 0) { // ..and NULL its name (0th character = '\0') channels[i].name[0] = '\0'; - printf("removechannel(): channel removed and topicwhen set to '%s'.\n", channels[i].topicwhen); + printf("removechannel(): channel '%s' removed and topicwhen set to '%s'.\n", name, channels[i].topicwhen); + // Finally clear all its users + for (int j = 0; j < MAXCHANUSERS; j++) { + channels[i].nicks[j][0] = '\0'; + } + printf("removechannel(): channel '%s' users cleared.\n", name); return 1; } } @@ -491,7 +498,7 @@ int removechannel(struct channel *channels, char *name) { // 'sourcefd' is the client to send to, and replayseconds is the number of // seconds of replay to replay. // Returns 1 for success or 0 for failure. -int doreplay(int sourcefd, int replayseconds, struct client *clients, struct settings *settings) { +int doreplay(int sourcefd, int replayseconds, struct client *clients, struct settings *settings, struct ircdstrings *ircdstrings) { char outgoingmsg[MAXDATASIZE]; // Figure out how many lines to replay @@ -502,7 +509,7 @@ int doreplay(int sourcefd, int replayseconds, struct client *clients, struct set printf("Error getting number of replay lines.\n"); exit(1); } else if (numlines == 0) { - snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :0 replay log lines found in the time requested, nothing to send.", settings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :0 replay log lines found in the time requested, nothing to send.", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings); return 1; } @@ -768,12 +775,19 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli // Was it us? if (strncmp(ircdstrings->nickuserhost, tokens[0], strlen(ircdstrings->nickuserhost)) == 0) { + // Make a copy of the old nickuserhost for updategreetings() below + char *nickuserhostcpy = strdup(ircdstrings->nickuserhost); // Update nickuserhost with the new :nick!user@host updatenickuserhost(ircdstrings->nickuserhost, tokens[2]); printf("Updated nickuserhost to '%s'.\n", ircdstrings->nickuserhost); - // Update ircnick + // Prepare to update ircnick and greetings strings + // Temporary copy of new nickuserhost char *prefixcopy = strdup(ircdstrings->nickuserhost); + // Get nick from it extractnickfromprefix(prefixcopy); + // Update greeting strings for relaying to new clients + updategreetings(ircdstrings->greeting001, ircdstrings->greeting002, ircdstrings->greeting003, ircdstrings->greeting004, ircdstrings->nickuserhost, nickuserhostcpy, tokens[2], ircdstrings->ircnick); + // Update our nick strcpy(ircdstrings->ircnick, prefixcopy); printf("Updated ircnick to '%s'.\n", ircdstrings->ircnick); } @@ -830,13 +844,13 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli sendtoclient(sourcefd, outgoingmsg, clients, settings); // Send our own greeting message - snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Welcome to blabouncer!", settings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Welcome to blabouncer!", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings); - snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer commands are all prefixed with BLABOUNCER which you can usually send using \"/QUOTE BLABOUNCER\"", settings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Blabouncer commands are all prefixed with BLABOUNCER which you can usually send using \"/QUOTE BLABOUNCER\"", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings); - snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Valid blabouncer commands are:", settings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Valid blabouncer commands are:", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings); - snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [seconds]\" (Where [seconds] is the number of seconds of replay log to replay.)", settings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [seconds]\" (Where [seconds] is the number of seconds of replay log to replay.)", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings); // Get the channel count so we can enumerate over all channels. @@ -917,9 +931,19 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli sendtoclient(sourcefd, outgoingmsg, clients, settings); } + // Set the client as registered + clients[arrindex(clients, sourcefd)].registered = 1; + // Catch the client up with the default number of seconds of replay - doreplay(sourcefd, settings->replayseconds, clients, settings); + doreplay(sourcefd, settings->replayseconds, clients, settings, ircdstrings); + + return 1; + } + // Pretty much ignore anything else the client says if it's not registered yet, + // as the first thing we want to hear is either PASS or USER + if (!clients[arrindex(clients, sourcefd)].registered) { + printf("Ignoring client command '%s' from sourcefd '%d' as not registered yet.\n", tokens[0], sourcefd); return 1; } @@ -1021,19 +1045,19 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli 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.", settings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Invalid number of seconds of replay requested by REPLAY command.", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings); return 1; } - doreplay(sourcefd, replayseconds, clients, settings); + doreplay(sourcefd, replayseconds, clients, settings, ircdstrings); return 1; // Unrecognised BLABOUNCER command received, send some help instructions } else { 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:", settings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :Unrecognised BLABOUNCER command received. Valid commands are:", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings); - snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [seconds]\" (Where [seconds] is the number of seconds of replay log to replay.)", settings->ircnick); + snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [seconds]\" (Where [seconds] is the number of seconds of replay log to replay.)", ircdstrings->ircnick); sendtoclient(sourcefd, outgoingmsg, clients, settings); return 1; } @@ -1186,10 +1210,11 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { // Set up clients structure struct client clients[MAXCLIENTS]; - // Set all the clients to be "not connected" and "not authenticated" + // Set all the clients to be "not connected", "not authenticated", and "not registered" for (int i = 0; i < MAXCLIENTS; i++) { clients[i].fd = 0; clients[i].authed = 0; + clients[i].registered = 0; } // Initialise OpenSSL (used for both client and server) diff --git a/functions.c b/functions.c index 6034250..ffd94f1 100644 --- a/functions.c +++ b/functions.c @@ -1,5 +1,50 @@ #include "functions.h" +// Internal function just to replace nick in server greeting strings +// (as in ":servername 00x oldnick :Blablabla" -> ":servername 00x newnick :Blablabla") +void updategreetingnick(char *greeting, char *greetingnum, char *newnick, char *oldnick) { + printf("updategreetingnick(): '%s' '%s' '%s' '%s'.\n", greeting, greetingnum, newnick, oldnick); + + // Find the position of the old nick in the current greeting + char searchstr[MAXDATASIZE]; + snprintf(searchstr, MAXDATASIZE, " %s %s :", greetingnum, oldnick); + char *ret; + ret = strstr(greeting, searchstr); + + // If ret not found, try again without the colon (e.g. for greeting 004) + if (ret == NULL) { + snprintf(searchstr, MAXDATASIZE, " %s %s ", greetingnum, oldnick); + ret = strstr(greeting, searchstr); + } + + // If ret *still* not found, abandon ship + if (ret == NULL) { + printf("Error updating greeting string, substring not found. Exiting!\n"); + exit(1); + } + + int pos = ret - greeting + 5; // +5 for " 001 " + + // Copy the start of the old greeting into a new string + char greetingtmp[MAXDATASIZE]; + strncpy(greetingtmp, greeting, pos); + // Terminate it + greetingtmp[pos] = '\0'; + + // Now smash everything (start of old greeting + new nick + remainder of old greeting) + // together into the new greeting, put in a new temporary string + char greetingtmp2[MAXDATASIZE]; + if (!snprintf(greetingtmp2, MAXDATASIZE, "%s%s %s", greetingtmp, newnick, greeting + pos + strlen(oldnick) + 1)) { + fprintf(stderr, "Error while preparing new greeting string!\n"); + exit(1); + } + + // And finally copy back to source string + strcpy(greeting, greetingtmp2); + + printf("updategreetingnick(): Built new greeting '%s' '%s', length '%ld'.\n", greetingnum, greeting, strlen(greeting)); +} + // Get stdin line with buffer overrun protection int getstdin(char *prompt, char *buff, size_t sz) { int ch, extra; @@ -182,3 +227,52 @@ void updatenickuserhost(char *nickuserhost, char *nick) { printf("updatenickuserhost(): new nickuserhost '%s', length '%ld'.\n", nickuserhost, strlen(nickuserhost)); } + +// Update existing greeting strings with a new nickuserhost and new nick +void updategreetings(char *greeting001, char *greeting002, char *greeting003, char *greeting004, char *newnickuserhost, char *oldnickuserhost, char *newnick, char *oldnick) { + printf("updategreetings(): updating greetings with new nickuserhost '%s' and nick '%s'.\n", newnickuserhost, newnick); + + // nickuserhost and greeting001's final component first + // (final component as in ":servername 001 nick :Blablabla final!com@ponent" + + // Make copies of the nickuserhosts to work with + char newnickuserhostcpy[MAXDATASIZE]; + strcpy(newnickuserhostcpy, newnickuserhost); + stripprefix(newnickuserhostcpy); + char oldnickuserhostcpy[MAXDATASIZE]; + strcpy(oldnickuserhostcpy, oldnickuserhost); + stripprefix(oldnickuserhostcpy); + + // Find the position of the old nickuserhost in the current greeting 001 + char *ret; + ret = strstr(greeting001, oldnickuserhostcpy); + int pos = ret - greeting001; + + // Terminate greeting001 where the nickuserhost begins + greeting001[pos] = '\0'; + + // Stick the new nickuserhost in place in a temporary greeting 001 string + char greetingtmp[MAXDATASIZE]; + snprintf(greetingtmp, MAXDATASIZE, "%s%s", greeting001, newnickuserhostcpy); + + // Terminate it + greetingtmp[pos + strlen(newnickuserhostcpy)] = '\0'; + + // Copy back to source greeting 001 + strcpy(greeting001, greetingtmp); + + printf("updategreetings(): new greeting 001 '%s', length '%ld'.\n", greeting001, strlen(greeting001)); + + // Get the new nick + + char newnickcpy[MAXDATASIZE]; + strcpy(newnickcpy, newnick); + extractnickfromprefix(newnickcpy); + + // greeting nicks next + // (as in ":servername 00x oldnick :Blablabla" -> ":servername 00x newnick :Blablabla") + updategreetingnick(greeting001, "001", newnickcpy, oldnick); + updategreetingnick(greeting002, "002", newnickcpy, oldnick); + updategreetingnick(greeting003, "003", newnickcpy, oldnick); + updategreetingnick(greeting004, "004", newnickcpy, oldnick); +} diff --git a/functions.h b/functions.h index de631e9..2877bc5 100644 --- a/functions.h +++ b/functions.h @@ -14,6 +14,7 @@ #include <sys/select.h> #define MAXDATASIZE 513 // max number of bytes we can get at once (RFC2812 says 512, plus one for null terminator) +#define MAXTOKENS 100 // maximum number of (CRLF or space) separated tokens per server response we expect (TODO - check this is reasonable) // getstdin() return codes #define OK 0 @@ -38,4 +39,7 @@ void extractnickfromprefix(char *string); // Update an existing nickuserhost string with a new nick void updatenickuserhost(char *nickuserhost, char *nick); +// Update an existing 001 greeting with a new nickuserhost +void updategreetings(char *greeting001, char *greeting002, char *greeting003, char *greeting004, char *newnickuserhost, char *oldnickuserhost, char *newnick, char *oldnick); + #endif |