summaryrefslogtreecommitdiff
path: root/blabouncer.c
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-05-19 14:25:22 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-05-19 14:25:22 +0100
commitc4de74dce0ada6bafd297222e85bfe170805d6fb (patch)
treeee57bf9c6a4c8021714c4725d73552581f8b72e5 /blabouncer.c
parenta25fdf5088dfa815d575134c12a60eadedef0aa9 (diff)
Rework all channel RPL_NAMREPLY code to properly relay user prefixes in channels when clients connect. Do this by asking server for the latest NAMES whenever a client connects and relay that to new clients.
Diffstat (limited to 'blabouncer.c')
-rw-r--r--blabouncer.c136
1 files changed, 84 insertions, 52 deletions
diff --git a/blabouncer.c b/blabouncer.c
index e817f7e..2c210b1 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -6,6 +6,9 @@
// - Comma separated channel list in JOINs/PARTs
// - Perhaps rename clients.ssl and server_ssl since they may not even be OpenSSL sockets
// - Is it possible to replay JOINs/PARTs accurately?
+// - Add help output for missing certs
+// - Only relay WHOIS replies to requesting client
+// - Remove any old channel name/mode/prefix code that isn't needed any more
// "server" means the real IRC server
// "client" means bouncer clients
@@ -62,6 +65,7 @@ struct channel {
char topicwhen[11]; // 32-bit unixtime is up to 10 characters (+1 for null char) We use "0" to mean "not set".
char nicks[MAXCHANUSERS][MAXNICKLENGTH]; // TODO - Need to modify this as people leave/join, not just when we first join
char namestype[2]; // Single character (@/*/=) (+1 for null char) // TODO - Is this a sensible name?
+ int gotnames; // Have we finished getting the RPL_NAMREPLYs for this channel yet?
};
struct ircdstrings {
@@ -106,6 +110,7 @@ struct client {
int pendingban; // Whether the client is waiting to hear back from a "MODE #channel b" command
int pendingwho; // Whether the client is waiting to hear back from a "MODE #channel" command
int pendinglist; // Whether the client is waiting to hear back from a "LIST" command
+ int pendingnames; // Count of RPL_NAMREPLYs the client is waiting on.
};
// Return index of requested client FD within the clients array.
@@ -280,6 +285,7 @@ int disconnectclient(int fd, struct client *clients, struct ircdstrings *ircdstr
clients[i].pendingban = 0;
clients[i].pendingwho = 0;
clients[i].pendinglist = 0;
+ clients[i].pendingnames = 0;
// Finish up with OpenSSL
SSL_free(clients[i].ssl);
// Close the socket
@@ -323,6 +329,7 @@ int createchannel(struct channel *channels, char *name, char *topic, char *topic
channels[i].topicwhen[strlen(topicwhen)] = '\0';
strncpy(channels[i].namestype, topic, strlen(namestype));
channels[i].namestype[strlen(namestype)] = '\0';
+ channels[i].gotnames = 0;
return 1;
break; // TODO - This should be safe to remove since return is hit first
}
@@ -436,21 +443,6 @@ int setchanneltopic(struct channel *channels, char *channelname, char *topic) {
return 0;
}
-int setchannelnamestype(struct channel *channels, char *channelname, char *type) {
- printf("setchannelnamestype(): given \"%s\" and \"%s\".\n", channelname, type);
-
- for (int i = 0; i < MAXCHANNELS; i++) {
- if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) {
- strncpy(channels[i].namestype, type, strlen(type));
- channels[i].namestype[strlen(type)] = '\0';
- return 1;
- }
- }
-
- // TODO - Make a failed return do something to callers
- return 0;
-}
-
int getchannelcount(struct channel *channels) {
int count = 0;
@@ -513,6 +505,27 @@ int removechannel(struct channel *channels, char *name) {
return 0;
}
+// Check if we have the NAMES for the channel 'name' already.
+// Return the index in the channel array if so, or 0 if not.
+int channelgotnames(struct channel *channels, char *name) {
+ printf("channelgotnames(): given '%s'.\n", name);
+ for (int i = 0; i < MAXCHANNELS; i++) {
+ if (strncmp(channels[i].name, name, strlen(name)) == 0) {
+ if (channels[i].gotnames) {
+ printf("channelgotnames(): channel '%s' gotnames was set, returning '%d'.\n", channels[i].name, channels[i].gotnames);
+ return channels[i].gotnames;
+ } else {
+ printf("channelgotnames(): channel '%s' gotnames was not set, returning '%d'.\n", channels[i].name, channels[i].gotnames);
+ return 0;
+ }
+ }
+ }
+
+ // We didn't find the channel, this isn't good! TODO - Do something if this happens.
+ printf("channelgotnames(): channel '%s' not found, this is bad, returning 0.\n", name);
+ return 0;
+}
+
// Send the requested number of lines of replay log to the requested client
// 'sourcefd' is the client to send to, and replayseconds is the number of
// seconds of replay to replay.
@@ -665,6 +678,9 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli
strncpy(ircdstrings->ircdname, tokens[3], strlen(tokens[3]));
// Null the end of the string
ircdstrings->ircdname[strlen(tokens[3])] = '\0';
+ // Receiving greeting 004 means we're now registered
+ // Request IRCv3 multi-prefix extension so we can more accurately inform new clients about current user prefixes
+ sendtoserver(server_ssl, "CAP REQ multi-prefix", strlen("CAP REQ multi-prefix"), 0, clients, settings);
free(strcopyPtr);
return 1;
}
@@ -744,19 +760,53 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli
} else if (strncmp(tokens[1], "333", strlen(tokens[1])) == 0) {
printf("Server 333 (RPL_TOPICWHOTIME) found, extracting who and when, and storing in channel struct.\n");
setchanneltopicwhotime(channels, tokens[3], tokens[4], tokens[5]);
- // Server 353 (RPL_NAMREPLY) add channel names type (secret/private/public) and the names of those already in the channel
+ // Server 353 (RPL_NAMREPLY), relay to all clients if we've just JOINed the channel, or relay to any clients
+ // who were pending RPL_NAMREPLYs if it's an existing channel.
} else if (strncmp(tokens[1], "353", strlen(tokens[1])) == 0) {
- // TODO - Do we also need to watch out for 366 (RPL_ENDOFNAMES)?
- printf("Server 353 (RPL_NAMREPLY) found, extracting nicks and storing in channel struct.\n");
- // Set channel type
- setchannelnamestype(channels, tokens[4], tokens[3]);
- // Add nicks
- // Strip prefix from first nick first
- stripprefix(tokens[5]);
- // Now go through all nicks and add them to the channel
- for (int i = 5; i < counter; i++) {
- addusertochannel(channels, tokens[4], tokens[i]);
+ // We were already in the channel and have the NAMES
+ if (!channelgotnames(channels, tokens[4])) {
+ printf("Server 353 received for a new channel, sending to all clients.\n");
+ // Relay to all clients
+ sendtoallclients(clients, str, sourcefd, settings);
+ } else {
+ // It must be a new channel and we don't have the NAMES
+ printf("Server 353 received for an existing channel, sending to all clients who were pending RPL_NAMREPLYs.\n");
+ // If any clients were pending RPL_NAMREPLYs, send this on to them
+ // TODO - Make sure clients only get the ones they were waiting on in case there are multiple conflicting requests going on at once
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingnames > 0) {
+ printf("Sending 353 RPL_NAMREPLY for channel '%s' to client with fd '%d' who was pending %d RPL_NAMREPLYs.\n", tokens[4], clients[i].fd, clients[i].pendingnames);
+ sendtoclient(clients[i].fd, str, clients, settings);
+ }
+ }
}
+ free(strcopyPtr);
+ return 1;
+ // Server 366 (RPL_ENDOFNAMES), relay to all clients if we've just JOINed the channel, or relay to
+ // and decrement from any clients who were waiting on RPL_NAMREPLY if it's an existing channel.
+ } else if (strncmp(tokens[1], "366", strlen(tokens[1])) == 0) {
+ int channelelement;
+ // We were already in the channel and have the NAMES
+ if (!(channelelement = channelgotnames(channels, tokens[3]))) {
+ printf("Server 366 received for a new channel, sending to all clients and set as got names.\n");
+ channels[channelelement].gotnames = 1;
+ // Relay to all clients
+ sendtoallclients(clients, str, sourcefd, settings);
+ } else {
+ // TODO - Make sure clients only get the ones they were waiting on in case there are multiple conflicting requests going on at once
+ printf("Server 366 received for an existing channel, sending to all clients who were pending RPL_NAMREPLYs and decrementing their pendingnames count.\n");
+ for (int i = 0; i < MAXCLIENTS; i++) {
+ if (clients[i].pendingnames > 0) {
+ sendtoclient(clients[i].fd, str, clients, settings);
+ // And decrement their pendingnames count
+ printf("Decrementing pendingnames due 366 RPL_ENDOFNAMES to for channel '%s' to client with fd '%d' who was pending %d RPL_NAMREPLYs.\n", tokens[3], clients[i].fd, clients[i].pendingnames);
+ clients[i].pendingnames--;
+ printf("Client with fd '%d' has '%d' pendingnames left.\n", clients[i].fd, clients[i].pendingnames);
+ }
+ }
+ }
+ free(strcopyPtr);
+ return 1;
}
// Server TOPIC received? Update our local channel topic info then relay to clients.
@@ -1065,6 +1115,9 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli
// Get the channel count so we can enumerate over all channels.
// Storing separately so we can skip over blank channels.
int channelcount = getchannelcount(channels);
+ // Set the client as pending RPL_NAMREPLYs for 'channelcount' channels
+ printf("Setting pendingnames to '%d' for client with fd '%d'.\n", channelcount, sourcefd);
+ clients[arrindex(clients, sourcefd)].pendingnames = channelcount;
// Get client to join channels, and tell client about those channels
for (int i = 0; i < channelcount; i++) {
@@ -1112,32 +1165,10 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli
sendtoclient(sourcefd, outgoingmsg, clients, settings);
}
- // Send list of names
- // Store count of names so we can increment it in the loop if we encounter gaps in the names array
- int namescount = getchannelnamescount(channels, channels[i].name);
- // Go through each name...
- for (int j = 0; j < namescount; j++) {
- // TODO - Batch these up and send multiple names per line
- // ...making sure it they are not null (probably a gap in the names array)
- if (!channels[i].nicks[j][0]) {
- // Skip this one and increment namescount so we find it later on instead
- namescount++;
- continue;
- }
- // If there was a nick found, send it to the client (one per line at the moment - TODO - batch them up into fewer lines)
- if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 353 %s %s %s :%s", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].namestype, channels[i].name, channels[i].nicks[j])) {
- fprintf(stderr, "Error while preparing USER just connected, channel NAMES responses!\n");
- exit(1);
- }
- sendtoclient(sourcefd, outgoingmsg, clients, settings);
- }
-
- // Once all names are sent, send the "end of /NAMES" 366 (RPL_ENDOFNAMES) message
- if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 366 %s %s :End of /NAMES list.", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].name)) {
- fprintf(stderr, "Error while preparing USER just connected, end of NAMES response!\n");
- exit(1);
- }
- sendtoclient(sourcefd, outgoingmsg, clients, settings);
+ // Get the latest RPL_NAMREPLY for this channel to relay to the client when it arrives
+ char namesreq[MAXDATASIZE];
+ snprintf(namesreq, MAXDATASIZE, "NAMES %s", channels[i].name);
+ sendtoserver(server_ssl, namesreq, strlen(namesreq), 0, clients, settings);
}
// Send our mode to the client
@@ -1492,6 +1523,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
clients[i].pendingban = 0;
clients[i].pendingwho = 0;
clients[i].pendinglist = 0;
+ clients[i].pendingnames = 0;
}
// Initialise OpenSSL (used for both client and server)