Added nonblocking socket support for SSL_read/SSL_accept/SSL_write

This commit is contained in:
Olof hagsand 2021-04-02 18:06:45 +02:00
parent 698625aa65
commit 01d0ea905c
2 changed files with 113 additions and 84 deletions

View file

@ -216,47 +216,65 @@ openspec_handle_set(clicon_handle h,
/* Write evbuf to socket /* Write evbuf to socket
* see also this function in restcont_api_openssl.c * see also this function in restcont_api_openssl.c
*/ */
static ssize_t static int
buf_write(char *buf, buf_write(char *buf,
size_t buflen, size_t buflen,
int s, int s,
SSL *ssl) SSL *ssl)
{ {
ssize_t retval = -1; int retval = -1;
int ret; ssize_t len;
ssize_t totlen = 0;
if (ssl){ clicon_debug(1, "%s %lu", __FUNCTION__, buflen);
if ((ret = SSL_write(ssl, buf, buflen)) <= 0){ while (totlen < buflen){
int e = errno; if (ssl){
switch (SSL_get_error(ssl, ret)){ clicon_debug(1, "%s ssl", __FUNCTION__);
case SSL_ERROR_SYSCALL: /* 5 */ if ((len = SSL_write(ssl, buf+totlen, buflen-totlen)) <= 0){
if (e == ECONNRESET) {/* Connection reset by peer */ int e = errno;
clicon_debug(1, "%s HERE", __FUNCTION__); switch (SSL_get_error(ssl, len)){
if (ssl) case SSL_ERROR_SYSCALL: /* 5 */
SSL_free(ssl); if (e == ECONNRESET) {/* Connection reset by peer */
close(s); if (ssl)
clixon_event_unreg_fd(s, restconf_connection); SSL_free(ssl);
goto ok; /* Close socket and ssl */ close(s);
} clixon_event_unreg_fd(s, restconf_connection);
else{ goto ok; /* Close socket and ssl */
clicon_err(OE_RESTCONF, e, "SSL_write %d", e); }
goto done; else if (e == EAGAIN){
} clicon_debug(1, "%s write EAGAIN", __FUNCTION__);
break; usleep(10000);
default: continue;
clicon_err(OE_SSL, 0, "SSL_write"); }
goto done; else{
break; clicon_err(OE_RESTCONF, e, "SSL_write %d", e);
} goto done;
goto done; }
break;
default:
clicon_err(OE_SSL, 0, "SSL_write");
goto done;
break;
}
goto done;
}
} }
} else{
else{ if ((len = write(s, buf+totlen, buflen-totlen)) < 0){
if (write(s, buf, buflen) < 0){ if (errno == EAGAIN){
clicon_err(OE_UNIX, errno, "write"); clicon_debug(1, "%s write EAGAIN", __FUNCTION__);
goto done; usleep(10000);
continue;
}
else{
clicon_err(OE_UNIX, errno, "write");
goto done;
}
}
assert(len != 0);
} }
} totlen += len;
} /* while */
ok: ok:
retval = 0; retval = 0;
done: done:
@ -916,15 +934,9 @@ restconf_connection(int s,
goto done; goto done;
} }
else{ else{
#if 1
/* Return 0 from evhtp parser can be that it needs more data. /* Return 0 from evhtp parser can be that it needs more data.
*/ */
readmore = 1; /* Readmore */ readmore = 1; /* Readmore */
#else
clicon_debug(1, "%s bev is NULL 1", __FUNCTION__);
if (send_badrequest(h, s, conn->ssl) < 0) /* actually error */
goto done;
#endif
} }
} }
else{ else{
@ -1032,6 +1044,7 @@ restconf_accept_client(int fd,
evhtp_t *evhtp = NULL; evhtp_t *evhtp = NULL;
evhtp_connection_t *conn; evhtp_connection_t *conn;
int e; int e;
int readmore;
clicon_debug(1, "%s %d", __FUNCTION__, fd); clicon_debug(1, "%s %d", __FUNCTION__, fd);
if (rsock == NULL){ if (rsock == NULL){
@ -1087,53 +1100,65 @@ restconf_accept_client(int fd,
clicon_err(OE_SSL, 0, "SSL_set_fd"); clicon_err(OE_SSL, 0, "SSL_set_fd");
goto done; goto done;
} }
/* 1: OK, -1 fatal, 0: TLS/SSL handshake was not successful readmore = 1;
* Both error cases: Call SSL_get_error() with the return value ret while (readmore){
*/ readmore = 0;
if ((ret = SSL_accept(ssl)) != 1) { /* 1: OK, -1 fatal, 0: TLS/SSL handshake was not successful
e = SSL_get_error(ssl, ret); * Both error cases: Call SSL_get_error() with the return value ret
*/
switch (e){ if ((ret = SSL_accept(ssl)) != 1) {
case SSL_ERROR_SSL: /* 1 */ e = SSL_get_error(ssl, ret);
clicon_debug(1, "%s SSL_ERROR_SSL (not ssl mesage on ssl socket)", __FUNCTION__); switch (e){
if (send_badrequest(h, s, NULL) < 0) case SSL_ERROR_SSL: /* 1 */
goto done; clicon_debug(1, "%s SSL_ERROR_SSL (not ssl mesage on ssl socket)", __FUNCTION__);
SSL_free(ssl); if (send_badrequest(h, s, NULL) < 0)
if (close_openssl_socket(s, NULL) < 0) goto done;
goto done; SSL_free(ssl);
conn->ssl = NULL; if (close_openssl_socket(s, NULL) < 0)
evhtp_connection_free(conn); goto done;
goto ok; conn->ssl = NULL;
break; evhtp_connection_free(conn);
case SSL_ERROR_SYSCALL: /* 5 */ goto ok;
/* look at error stack/return value/errno */ break;
clicon_debug(1, "%s SSL_ERROR_SYSCALL", __FUNCTION__); case SSL_ERROR_SYSCALL: /* 5 */
SSL_free(ssl); /* look at error stack/return value/errno */
if (close_openssl_socket(s, NULL) < 0) clicon_debug(1, "%s SSL_ERROR_SYSCALL", __FUNCTION__);
goto done; SSL_free(ssl);
conn->ssl = NULL; if (close_openssl_socket(s, NULL) < 0)
evhtp_connection_free(conn); goto done;
goto ok; conn->ssl = NULL;
break; evhtp_connection_free(conn);
case SSL_ERROR_NONE: /* 0 */ goto ok;
case SSL_ERROR_ZERO_RETURN: /* 6 */ break;
case SSL_ERROR_WANT_READ: /* 2 */ case SSL_ERROR_WANT_READ: /* 2 */
case SSL_ERROR_WANT_WRITE: /* 3 */ case SSL_ERROR_WANT_WRITE: /* 3 */
case SSL_ERROR_WANT_CONNECT: /* 7 */ /* SSL_ERROR_WANT_READ is returned when the last operation was a read operation
case SSL_ERROR_WANT_ACCEPT: /* 8 */ * from a nonblocking BIO.
case SSL_ERROR_WANT_X509_LOOKUP: /* 4 */ * That is, it can happen if restconf_socket_init() below is called
case SSL_ERROR_WANT_ASYNC: /* 8 */ * with SOCK_NONBLOCK
case SSL_ERROR_WANT_ASYNC_JOB: /* 10 */ */
clicon_debug(1, "%s write SSL_ERROR_WANT_READ", __FUNCTION__);
usleep(10000);
readmore = 1;
break;
case SSL_ERROR_NONE: /* 0 */
case SSL_ERROR_ZERO_RETURN: /* 6 */
case SSL_ERROR_WANT_CONNECT: /* 7 */
case SSL_ERROR_WANT_ACCEPT: /* 8 */
case SSL_ERROR_WANT_X509_LOOKUP: /* 4 */
case SSL_ERROR_WANT_ASYNC: /* 8 */
case SSL_ERROR_WANT_ASYNC_JOB: /* 10 */
#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB #ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
case SSL_ERROR_WANT_CLIENT_HELLO_CB: /* 11 */ case SSL_ERROR_WANT_CLIENT_HELLO_CB: /* 11 */
#endif #endif
break; break;
default: default:
break; break;
}
clicon_err(OE_SSL, 0, "SSL_accept:%d", e);
goto done;
} }
clicon_err(OE_SSL, 0, "SSL_accept:%d", e); } /* while(readmore) */
goto done;
}
/* For client-cert authentication, check if any certs are present, /* For client-cert authentication, check if any certs are present,
* if not, send bad request * if not, send bad request
* Alt: set SSL_CTX_set_verify(ctx, SSL_VERIFY_FAIL_IF_NO_PEER_CERT) * Alt: set SSL_CTX_set_verify(ctx, SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
@ -1308,7 +1333,9 @@ openssl_init_socket(clicon_handle h,
goto done; goto done;
/* Open restconf socket and bind */ /* Open restconf socket and bind */
if (restconf_socket_init(netns, address, addrtype, port, if (restconf_socket_init(netns, address, addrtype, port,
SOCKET_LISTEN_BACKLOG, SOCK_NONBLOCK, &ss) < 0) SOCKET_LISTEN_BACKLOG,
SOCK_NONBLOCK, /* Also 0 is possible */
&ss) < 0)
goto done; goto done;
if ((rh = restconf_handle_get(h)) == NULL){ if ((rh = restconf_handle_get(h)) == NULL){
clicon_err(OE_XML, EFAULT, "No openssl handle"); clicon_err(OE_XML, EFAULT, "No openssl handle");

View file

@ -45,6 +45,7 @@
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_netns.h" #include "clixon_netns.h"
#ifdef HAVE_SETNS
/* /*
* @thanks Anders Franzén * @thanks Anders Franzén
*/ */
@ -104,6 +105,7 @@ get_sock(int usock,
done: done:
return retval; return retval;
} }
#endif /* HAVE_SETNS */
/*! Create and bind stream socket /*! Create and bind stream socket
* @param[in] sa Socketaddress * @param[in] sa Socketaddress