summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--blabouncer.c167
1 files changed, 148 insertions, 19 deletions
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);