summaryrefslogtreecommitdiff
path: root/src/ssl_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ssl_io.c')
-rw-r--r--src/ssl_io.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/src/ssl_io.c b/src/ssl_io.c
new file mode 100644
index 0000000..883380f
--- /dev/null
+++ b/src/ssl_io.c
@@ -0,0 +1,269 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "iopause.h"
+#include "buffer.h"
+#include "taia.h"
+#include "ucspissl.h"
+#include "error.h"
+
+static int leftstatus = 0;
+static char leftbuf[16 * 1024];
+static int leftlen;
+static int leftpos;
+
+static int rightstatus = 0;
+static char rightbuf[16 * 1024];
+static int rightlen;
+static int rightpos;
+
+int ssl_io(SSL *ssl,int fdleft,int fdright,unsigned int timeout) {
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x[4];
+ int xlen;
+ iopause_fd *io0;
+ iopause_fd *ioleft;
+ iopause_fd *io1;
+ iopause_fd *ioright;
+ int r;
+ int rc = 0;
+ int rfd;
+ int wfd;
+
+ rfd = SSL_get_fd(ssl); /* XXX */
+ if (rfd == -1) {
+ close(fdleft); close(fdright);
+ return -1;
+ }
+ wfd = SSL_get_fd(ssl); /* XXX */
+ if (wfd == -1) {
+ close(fdleft); close(fdright);
+ return -1;
+ }
+
+ for (;;) {
+ xlen = 0;
+
+ if (leftstatus == -1 && rightstatus == -1)
+ goto DONE;
+
+ io0 = 0;
+ if (leftstatus == 0 && rightstatus != 1) {
+ io0 = &x[xlen++];
+ io0->fd = rfd;
+ io0->events = IOPAUSE_READ;
+ }
+
+ ioleft = 0;
+ if (leftstatus == 1) {
+ ioleft = &x[xlen++];
+ ioleft->fd = fdleft;
+ ioleft->events = IOPAUSE_WRITE;
+ }
+
+ ioright = 0;
+ if (rightstatus == 0) {
+ ioright = &x[xlen++];
+ ioright->fd = fdright;
+ ioright->events = IOPAUSE_READ;
+ }
+
+ io1 = 0;
+ if (rightstatus == 1) {
+ io1 = &x[xlen++];
+ io1->fd = wfd;
+ io1->events = IOPAUSE_WRITE;
+ }
+
+ if (taia_now(&now) == -1) {
+ errno = ETIMEDOUT;
+ rc = -1;
+ goto BOMB;
+ }
+ taia_uint(&deadline,timeout);
+ taia_add(&deadline,&now,&deadline);
+ iopause(x,xlen,&deadline,&now);
+
+ for (r = 0; r < xlen; ++r)
+ if (x[r].revents) goto EVENTS;
+
+ if (io0 && !ssl_pending(ssl)) {
+ close(fdleft);
+ leftstatus = -1;
+ continue;
+ }
+ errno = ETIMEDOUT;
+ rc = -1;
+ goto BOMB;
+
+
+EVENTS:
+ if (io0 && io0->revents) {
+ r = SSL_read(ssl,leftbuf,sizeof(leftbuf));
+ ssl_errno = SSL_get_error(ssl,r);
+ switch (ssl_errno) {
+ case SSL_ERROR_NONE:
+ leftstatus = 1;
+ leftpos = 0;
+ leftlen = r;
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ if (rightstatus == -1) goto DONE;
+ close(fdleft);
+ leftstatus = -1;
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (errno == EAGAIN || errno == EINTR) break;
+ close(fdleft);
+ leftstatus = -1;
+ if (!errno) break;
+ /* premature close */
+ if (errno == ECONNRESET && rightstatus == -1) goto DONE;
+ goto BOMB;
+ case SSL_ERROR_SSL:
+/* Continuing after a received SSL error given the socket is still
+ * potentially active might be a noble cause, but is impracticle.
+ * We consider an SSL_ERROR_SSL as application failure; not TLS
+ * and close the connection gracefully.
+ * if (errno == EAGAIN || errno == EINTR) break;
+ * if (!errno) break;
+ */
+ goto DONE;
+ default:
+ close(fdleft);
+ leftstatus = -1;
+ if (rightstatus == 1) break;
+ if (ssl_shutdown_pending(ssl)) goto DONE;
+ goto BOMB;
+ }
+ }
+
+ if (ioleft && ioleft->revents) {
+ r = buffer_unixwrite(fdleft,leftbuf + leftpos,leftlen - leftpos);
+ if (r == -1) {
+ if (errno == EINTR || errno == EWOULDBLOCK) {
+ /* retry */
+ }
+ else if (errno == EPIPE || errno == EAGAIN) {
+ if (rightstatus == -1) goto DONE;
+ close(fdleft);
+ leftstatus = -1;
+ } else {
+ rc = -1;
+ goto BOMB;
+ }
+ }
+ else {
+ leftpos += r;
+ if (leftpos == leftlen) {
+ leftstatus = 0;
+ if ((r = ssl_pending(ssl))) {
+ if (r > sizeof(leftbuf)) r = sizeof(leftbuf);
+ r = SSL_read(ssl,leftbuf,r);
+ ssl_errno = SSL_get_error(ssl,r);
+ switch (ssl_errno) {
+ case SSL_ERROR_NONE:
+ leftstatus = 1;
+ leftpos = 0;
+ leftlen = r;
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ if (rightstatus == -1) goto DONE;
+ close(fdleft);
+ leftstatus = -1;
+ break;
+ default:
+ rc = -1;
+ goto BOMB;
+ }
+ }
+ }
+ }
+ }
+
+ if (ioright && ioright->revents) {
+ r = buffer_unixread(fdright,rightbuf,sizeof(rightbuf));
+ if (r == -1) {
+ if (errno == EINTR || errno == EWOULDBLOCK) {
+ /* retry */
+ } else {
+ rc = -1;
+ goto BOMB; /* errno == EAGAIN => unrecoverable */
+ }
+ }
+ else if (r == 0) {
+ close(fdright);
+ rightstatus = -1;
+ if (ssl_shutdown(ssl)) goto DONE;
+ if (leftstatus == -1) goto DONE;
+ }
+ else {
+ rightstatus = 1;
+ rightpos = 0;
+ rightlen = r;
+ }
+ }
+
+ if (io1 && io1->revents) {
+ r = SSL_write(ssl,rightbuf + rightpos,rightlen - rightpos);
+ ssl_errno = SSL_get_error(ssl,r);
+ switch (ssl_errno) {
+ case SSL_ERROR_NONE:
+ rightpos += r;
+ if (rightpos == rightlen) rightstatus = 0;
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ close(fdright);
+ rightstatus = -1;
+ if (leftstatus == -1) goto DONE;
+ if (ssl_shutdown(ssl)) goto DONE;
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (errno == EAGAIN || errno == EINTR) break;
+ if (errno == EPIPE) {
+ close(fdright);
+ rightstatus = -1;
+ if (leftstatus == -1) goto DONE;
+ if (ssl_shutdown(ssl)) goto DONE;
+ break;
+ }
+ default:
+ rc = -1;
+ goto BOMB;
+ }
+ }
+ }
+
+
+BOMB:
+ r = errno;
+ if (leftstatus != -1) close(fdleft);
+ if (rightstatus != -1) close(fdright);
+ if (!ssl_shutdown_sent(ssl)) ssl_shutdown(ssl);
+ if (!ssl_shutdown_pending(ssl)) ssl_shutdown(ssl);
+ shutdown(wfd,2);
+ errno = r;
+ return rc;
+
+
+DONE:
+ if (!ssl_shutdown_sent(ssl)) ssl_shutdown(ssl);
+ if (!ssl_shutdown_pending(ssl)) ssl_shutdown(ssl);
+ shutdown(wfd,2);
+ if (leftstatus != -1) close(fdleft);
+ if (rightstatus != -1) close(fdright);
+ return 0;
+}