diff options
author | Luke Bratch <luke@bratch.co.uk> | 2019-07-09 22:36:35 +0100 |
---|---|---|
committer | Luke Bratch <luke@bratch.co.uk> | 2019-07-09 22:36:35 +0100 |
commit | 05d3d94613168187cbf7d54ac6de345bb75910dd (patch) | |
tree | 7293a4c9effa6a51683d091e3ff3debe1880f9db /sockets.c | |
parent | c70cd5cccc966a35f175913f2281ce251fd62425 (diff) |
Avoid SSL_accept() blocking if the client fails to do TLS negotiation.
Diffstat (limited to 'sockets.c')
-rw-r--r-- | sockets.c | 65 |
1 files changed, 65 insertions, 0 deletions
@@ -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; + } +} |