From bcdbc1755c4399e20d5297c6962629257e3b2100 Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Wed, 17 Apr 2019 22:09:21 +0200 Subject: More or less the same functionality, but now stable with clients connecting/disconnecting and lots more debug output --- blabouncer.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/blabouncer.c b/blabouncer.c index ca92909..23454cb 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -1,3 +1,6 @@ +// "server" means the real IRC server +// "client" means bouncer clients + // uses socket demo code from https://beej.us/guide/bgnet/html/single/bgnet.html // and getstdin() uses getLine() from https://stackoverflow.com/questions/4023895/ @@ -16,6 +19,7 @@ #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 BACKLOG 10 // maximum length to which the queue of pending connections for sockfd may grow +#define MAXCLIENTS 32 // maximum number of clients that can connect to the bouncer at a time #define BOUNCERLISTENPORT "1234" // TODO: change this to a config option! @@ -207,8 +211,10 @@ void dochat(int *serversockfd, int *clientsockfd) { 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 + int arr_clients[MAXCLIENTS]; // Array of all clients - 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) + int num_clients = 0; // Current number of clients - int fdmax; // highest nunmbered socket fd + int fdmax; // highest numbered socket fd socklen_t addrlen; // client remote address size char remoteIP[INET6_ADDRSTRLEN]; // remote IP (assume up to IPv6 size) @@ -218,31 +224,47 @@ void dochat(int *serversockfd, int *clientsockfd) { 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() + fd_set rfds; // set of read fds to monitor with select() - 0: stdin, 1: stdout, 2: stderr, 3 and higher: sockets (at time of writing, 3: real IRC server, 4: client listener, 5 and higher: clients) + + // set all the clients to be "not connected" + for (int i = 0; i < MAXCLIENTS; i++) { + arr_clients[i] = 0; + } while (1) { -printf("top of loop, fdmax %d.\n", fdmax); + 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 server network socket to monitor -// FD_SET(*clientsockfd, &rfds); // add our client 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)) + // Add all connected clients to monitor (only add ones that are connected (clients[i] > 0)) // 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); +// for (int i = *serversockfd; i <= fdmax; i++) { +// printf("adding fd %d.\n", i); +// FD_SET(i, &rfds); +// } + for (int i = 0; i < MAXCLIENTS; i++) { + if (arr_clients[i] > 0) { + printf("monitoring fd %d.\n", arr_clients[i]); + FD_SET(arr_clients[i], &rfds); + } } + printf("select()ing...\n"); + // check to see if anything in the fd_set is waiting - waits here until one of the fds in the set does something 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); +// OK temporarily (?) removed the exit again - surely if there's a problem we just ditch the fd and move on? +// exit(1); } + // 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. + // see if there's anything on the server side from the real IRCd if (FD_ISSET(*serversockfd, &rfds)) { printf("reading server socket!\n"); @@ -261,12 +283,18 @@ printf("top of loop, fdmax %d.\n", fdmax); printf("BOUNCER-SERVER RECEIVED: %s\n", serverbuf); printf("bouncer-server: sending it to all clients...\n"); + // relay/send to all clients... 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"); + // ...but only if they are connected + for (int j = 0; j < MAXCLIENTS; j++) { + if (arr_clients[j] == i) { + printf("bouncer-server: sending to client with fd %d.\n", i); + if (send(i, serverbuf, servernumbytes, 0) == -1) { + perror("send"); + } + } } } } @@ -291,12 +319,25 @@ printf("top of loop, fdmax %d.\n", fdmax); } } - // go through all the remaining sockets to see if there's anything from the client sockets (new connections or existing clients sending messages) + // go through all the remaining sockets to see if there's anything from the client sockets (either new connections or existing clients sending messages) + // (clear newfd before doing this so we can tell if we're querying a new client or not) + newfd = 0; for (int i = *clientsockfd; i <= fdmax; i++) { - printf("checking client socket %d.\n", i); + // skip if newfd as we know we have just accept()ed it + if (i == newfd) { + continue; + } + printf("checking client socket %d out of %d.\n", i, fdmax); if (FD_ISSET(i, &rfds)) { + printf("fd %d is FD_ISSET and it is a...\n", i); + // if value of clientsockfd then must be a new connection, if greater must be an existing connection if (i == *clientsockfd) { + printf("...new connection!\n"); // handle new connections + if (num_clients >= MAXCLIENTS) { + fprintf(stderr, "too many clients!\n"); + exit(1); // TODO - handle cleanly instead of exiting! + } addrlen = sizeof remoteaddr; newfd = accept(*clientsockfd, (struct sockaddr *)&remoteaddr, &addrlen); if (newfd == -1) { @@ -305,11 +346,22 @@ printf("top of loop, fdmax %d.\n", fdmax); } 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; + fdmax = newfd; + } + // Find a free element in the clients array and set to new fd value + for (int j = 0; j < MAXCLIENTS; j++) { + if (arr_clients[j] == 0) { + arr_clients[j] = newfd; + break; + } } + // TODO - Handle the "find a free element" loop not finding a free element + num_clients++; // Track total number of clients 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); + printf("bouncer-client: total client connections: %d\n", num_clients); } } else { + printf("...previous connection!\n"); // handle data from a client if ((clientnumbytes = recv(i, clientbuf, sizeof clientbuf, 0)) <= 0) { // got error or connection closed by client @@ -321,6 +373,17 @@ printf("top of loop, fdmax %d.\n", fdmax); } 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 + // Remove the client from the clients array + for (int j = 0; j < MAXCLIENTS; j++) { + if (arr_clients[j] == i) { + printf("found and clearing fd %d from arr_clients[%d]\n", i, j); + arr_clients[j] = 0; + break; + } + } + // TODO - Handle the "remove the client" loop not finding the old fd + num_clients--; // Track total number of clients + printf("bouncer-client: total client connections: %d\n", num_clients); } else { // we got some data from a client // null terminate that baby @@ -378,7 +441,7 @@ int main(int argc, char *argv[]) { if (argc == 4) { if (!strcmp(argv[3], "-d")) { debugmode = 1; - printf("debug mode enabled\n"); + debug("debug mode enabled\n"); } } @@ -392,9 +455,11 @@ 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); -- cgit v1.2.3