On Thu, Sep 16, 2021 at 11:00:31PM +0200, Kristaps Dzonsons wrote: > Hi, > > I'm porting a nonblocking, polling OpenSSL system to libtls. However, I'm > not sure how this is non-hackily possible without SSL_pending(3) to detect > if less data is read with tls_read() than is buffered. > > writer: > tls_write(40) > > reader: > poll(POLLIN, INFTIM) -> POLLIN /* descriptor has a read */ > tls_read(20) /* 20 bytes read, 20 bytes remain */ > poll(POLLIN, INFTIM) -> 0 /* data was buffered */ > > This introduces tls_pending(3), which calls through to SSL_pending(3), and > includes a simple example in tls_read(3). > > Beck's tutorial says that "libtls is designed to do the common things you do > for making TLS connections easy", and I'm not sure my use case meets that > standard. If it looks reasonable, however, I can also add the regression > tests.
In general you should call tls_read() until it retruns TLS_WANT_POLL*. Then you poll again. So you need to adapt the poll loop a bit but in most cases this is not hard to do. > Thank you, > > Kristaps > Index: tls.c > =================================================================== > RCS file: /cvs/src/lib/libtls/tls.c,v > retrieving revision 1.89 > diff -u -p -r1.89 tls.c > --- tls.c 1 Feb 2021 15:35:41 -0000 1.89 > +++ tls.c 16 Sep 2021 20:57:32 -0000 > @@ -788,6 +788,16 @@ tls_handshake(struct tls *ctx) > return (rv); > } > > +size_t > +tls_pending(const struct tls *ctx) > +{ > + > + if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) > + return 0; > + > + return (size_t)SSL_pending(ctx->ssl_conn); > +} > + > ssize_t > tls_read(struct tls *ctx, void *buf, size_t buflen) > { > Index: tls.h > =================================================================== > RCS file: /cvs/src/lib/libtls/tls.h,v > retrieving revision 1.58 > diff -u -p -r1.58 tls.h > --- tls.h 22 Jan 2020 06:44:02 -0000 1.58 > +++ tls.h 16 Sep 2021 20:57:32 -0000 > @@ -177,6 +177,7 @@ int tls_connect_socket(struct tls *_ctx, > int tls_connect_cbs(struct tls *_ctx, tls_read_cb _read_cb, > tls_write_cb _write_cb, void *_cb_arg, const char *_servername); > int tls_handshake(struct tls *_ctx); > +size_t tls_pending(const struct tls *_ctx); > ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen); > ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen); > int tls_close(struct tls *_ctx); > Index: man/tls_read.3 > =================================================================== > RCS file: /cvs/src/lib/libtls/man/tls_read.3,v > retrieving revision 1.7 > diff -u -p -r1.7 tls_read.3 > --- man/tls_read.3 9 Jul 2019 17:58:33 -0000 1.7 > +++ man/tls_read.3 16 Sep 2021 20:57:32 -0000 > @@ -27,6 +27,7 @@ > .Nm tls_handshake , > .Nm tls_error , > .Nm tls_close , > +.Nm tls_pending , > .Nm tls_reset > .Nd use a TLS connection > .Sh SYNOPSIS > @@ -51,6 +52,8 @@ > .Fn tls_close "struct tls *ctx" > .Ft void > .Fn tls_reset "struct tls *ctx" > +.Ft size_t > +.Fn tls_pending "const struct tls *ctx" > .Sh DESCRIPTION > .Fn tls_read > reads > @@ -58,6 +61,8 @@ reads > bytes of data from the socket into > .Fa buf . > It returns the amount of data read. > +.Fn tls_pending > +returns the number of bytes that may be read without blocking. > .Pp > .Fn tls_write > writes > @@ -99,6 +104,9 @@ and > .Fn tls_write > return a size on success or -1 on error. > .Pp > +.Fn tls_pending > +returns a size or zero if no data is buffered for immediate reading. > +.Pp > .Fn tls_handshake > and > .Fn tls_close > @@ -198,6 +206,28 @@ while (len > 0) { > buf += ret; > len -= ret; > } > + } > +} > +\&... > +.Ed > +.Pp > +For non-blocking input, the following example demonstrates how to handle > +buffered data when the descriptor may have none to read: > +.Bd -literal -offset indent > +\&... > +pfd[0].fd = fd; > +pfd[0].events = POLLIN; > +for (;;) { > + int timeo; > + > + timeo = tls_pending(tls) ? 0 : INFTIM; > + if (poll(pfd, 1, timeo) == -1) > + err(1, "poll"); > + if ((pfd[0].revents & POLLIN) || tls_pending(tls)) { > + ssize_t ret; > + > + ret = tls_read(ctx, buf, len); > + \&... > } > } > \&... -- :wq Claudio