From 177c8c843c5dad1741992a7ed24dd1902a0d9ba7 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Sun, 1 Apr 2018 03:03:16 +0200 Subject: It's now actually a semi-bouncer! It doesn't tell new clients which channels to join or anything, but it does relay everything to each client. --- blabouncer.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 148 insertions(+), 19 deletions(-) (limited to 'blabouncer.c') diff --git a/blabouncer.c b/blabouncer.c index ccbfa13..ca92909 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -24,6 +24,15 @@ #define NO_INPUT 1 #define TOO_LONG 2 +int debugmode = 0; + +// Print a debugging message, if debugging enabled +void debug(char *string) { + if (debugmode) { + printf("DEBUG: %s\n", string); + } +} + // Get stdin line with buffer overrun protection static int getstdin(char *prompt, char *buff, size_t sz) { int ch, extra; @@ -54,7 +63,7 @@ static int getstdin(char *prompt, char *buff, size_t sz) { return OK; } -// Append CR-LF to the end of a string +// Append CR-LF to the end of a string (after cleaning up any existing trailing CR or LF) void appendcrlf(char *string) { // Make sure it doesn't already end with CR or LF while (string[strlen(string) - 1] == '\r' || string[strlen(string) - 1] == '\r') { @@ -124,10 +133,9 @@ int createclientsocket(char *listenport) { listenport = BOUNCERLISTENPORT; int listener; // listening socket descriptor - int rv; // return value for getaddrinfo (for error message) - struct addrinfo hints, *ai, *p; + int yes = 1; // for enabling socket options with setsockopt // get us a socket and bind it memset(&hints, 0, sizeof hints); @@ -136,7 +144,7 @@ int createclientsocket(char *listenport) { hints.ai_flags = AI_PASSIVE; if ((rv = getaddrinfo(NULL, listenport, &hints, &ai)) != 0) { - fprintf(stderr, "selectserver: %s\n", gai_strerror(rv)); + fprintf(stderr, "bouncer-client: %s\n", gai_strerror(rv)); exit(1); } @@ -166,6 +174,9 @@ int createclientsocket(char *listenport) { } } + // allow address re-use + setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); // 1 as in non-zero as in enable + if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) { // failed to bind close(listener); @@ -175,7 +186,7 @@ int createclientsocket(char *listenport) { // if we got here, it means we didn't get bound if (p == NULL) { - fprintf(stderr, "selectserver: failed to bind\n"); + fprintf(stderr, "bouncer-client: failed to bind\n"); exit(2); } @@ -191,44 +202,76 @@ int createclientsocket(char *listenport) { } void dochat(int *serversockfd, int *clientsockfd) { - char serverbuf[MAXDATASIZE]; - int numbytes; // Number of bytes received from remote server + char serverbuf[MAXDATASIZE]; // buffer for receiving data on server socket + char clientbuf[MAXDATASIZE]; // buffer for receiving data on client socket(s) + int servernumbytes; // Number of bytes received from remote server char outgoingmsg[MAXDATASIZE]; // String to send to server int outgoingmsgrc; // Return code from getstdin() for outgoing message - fd_set rfds; + int fdmax; // highest nunmbered socket fd + + socklen_t addrlen; // client remote address size + char remoteIP[INET6_ADDRSTRLEN]; // remote IP (assume up to IPv6 size) + int newfd; // newly accept()ed socket descriptor + struct sockaddr_storage remoteaddr; // client address + int clientnumbytes; + + fdmax = *clientsockfd; // keep track of highest fd number, currently client socket as created last (after server socket) + + fd_set rfds; // set of read fds to monitor with select() while (1) { +printf("top of loop, fdmax %d.\n", fdmax); FD_ZERO(&rfds); // clear entries from fd set FD_SET(STDIN, &rfds); // add STDIN (fd 0) to read fds to monitor - FD_SET(*serversockfd, &rfds); // add our network socket to monitor +// FD_SET(*serversockfd, &rfds); // add our server network socket to monitor +// FD_SET(*clientsockfd, &rfds); // add our client network socket to monitor + + // Add all sockets to read fds to monitor (server socket, master client socket (for new client connections), all connected clients (for existing client connections)) + // TODO - make sure *serversockfd stays at the same value (probably 3?) in all cases - what if the server disconnects/reconnects/etc. + // TODO - only monitor connected clients perhaps? (Keep track of connected clients in some array rather than going x to fdmax each time - there may be gaps of disconnected sockets) + for (int i = *serversockfd; i <= fdmax; i++) { + printf("adding fd %d.\n", i); + FD_SET(i, &rfds); + } - // TODO - Change this to use number of fds + 1 instead of sockfd + 1 (I don't think they will be the same with multiple sockets/connections) - if (select(*serversockfd + 1, &rfds, NULL, NULL, NULL) < 0) { // network socket + 1, rfds, no writes, no exceptions/errors, no timeout + if (select(fdmax + 1, &rfds, NULL, NULL, NULL) < 0) { // network socket + 1, rfds, no writes, no exceptions/errors, no timeout printf("receive error, exiting!?\n"); perror("select"); // TODO exit here? let's try it. exit(1); } + // see if there's anything on the server side from the real IRCd if (FD_ISSET(*serversockfd, &rfds)) { - printf("reading socket!\n"); + printf("reading server socket!\n"); - if ((numbytes = recv(*serversockfd, serverbuf, MAXDATASIZE - 1, 0)) == -1) { + if ((servernumbytes = recv(*serversockfd, serverbuf, MAXDATASIZE - 1, 0)) == -1) { printf("receive error (-1), exiting...\n"); perror("recv"); exit(1); - } else if (numbytes == 0) { + } else if (servernumbytes == 0) { printf("socket closed (or no data received) (0), exiting...\n"); perror("recv"); exit(1); } - serverbuf[numbytes] = '\0'; + serverbuf[servernumbytes] = '\0'; - printf("RECEIVED: %s\n", serverbuf); + printf("BOUNCER-SERVER RECEIVED: %s\n", serverbuf); + printf("bouncer-server: sending it to all clients...\n"); + + for (int i = *clientsockfd + 1; i <= fdmax; i++) { + // TODO maybe see if things are in rfds + // TODO function this send-to-all baby up (used in approximately two places so far) + printf("bouncer-server: sending to client with fd %d.\n", i); + if (send(i, serverbuf, servernumbytes, 0) == -1) { + perror("send"); + } + } } + // see if there's anything from stdin if (FD_ISSET(STDIN, &rfds)) { printf("reading stdin!\n"); @@ -242,20 +285,103 @@ void dochat(int *serversockfd, int *clientsockfd) { appendcrlf(outgoingmsg); - if (send(*serversockfd, outgoingmsg, strlen(outgoingmsg), 0) == -1) { + if (send(*serversockfd, outgoingmsg, strlen(outgoingmsg), 0) == -1) { // 0 means no flags printf("send error, exiting...\n"); perror("send"); } } + + // go through all the remaining sockets to see if there's anything from the client sockets (new connections or existing clients sending messages) + for (int i = *clientsockfd; i <= fdmax; i++) { + printf("checking client socket %d.\n", i); + if (FD_ISSET(i, &rfds)) { + if (i == *clientsockfd) { + // handle new connections + addrlen = sizeof remoteaddr; + newfd = accept(*clientsockfd, (struct sockaddr *)&remoteaddr, &addrlen); + if (newfd == -1) { + // something went wrong when accept()ing + perror("accept"); + } else { + FD_SET(newfd, &rfds); // add to master set // TODO - needed? going to be re-done at the top anyway... + if (newfd > fdmax) { // keep track of the max + fdmax = newfd; + } + printf("bouncer-client: new connection from %s on socket %d\n", inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr*)&remoteaddr), remoteIP, INET6_ADDRSTRLEN), newfd); + } + } else { + // handle data from a client + if ((clientnumbytes = recv(i, clientbuf, sizeof clientbuf, 0)) <= 0) { + // got error or connection closed by client + if (clientnumbytes == 0) { + // connection closed + printf("bouncer-client: socket %d hung up\n", i); + } else { + perror("recv"); + } + close(i); // bye! + 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 + } else { + // we got some data from a client + // null terminate that baby + clientbuf[clientnumbytes] = '\0'; // TODO make sure this can't overrun if some super long line (max bytes?) was received + // clear up any newlines + while (clientbuf[strlen(clientbuf) - 1] == '\n' || clientbuf[strlen(clientbuf) - 1] == '\r') { + clientbuf[strlen(clientbuf) - 1] = '\0'; + } + 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"); + } + // send the same thing to all *other* clients + for (int j = *clientsockfd + 1; j <= fdmax; j++) { + // TODO maybe see if things are in rfds + // TODO function this send-to-all baby up (used in approximately two places so far) + // skip the current client (no need to send back to itself) + if (j == i) { + continue; + } + printf("bouncer-client: sending to client with fd %d.\n", j); + if (send(j, clientbuf, strlen(clientbuf), 0) == -1) { + perror("send"); + } + } + //~ // THIS GOOD STUFF FOR THE BOUNCING + //~ for(j = 0; j <= fdmax; j++) { + //~ // send to everyone! + //~ if (FD_ISSET(j, &master)) { + //~ // except the listener and ourselves + //~ if (j != listener && j != i) { + //~ if (send(j, buf, nbytes, 0) == -1) { + //~ perror("send"); + //~ } + //~ } + //~ } + //~ } + } + } + } + } } } int main(int argc, char *argv[]) { - if (argc != 3) { - fprintf(stderr,"usage: %s [hostname] [port]\n", argv[0]); + if (argc < 3) { + fprintf(stderr,"usage: %s hostname port [-d]\n", argv[0]); exit(1); } + if (argc == 4) { + if (!strcmp(argv[3], "-d")) { + debugmode = 1; + printf("debug mode enabled\n"); + } + } + // TODO: see if any of this can be shared (i.e. 1. avoid code duplication, and 2. see if variables can be shared between client/server sockets) // TODO: track fdmax @@ -264,7 +390,10 @@ int main(int argc, char *argv[]) { // BOUNCER-TO-SERVER socket things + // Create server socket int serversockfd = createserversocket(argv[1], argv[2]); + + // Create client socket (after server so we can use its fd number later as fdmax) int clientsockfd = createclientsocket(BOUNCERLISTENPORT); dochat(&serversockfd, &clientsockfd); -- cgit v1.2.3