diff --git a/include/tlsport.h b/include/tlsport.h index 7ed41ba..a8e9e27 100644 --- a/include/tlsport.h +++ b/include/tlsport.h @@ -27,6 +27,11 @@ struct tlsport { SSL_CTX *sslctx; }; +typedef enum { + TLSIO_READ = 1, + TLSIO_WRITE = 2, +} tls_io_dir_t; + /* this function shall be called before main loop */ int ssllib_init(void); @@ -45,4 +50,9 @@ int tls_connect(const char *, const char *, struct tlsport *); */ int tls_close(struct tlsport *); +/* read or write (depends on tls_io_dir_t) data via tlsport + * returns amount of bytes ridden or written, -1 in case of error + */ +ssize_t tls_io(struct tlsport *, void *, size_t, tls_io_dir_t); + #endif /* __TLSPORT_H__ */ diff --git a/src/tls.c b/src/tls.c index b015b8a..1700fb5 100644 --- a/src/tls.c +++ b/src/tls.c @@ -186,3 +186,84 @@ int tls_close(struct tlsport *p) return e; } + +ssize_t tls_io(struct tlsport *p, void *buf, size_t bf_size, tls_io_dir_t dr) +{ + int sel_read, sel_write; + int r, w = 0; + fd_set readfd, writefd; + + if(!p || !buf) { + errno = EINVAL; + return -1; + } + + if(!bf_size) { + errno = EINVAL; + return -1; + } + + ssllib_io: + sel_read = sel_write = 0; + do { + switch(dr) { + case TLSIO_READ: + r = SSL_read(p->ssl, buf, bf_size); + if(r > 0) return r; + break; + case TLSIO_WRITE: + r = SSL_write(p->ssl, (char *)buf + sizeof(char)*w, bf_size - w); + if(r == (bf_size - w)) return bf_size; + else if(r > 0) w += r; + break; + default: + errno = EINVAL; + return -1; + } + + switch(SSL_get_error(p->ssl, r)) { + case SSL_ERROR_NONE: + break; + case SSL_ERROR_WANT_READ: + sel_read = 1; /* is required to wait during read */ break; + case SSL_ERROR_WANT_WRITE: + sel_write = 1; /* is required to wait during write */ break; + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + break; + case SSL_ERROR_SYSCALL: + if(errno == EAGAIN || errno == EINTR) break; + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: /* expected close */ + default: + return -1; + break; + } + } while(SSL_pending(p->ssl) && !sel_read); + + /* wait to read */ + if(r != SSL_ERROR_NONE && sel_read) { + FD_ZERO(&readfd); + FD_SET(p->fd, &readfd); + + ioread_select: + r = select(p->fd + 1, &readfd, NULL, NULL, NULL); + if(r <= 0) { + if(errno == EAGAIN || errno == EINTR) goto ioread_select; + else return -1; + } else if(FD_ISSET(p->fd, &readfd)) goto ssllib_io; + } + + /* wait to write */ + if(r != SSL_ERROR_NONE && sel_write) { + FD_ZERO(&readfd); + FD_ZERO(&writefd); + FD_SET(p->fd, &readfd); + FD_SET(p->fd, &writefd); + + r = select(p->fd + 1, &readfd, &writefd, NULL, NULL); + if(r && FD_ISSET(p->fd, &writefd)) goto ssllib_io; + } + + return 0; +}