summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--blabouncer.c116
-rw-r--r--blabouncer.conf6
-rw-r--r--sockets.c20
-rw-r--r--sockets.h6
4 files changed, 94 insertions, 54 deletions
diff --git a/blabouncer.c b/blabouncer.c
index dcfdd1b..c883ed6 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -9,16 +9,11 @@
// - "01:53:47 -!- ServerMode/#test [b] by irc.tghost.co.uk" on existing clients when new client connects
// - Keep track of changing user nicks/modes
// - Should replay log do more than PRIVMSGs?
-// - Make certificate paths configurable
// - Implement TLS on real IRCd server side
-// - Make TLS optional
-// - Make cert/key filenames configurable
-// - Make listen port configurable
// - Consider moving the three fd-related arrays into one struct
// - Check authentication before even getting to the send functions to save unnecessary processing
// - Configurable auto channels
// - Comma separated channel list in JOINs/PARTs
-// - Long raw strings like a 321/322/323 channel LIST, and the initial server welcome, need to be read and split properly
// - Only send some things to the requesting client (e.g. LIST replies)
// - Normal (non-replay) log
// - Alert when clients connect/authenticate/disconnect
@@ -106,6 +101,7 @@ struct settings {
char conffile[PATH_MAX];
char certfile[PATH_MAX];
char keyfile[PATH_MAX];
+ int clienttls;
};
// Return index of requested client FD within arr_clients
@@ -125,7 +121,7 @@ int arrindex(int arr_clients[], int clientfd) {
}
// Send whatever string to a specific client by providing the FD
-int sendtoclient(int fd, char *str, int arr_clients[], int arr_authed[], SSL **arr_ssl) {
+int sendtoclient(int fd, char *str, int arr_clients[], int arr_authed[], SSL **arr_ssl, struct settings *settings) {
appendcrlf(str); // Do this just before sending so callers don't need to worry about it
// Find the client in the clients array and make sure they are authenticated
@@ -140,8 +136,7 @@ int sendtoclient(int fd, char *str, int arr_clients[], int arr_authed[], SSL **a
}
printf("sendtoclient(): sending \"%s\" (length %zd) to client with fd %d.\n", str, strlen(str), fd);
-// if (send(fd, str, strlen(str), 0) == -1) {
- if (SSL_write(arr_ssl[arrindex(arr_clients, fd)], str, strlen(str)) == -1) {
+ if (socksend(arr_ssl[arrindex(arr_clients, fd)], str, strlen(str), settings->clienttls) == -1) {
perror("error: sendtoclient() send()\n");
return 0;
}
@@ -174,7 +169,7 @@ int disconnectclient(int fd, int arr_clients[], int arr_authed[]) {
// "except" is used to send to all clients _except_ the fd provided (except = 0 (EXCEPT_NONE) avoids this, i.e. sends to all)
// "except" is really the "sourcefd" and is also used as part of the authentication check - this is messy and they should perhaps be two separate arguments.
// TODO - is passing str_len useful if we're appendcrlfing and then using strlen(str) in the send? I guess not... (As long as we're always null terminated in the correct place.)
-int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str, int except, int arr_authed[], SSL **arr_ssl) {
+int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str, int except, int arr_authed[], SSL **arr_ssl, struct settings *settings) {
char *sendertype;
@@ -220,8 +215,7 @@ int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str,
continue;
}
printf("sendtoallclients(): %s: sending '%s' to client with fd %d.\n", sendertype, str, i);
-// if (send(i, str, strlen(str), 0) == -1) {
- if (SSL_write(arr_ssl[arrindex(arr_clients, i)], str, strlen(str)) == -1) {
+ if (socksend(arr_ssl[arrindex(arr_clients, i)], str, strlen(str), settings->clienttls) == -1) {
perror("error: sendtoallclients() send()\n");
}
}
@@ -559,7 +553,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
}
// And then send to all clients
- sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl);
+ sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl, settings);
return 1;
}
@@ -580,7 +574,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
}
// And then send to all clients
- sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl);
+ sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl, settings);
return 1;
}
@@ -647,7 +641,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
setchanneltopicwhotime(channels, tokens[2], prefixcopy, timenowstr);
// And then finally relay to all clients
- sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl);
+ sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl, settings);
return 1;
}
@@ -655,7 +649,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
if (strncmp(tokens[1], "PRIVMSG", strlen(tokens[1])) == 0) {
printf("Server PRIVMSG found and it is: %s with length %zd! Next token is '%s'. Relaying to all clients.\n", tokens[0], strlen(tokens[0]), tokens[2]);
- sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl);
+ sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl, settings);
// Write to replay log
writereplayline(str);
@@ -695,13 +689,13 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
// Send IRC greeting strings (001/RPL_WELCOME, 002/RPL_YOURHOST, 003/RPL_CREATED, 004/RPL_MYINFO) to client
snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting001);
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting002);
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting003);
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting004);
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
// Get the channel count so we can enumerate over all channels.
// Storing separately so we can skip over blank channels.
@@ -722,7 +716,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses!\n");
exit(1);
}
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
// Send topic (or lack thereof) to client
// If there isn't one set (we guess this if topic timestamp is 0), send 331 RPL_NOTOPIC
@@ -733,7 +727,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
exit(1);
}
// ..and send it to the client
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
// If there is one set, send 332 RPL_TOPIC and 333 RPL_TOPICWHOTIME
} else {
// Prepare the topic message...
@@ -742,7 +736,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
exit(1);
}
// ..and send it to the client
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
// Next prepare the topic who/when message...
if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 333 %s %s %s %s", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].name, channels[i].topicwho, channels[i].topicwhen)) {
@@ -750,7 +744,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
exit(1);
}
// ..and send it to the client
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
}
// Send list of names
@@ -770,7 +764,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
fprintf(stderr, "Error while preparing USER just connected, channel NAMES responses!\n");
exit(1);
}
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
}
// Once all names are sent, send the "end of /NAMES" 366 (RPL_ENDOFNAMES) message
@@ -778,7 +772,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
fprintf(stderr, "Error while preparing USER just connected, end of NAMES response!\n");
exit(1);
}
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
}
// Figure out how many lines to replay
@@ -797,7 +791,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
exit(1);
}
printf("Sending replay line: '%s'.\n", outgoingmsg);
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
}
return 1;
@@ -812,7 +806,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
fprintf(stderr, "Error while preparing PONG response!\n");
exit(1);
}
- sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl);
+ sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, arr_ssl, settings);
// We processed something so return true
return 1;
@@ -845,7 +839,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc
exit(1);
}
// Send to all except source client
- sendtoallclients(clientsockfd, fdmax, arr_clients, outgoingmsg, sourcefd, arr_authed, arr_ssl);
+ sendtoallclients(clientsockfd, fdmax, arr_clients, outgoingmsg, sourcefd, arr_authed, arr_ssl, settings);
// Write to replay log
writereplayline(outgoingmsg);
@@ -983,7 +977,7 @@ int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source
// Relay/send to all clients ("except" = 0 because this should send to all clients)
// TODO - Is this really going to send the original string if we have messed it with it in processrawstring() and friends!?
printf("bouncer-server: sending unprocessed server message \"%s\" to all clients, length %zd.\n", messages[i], strlen(messages[i]));
- sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], EXCEPT_NONE, arr_authed, arr_ssl);
+ sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], EXCEPT_NONE, arr_authed, arr_ssl, settings);
break;
case SOURCE_CLIENT: // If message(s) were from a real IRC client
// Send to server
@@ -992,7 +986,7 @@ int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source
printf("bouncer-client: sending unprocessed client message \"%s\" to all other clients, length %zd.\n", messages[i], strlen(messages[i]));
// send the same thing to all *other* clients (all except for source fd)
- sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], sourcefd, arr_authed, arr_ssl);
+ sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], sourcefd, arr_authed, arr_ssl, settings);
break;
default:
fprintf(stderr, "Unexpected raw IRC string source for unprocessed message \"%s\", length %zd.!\n", messages[i], strlen(messages[i]));
@@ -1015,7 +1009,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
int outgoingmsgrc; // Return code from getstdin() for outgoing message
int arr_clients[MAXCLIENTS]; // Array of all client FDs - 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 arr_authed[MAXCLIENTS]; // Array of client authentication statuses - 0 means not authenticated, 1 means authenticated. Element numbers match those of arr_clients.
- SSL *arr_ssl[MAXCLIENTS]; // Array of OpenSSL structures
+ SSL *arr_ssl[MAXCLIENTS]; // Array of OpenSSL structures when using TLS, or faked by casting fd ints to SSL* if not. - TODO - Can we drop one of either arr_clients or arr_ssl now?
int num_clients = 0; // Current number of clients
int fdmax; // highest numbered socket fd
@@ -1076,13 +1070,16 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
channels = malloc(sizeof(struct channel) * MAXCHANNELS);
// =============================================>
- // OpenSSL context for client side (that clients connect to)
+ // OpenSSL context for client side (that clients connect to) (need to create this whether or not using TLS as it is referenced later)
SSL_CTX *ctx;
- // Initialise OpenSSL
- init_openssl();
- ctx = create_context();
- configure_context(ctx, settings->certfile, settings->keyfile);
+ // If using client TLS
+ if (settings->clienttls) {
+ // Initialise OpenSSL
+ init_openssl();
+ ctx = create_context();
+ configure_context(ctx, settings->certfile, settings->keyfile);
+ }
while (1) {
printf("top of loop, fdmax %d.\n", fdmax);
@@ -1210,14 +1207,20 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
arr_clients[j] = newfd;
// Ensure its authentication status is set to 0
arr_authed[j] = 0;
- // Set as OpenSSL FD and SSL_accept it
- arr_ssl[j] = SSL_new(ctx);
- SSL_set_fd(arr_ssl[j], newfd);
- if (SSL_accept(arr_ssl[j]) <= 0) {
- printf("SSL_accept failed for fd %d.\n", j);
- ERR_print_errors_fp(stderr);
+ // If using TLS then...
+ if (settings->clienttls) {
+ // ...set as OpenSSL FD and SSL_accept it
+ arr_ssl[j] = SSL_new(ctx);
+ SSL_set_fd(arr_ssl[j], newfd);
+ if (SSL_accept(arr_ssl[j]) <= 0) {
+ printf("SSL_accept failed for fd %d.\n", j);
+ ERR_print_errors_fp(stderr);
+ } else {
+ printf("SSL_accept succeeded for fd %d.\n", j);
+ }
} else {
- printf("SSL_accept succeeded for fd %d.\n", j);
+ // If not using TLS then cast newfd to SSL* even though it will just be the original newfd int really
+ arr_ssl[j] = (SSL*)(long int)newfd;
}
break;
}
@@ -1230,8 +1233,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
} else {
printf("...previous connection!\n");
// handle data from a client
-// if ((clientnumbytes = recv(i, clientbuf, sizeof clientbuf, 0)) <= 0) {
- if ((clientnumbytes = SSL_read(arr_ssl[arrindex(arr_clients, i)], clientbuf, sizeof clientbuf)) <= 0) {
+ if ((clientnumbytes = sockread(arr_ssl[arrindex(arr_clients, i)], clientbuf, sizeof clientbuf, settings->clienttls)) <= 0) {
// got error or connection closed by client
if (clientnumbytes == 0) {
// connection closed
@@ -1330,16 +1332,22 @@ int main(int argc, char *argv[]) {
exit(1);
}
- // What is the certificate file path?
- if (!getconfstr("certfile", settings.conffile, settings.certfile)) {
- printf("main(): error getting 'certfile' from configuration file.\n");
- exit(1);
- }
+ // Should the bouncer use TLS for clients?
+ settings.clienttls = getconfint("clienttls", settings.conffile);
- // What is the certificate key file path?
- if (!getconfstr("keyfile", settings.conffile, settings.keyfile)) {
- printf("main(): error getting 'keyfile' from configuration file.\n");
- exit(1);
+ // If so, load the certificates
+ if (settings.clienttls) {
+ // What is the certificate file path?
+ if (!getconfstr("certfile", settings.conffile, settings.certfile)) {
+ printf("main(): error getting 'certfile' from configuration file.\n");
+ exit(1);
+ }
+
+ // What is the certificate key file path?
+ if (!getconfstr("keyfile", settings.conffile, settings.keyfile)) {
+ printf("main(): error getting 'keyfile' from configuration file.\n");
+ exit(1);
+ }
}
// 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)
diff --git a/blabouncer.conf b/blabouncer.conf
index 3428960..72646de 100644
--- a/blabouncer.conf
+++ b/blabouncer.conf
@@ -17,6 +17,10 @@ password = "bananas"
# Port the bouncer should listen on
clientport = "1234"
+# Enable TLS for clients connecting to the bouncer ("1" for yes or "0" for no)
+# If "0" then certfile and keyfile need not be set
+clienttls = "1"
+
# Real IRC server the bouncer connects to
ircserver = "irc.blatech.net"
@@ -24,7 +28,9 @@ ircserver = "irc.blatech.net"
ircserverport = "6667"
# Certificate file
+# If clienttls = "0" then this need not be set
certfile = "cert.pem"
# Certificate key file
+# If clienttls = "0" then this need not be set
keyfile = "key.pem"
diff --git a/sockets.c b/sockets.c
index 0f78b21..c5ea41f 100644
--- a/sockets.c
+++ b/sockets.c
@@ -165,3 +165,23 @@ void configure_context(SSL_CTX *ctx, char *certfile, char *keyfile) {
exit(EXIT_FAILURE);
}
}
+
+// Read from a socket, whether or not using TLS
+int sockread(SSL *fd, char *buf, int bufsize, int tls) {
+ if (tls) {
+ return SSL_read(fd, buf, bufsize);
+ } else {
+ // Cast the supposed SSL *fd to a long int if we're not using TLS
+ return recv((long int)fd, buf, bufsize, 0);
+ }
+}
+
+// Write to a socket, whether or not using TLS
+int socksend(SSL *fd, char *buf, int bufsize, int tls) {
+ if (tls) {
+ return SSL_write(fd, buf, bufsize);
+ } else {
+ // Cast the supposed SSL *fd to a long int if we're not using TLS
+ return send((long int)fd, buf, bufsize, 0);
+ }
+}
diff --git a/sockets.h b/sockets.h
index c342de2..099fc53 100644
--- a/sockets.h
+++ b/sockets.h
@@ -34,4 +34,10 @@ SSL_CTX *create_context();
void configure_context(SSL_CTX *ctx, char *certfile, char *keyfile);
+// Read from a socket, whether or not using TLS
+int sockread(SSL *fd, char *buf, int bufsize, int tls);
+
+// Write to a socket, whether or not using TLS
+int socksend(SSL *fd, char *buf, int bufsize, int tls);
+
#endif