summaryrefslogtreecommitdiff
path: root/blabouncer.c
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-04-18 15:02:07 +0200
committerLuke Bratch <luke@bratch.co.uk>2019-04-18 15:02:07 +0200
commitfdbbc1afcec71c7a447d3fba98db2896289dd7e0 (patch)
treecd63bfaf9f01d12ea29f1b3ae314ffe6ae5b4445 /blabouncer.c
parenta519e3c9ded2342df72d1aa885d0a2173cfbf1c4 (diff)
First actual "bouncer" features - self connects and registers (NICK/USER) and automatically handles PING/PONG response/requests
Diffstat (limited to 'blabouncer.c')
-rw-r--r--blabouncer.c158
1 files changed, 144 insertions, 14 deletions
diff --git a/blabouncer.c b/blabouncer.c
index 4ed2c9a..bd280d2 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -1,3 +1,12 @@
+// TODO FOR TOMORROW:
+// - SPLIT THE RECEIVED MESSAGES INTO TOKENS AND FIGURE OUT WHEN WE'RE REGISTERED (NICK and USER from user are replied to with commands 001, 002, 003, and 004 from ircd when registration successful
+// - perhaps split with strtok()
+// - int registered = 0; already created to track when this has happened (assuming still want to do it this way)
+// - Actually maybe let's just try to register with the server ourselves without a client (telnet might be more useful than irssi for debugging)
+// - implement automatic PING/PONG responses
+// Example WHOIS reply:
+// BOUNCER-SERVER RECEIVED: :irc.tghost.co.uk 307 blabounce l_bratch :is identified for this nick
+
// "server" means the real IRC server
// "client" means bouncer clients
@@ -22,6 +31,12 @@
#define MAXDATASIZE 513 // max number of bytes we can get at once (RFC2812 says 512, plus one for null terminator)
#define STDIN 0 // stdin is fd 0
#define MAXCLIENTS 32 // maximum number of clients that can connect to the bouncer at a time
+#define MAXTOKENS 100 // maximum number of space-separated tokens per server response
+#define MAXPONGSIZE 32 // let's assume PING/PONG responses can't be larger than this (TODO - check this!)
+
+#define IRCNICK "blabounce" // TODO: change this to a config option!
+#define IRCUSER "blabounce" // TODO: change this to a config option!
+#define IRCREALNAME "Mr Bla Bouncer" // TODO: change this to a config option!
int debugmode = 0;
@@ -31,6 +46,9 @@ int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str,
char *sendertype;
+ // Decide what sort of text to prefix the debug output with
+ // At the moment if non-zero "except" is specified then it must be a message from a bouncer client
+ // and if "except" is zero then it must be a message from the real IRC server
if (except) {
sendertype = "bouncer-client";
} else {
@@ -58,6 +76,102 @@ int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str,
return 0;
}
+// Send whatever string to the real IRC server
+int sendtoserver(int *serversockfd, char *str, int str_len) {
+ printf("sendtoserver(): sending %s to IRC server (length %d).\n", str, str_len);
+ if (send(*serversockfd, str, str_len, 0) == -1) { // 0 is bitwise OR for no send() flags
+ printf("send error, exiting...\n");
+ perror("send");
+ }
+
+ return 0;
+}
+
+// Figure out what to do with each CRLF-split server response (if anything)
+// Now by splitting out the different components by space (ASCII 0x20)
+// Get serversockfd in case we need to send a response
+//
+// 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 processserverresponse(int *serversockfd, char *str) {
+ // Track which space-separated token within this response we're on
+ int counter = 0;
+
+ // Build array of each space-separated token (TODO - Use counter to stop splitting once we reach some reasonable value - i.e. once we're definitely past commands and into just free text)
+ char tokens[MAXTOKENS][MAXDATASIZE];
+
+ printf(" >> processserverresponse(): The server response is: \"%s\". Processing it...\n", str);
+
+ char *token;
+
+ while ((token = strsep(&str, " ")) != NULL) {
+ if (*token == '\0') continue; // Skip consecutive matches
+ printf("Response Token: \"%s\", length %zd.\n", token, strlen(token));
+ // Copy into the token array (strlen + 1 to get the NULL terminator)
+ strncpy(tokens[counter], token, strlen(token) + 1);
+ counter++;
+ }
+
+ printf("We found these tokens:\n");
+ for (int i = 0; i < counter; i++) {
+ printf("Token %d: \"%s\"\n", i, tokens[i]);
+ }
+
+ // <=============================================
+ // IRC command processing (commands from 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) {
+ printf("PING found and it is: %s with length %zd! Sending response...\n", tokens[0], strlen(tokens[0]));
+
+ char outgoingmsg[MAXDATASIZE]; // String to send to server
+ if (!snprintf(outgoingmsg, MAXDATASIZE, "PONG %s", tokens[1])) { // TODO - Make sure tokens[1] actually has a token
+ fprintf(stderr, "Error while preparing PONG response!\n");
+ exit(1);
+ }
+ appendcrlf(outgoingmsg);
+ sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg));
+
+ // We processed something so return true
+ return 1;
+ }
+ // =============================================>
+
+ printf("Done with processserverresponse()\n");
+
+ // If we got here then we didn't process anything
+ return 0;
+}
+
+// Figure out what the server sent and decide what to do with it (if anything)
+// First by splitting out the different responses by CRLF
+// Get serversockfd in case we need to send a response
+//
+// 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 processserverstring(int *serversockfd, char *str) {
+ // Track if something in this string got processed
+ int processed = 0;
+
+ printf("processserverstring(): The server sent: \"%s\". Processing it...\n", str);
+
+ // Split the string by CRLF and send each CRLF-separated string (each "response") on to processserverresponse(s)
+ char *token;
+
+ while ((token = strsep(&str, "\r\n")) != NULL) {
+ if (*token == '\0') continue; // Skip consecutive matches
+ printf("String Token: \"%s\", length %zd.\n", token, strlen(token));
+ // Pass on for the individual response to be processed
+ if (processserverresponse(serversockfd, token)) {
+ processed = 1;
+ }
+ }
+
+ printf("Done with processserverstring()\n");
+
+ return processed;
+}
+
// Where the big bouncing loop is
void dochat(int *serversockfd, int *clientsockfd) {
char serverbuf[MAXDATASIZE]; // buffer for receiving data on server socket
@@ -85,6 +199,24 @@ void dochat(int *serversockfd, int *clientsockfd) {
arr_clients[i] = 0;
}
+ // <=============================================
+ // Initialise IRC connecting/registration state
+
+ // Registered with the IRCd yet?
+////////////// int registered = 0;
+
+ // Send our NICK
+ snprintf(outgoingmsg, MAXDATASIZE, "NICK %s", IRCNICK); // TODO - Check for success (with return code)
+ appendcrlf(outgoingmsg);
+ sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg));
+
+ // Send our USER
+
+ snprintf(outgoingmsg, MAXDATASIZE, "USER %s 8 * : %s", IRCUSER, IRCREALNAME); // TODO - Check for success (with return code)
+ appendcrlf(outgoingmsg);
+ sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg));
+ // =============================================>
+
while (1) {
printf("top of loop, fdmax %d.\n", fdmax);
FD_ZERO(&rfds); // clear entries from fd set
@@ -111,8 +243,9 @@ void dochat(int *serversockfd, int *clientsockfd) {
}
// 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)
- // see if there's anything on the server side from the real IRCd
+ // See if there's anything to read from the server side (the real IRCd)
if (FD_ISSET(*serversockfd, &rfds)) {
printf("reading server socket!\n");
@@ -130,8 +263,14 @@ void dochat(int *serversockfd, int *clientsockfd) {
printf("BOUNCER-SERVER RECEIVED: %s\n", serverbuf);
printf("bouncer-server: sending it to all clients...\n");
- // Relay/send to all clients ("except" = 0 because this should send to all clients)
- sendtoallclients(clientsockfd, fdmax, arr_clients, serverbuf, servernumbytes, 0);
+ // Try to process received string (which should contain one or more server responses/commands)
+ // If 0 returned, do nothing because we processed it elsewhere
+ // If non-0 returned, send to all clients to see if they want to handle it
+ // TODO - What if there were two server respones/commands and only one didn't need relaying?
+ if (!processserverstring(serversockfd, serverbuf)) {
+ // Relay/send to all clients ("except" = 0 because this should send to all clients)
+ sendtoallclients(clientsockfd, fdmax, arr_clients, serverbuf, servernumbytes, 0);
+ }
}
// see if there's anything from stdin
@@ -148,10 +287,7 @@ void dochat(int *serversockfd, int *clientsockfd) {
appendcrlf(outgoingmsg);
- if (send(*serversockfd, outgoingmsg, strlen(outgoingmsg), 0) == -1) { // 0 means no flags
- printf("send error, exiting...\n");
- perror("send");
- }
+ sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg));
}
// go through all the remaining sockets to see if there's anything from the client sockets (either new connections or existing clients sending messages)
@@ -230,11 +366,7 @@ void dochat(int *serversockfd, int *clientsockfd) {
printf("BOUNCER-CLIENT RECEIVED: '%s'\n", clientbuf);
printf("bouncer-client: sending it to the server...\n");
appendcrlf(clientbuf);
- // send it to the server - TODO function up this sending thing
- if (send(*serversockfd, clientbuf, strlen(clientbuf), 0) == -1) { // 0 means no flags
- printf("send error, exiting...\n");
- perror("send");
- }
+ sendtoserver(serversockfd, clientbuf, strlen(clientbuf));
// send the same thing to all *other* clients (all except for fd "i")
sendtoallclients(clientsockfd, fdmax, arr_clients, clientbuf, strlen(clientbuf), i);
@@ -268,11 +400,9 @@ int main(int argc, char *argv[]) {
// Create server socket
int serversockfd = createserversocket(argv[1], argv[2]);
-printf("serversockfd: %d.\n", serversockfd);
// Create client socket (after server so we can use its fd number later as fdmax)
int clientsockfd = createclientsocket(BOUNCERLISTENPORT);
-printf("clientsockfd: %d.\n", clientsockfd);
dochat(&serversockfd, &clientsockfd);