summaryrefslogtreecommitdiff
path: root/sockets.c
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-07-09 22:36:35 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-07-09 22:36:35 +0100
commit05d3d94613168187cbf7d54ac6de345bb75910dd (patch)
tree7293a4c9effa6a51683d091e3ff3debe1880f9db /sockets.c
parentc70cd5cccc966a35f175913f2281ce251fd62425 (diff)
Avoid SSL_accept() blocking if the client fails to do TLS negotiation.
Diffstat (limited to 'sockets.c')
-rw-r--r--sockets.c65
1 files changed, 65 insertions, 0 deletions
diff --git a/sockets.c b/sockets.c
index bf83176..d10811e 100644
--- a/sockets.c
+++ b/sockets.c
@@ -228,6 +228,7 @@ int socksend(SSL *fd, char *buf, int bufsize, int tls) {
}
}
+// Return character array of latest OpenSSL error
char *openssl_error_string() {
BIO *bio = BIO_new (BIO_s_mem ());
ERR_print_errors (bio);
@@ -240,3 +241,67 @@ char *openssl_error_string() {
BIO_free (bio);
return ret;
}
+
+// Set a socket "fd" to be blocking ("blocking" = 1) or non-blocking ("blocking" = 0).
+// Returns 1 on success or 0 on failure.
+int fd_toggle_blocking(int fd, int blocking) {
+ debugprint(DEBUG_FULL, "fd_toggle_blocking(): setting blocking to %d for fd %d.\n", blocking, fd);
+
+ // Save the current flags
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags == -1) {
+ // Error getting current flags
+ return 0;
+ }
+
+ // Add or remove O_NONBLOCK as appropriate
+ if (blocking) {
+ flags &= ~O_NONBLOCK;
+ } else {
+ flags |= O_NONBLOCK;
+ }
+
+ if (fcntl(fd, F_SETFL, flags) == -1) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+// Attempt to do SSL_accept() on a client with fd "fd". Expects the socket fd to have just been set
+// to non-blocking. Will make the socket blocking again and set the client's pendingsslaccept status
+// to 0 if SSL_accept() succeeds. Calls disconnectclient() on hard failure.
+// Returns 1 on success, 0 on hard failure, or -1 on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
+int openssl_accept(int fd, struct client *clients, struct ircdstate *ircdstate, struct settings *settings, struct clientcodes *clientcodes) {
+ // Get the index of the this client fd
+ int clientindex = arrindex(clients, fd);
+
+ // Clear OpenSSL errors before proceeding so we can reliably get errors from SSL_accept
+ ERR_clear_error();
+ // Try to SSL_accept();
+ int ret;
+ if ((ret = SSL_accept(clients[clientindex].ssl)) <= 0) {
+ // SSL_accept() either failed (bad) or is just pending read or write (which is OK)
+ int sslerr = SSL_get_error(clients[clientindex].ssl, ret);
+ if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) {
+ debugprint(DEBUG_CRIT, "SSL_accept() pending for new connection fd %d (ret = %d, sslerr = %d), looping...\n", clients[clientindex].fd, ret, sslerr);
+ return -1;
+ } else {
+ char* errstr = openssl_error_string();
+ debugprint(DEBUG_CRIT, "SSL_accept failed for new connection fd %d (ret = %d) - %s", clients[clientindex].fd, ret, errstr);
+ if (errstr != NULL) free(errstr);
+ disconnectclient(clients[clientindex].fd, clients, ircdstate, settings, clientcodes);
+ return 0;
+ }
+ } else {
+ debugprint(DEBUG_FULL, "SSL_accept succeeded for new connection fd %d.\n", clients[clientindex].fd);
+ // Change the socket back to blocking
+ if (!fd_toggle_blocking(clients[clientindex].fd, 1)) {
+ debugprint(DEBUG_CRIT, "fd_toggle_blocking off failed for fd %d: %s.\n", clients[clientindex].fd, strerror(errno));
+ disconnectclient(clients[clientindex].fd, clients, ircdstate, settings, clientcodes);
+ }
+ // And mark as no longer pending SSL_accept()
+ clients[clientindex].pendingsslaccept = 0;
+ return 1;
+ }
+}