summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-04-17 22:09:21 +0200
committerLuke Bratch <luke@bratch.co.uk>2019-04-17 22:09:21 +0200
commitbcdbc1755c4399e20d5297c6962629257e3b2100 (patch)
tree19a4b1adddbf476e30960847c4ab50e22b7b3b06
parent177c8c843c5dad1741992a7ed24dd1902a0d9ba7 (diff)
More or less the same functionality, but now stable with clients connecting/disconnecting and lots more debug output
-rw-r--r--blabouncer.c99
1 files 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);