aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/libpq/be-secure-openssl.c81
-rw-r--r--src/backend/libpq/be-secure.c143
-rw-r--r--src/backend/libpq/pqcomm.c3
-rw-r--r--src/include/libpq/libpq-be.h4
4 files changed, 79 insertions, 152 deletions
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index d13ce334ccc..37af6e4fdaf 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -511,14 +511,11 @@ be_tls_close(Port *port)
* Read data from a secure connection.
*/
ssize_t
-be_tls_read(Port *port, void *ptr, size_t len)
+be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
{
ssize_t n;
int err;
- int waitfor;
- int latchret;
-rloop:
errno = 0;
n = SSL_read(port->ssl, ptr, len);
err = SSL_get_error(port->ssl, n);
@@ -528,39 +525,15 @@ rloop:
port->count += n;
break;
case SSL_ERROR_WANT_READ:
+ *waitfor = WL_SOCKET_READABLE;
+ errno = EWOULDBLOCK;
+ n = -1;
+ break;
case SSL_ERROR_WANT_WRITE:
- /* Don't retry if the socket is in nonblocking mode. */
- if (port->noblock)
- {
- errno = EWOULDBLOCK;
- n = -1;
- break;
- }
-
- waitfor = WL_LATCH_SET;
-
- if (err == SSL_ERROR_WANT_READ)
- waitfor |= WL_SOCKET_READABLE;
- else
- waitfor |= WL_SOCKET_WRITEABLE;
-
- latchret = WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
-
- /*
- * We'll, among other situations, get here if the low level
- * routine doing the actual recv() via the socket got interrupted
- * by a signal. That's so we can handle interrupts once outside
- * openssl, so we don't jump out from underneath its covers. We
- * can check this both, when reading and writing, because even
- * when writing that's just openssl's doing, not a 'proper' write
- * initiated by postgres.
- */
- if (latchret & WL_LATCH_SET)
- {
- ResetLatch(MyLatch);
- ProcessClientReadInterrupt(true); /* preserves errno */
- }
- goto rloop;
+ *waitfor = WL_SOCKET_WRITEABLE;
+ errno = EWOULDBLOCK;
+ n = -1;
+ break;
case SSL_ERROR_SYSCALL:
/* leave it to caller to ereport the value of errno */
if (n != -1)
@@ -595,12 +568,10 @@ rloop:
* Write data to a secure connection.
*/
ssize_t
-be_tls_write(Port *port, void *ptr, size_t len)
+be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
{
ssize_t n;
int err;
- int waitfor;
- int latchret;
/*
* If SSL renegotiations are enabled and we're getting close to the
@@ -630,7 +601,6 @@ be_tls_write(Port *port, void *ptr, size_t len)
errmsg("SSL failure during renegotiation start")));
}
-wloop:
errno = 0;
n = SSL_write(port->ssl, ptr, len);
err = SSL_get_error(port->ssl, n);
@@ -640,30 +610,15 @@ wloop:
port->count += n;
break;
case SSL_ERROR_WANT_READ:
+ *waitfor = WL_SOCKET_READABLE;
+ errno = EWOULDBLOCK;
+ n = -1;
+ break;
case SSL_ERROR_WANT_WRITE:
-
- waitfor = WL_LATCH_SET;
-
- if (err == SSL_ERROR_WANT_READ)
- waitfor |= WL_SOCKET_READABLE;
- else
- waitfor |= WL_SOCKET_WRITEABLE;
-
- latchret = WaitLatchOrSocket(MyLatch, waitfor, port->sock, 0);
-
- /*
- * Check for interrupts here, in addition to secure_write(),
- * because an interrupted write in secure_raw_write() will return
- * here, and we cannot return to secure_write() until we've
- * written something.
- */
- if (latchret & WL_LATCH_SET)
- {
- ResetLatch(MyLatch);
- ProcessClientWriteInterrupt(true); /* preserves errno */
- }
-
- goto wloop;
+ *waitfor = WL_SOCKET_WRITEABLE;
+ errno = EWOULDBLOCK;
+ n = -1;
+ break;
case SSL_ERROR_SYSCALL:
/* leave it to caller to ereport the value of errno */
if (n != -1)
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index c2c1842eb8e..4e7acbe0804 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -127,30 +127,45 @@ ssize_t
secure_read(Port *port, void *ptr, size_t len)
{
ssize_t n;
+ int waitfor;
retry:
#ifdef USE_SSL
+ waitfor = 0;
if (port->ssl_in_use)
{
- n = be_tls_read(port, ptr, len);
+ n = be_tls_read(port, ptr, len, &waitfor);
}
else
#endif
{
n = secure_raw_read(port, ptr, len);
+ waitfor = WL_SOCKET_READABLE;
}
- /* retry after processing interrupts */
- if (n < 0 && errno == EINTR)
+ /* In blocking mode, wait until the socket is ready */
+ if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
{
- /*
- * We tried to read data, the socket was empty, and we were
- * interrupted while waiting for readability. We only process
- * interrupts if we got interrupted while reading and when in blocking
- * mode. In other cases it's better to allow the interrupts to be
- * handled at higher layers.
- */
- ProcessClientReadInterrupt(!port->noblock); /* preserves errno */
+ int w;
+
+ Assert(waitfor);
+
+ w = WaitLatchOrSocket(MyLatch,
+ WL_LATCH_SET | waitfor,
+ port->sock, 0);
+
+ /* Handle interrupt. */
+ if (w & WL_LATCH_SET)
+ {
+ ResetLatch(MyLatch);
+ ProcessClientReadInterrupt(true);
+
+ /*
+ * We'll retry the read. Most likely it will return immediately
+ * because there's still no data available, and we'll wait
+ * for the socket to become ready again.
+ */
+ }
goto retry;
}
@@ -173,7 +188,6 @@ secure_raw_read(Port *port, void *ptr, size_t len)
* Try to read from the socket without blocking. If it succeeds we're
* done, otherwise we'll wait for the socket using the latch mechanism.
*/
-rloop:
#ifdef WIN32
pgwin32_noblock = true;
#endif
@@ -182,37 +196,6 @@ rloop:
pgwin32_noblock = false;
#endif
- if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
- {
- int w;
- int save_errno = errno;
-
- w = WaitLatchOrSocket(MyLatch,
- WL_LATCH_SET | WL_SOCKET_READABLE,
- port->sock, 0);
-
- if (w & WL_LATCH_SET)
- {
- ResetLatch(MyLatch);
- /*
- * Force a return, so interrupts can be processed when not
- * (possibly) underneath a ssl library.
- */
- errno = EINTR;
- return -1;
- }
- else if (w & WL_SOCKET_READABLE)
- {
- goto rloop;
- }
-
- /*
- * Restore errno, clobbered by WaitLatchOrSocket, so the caller can
- * react properly.
- */
- errno = save_errno;
- }
-
return n;
}
@@ -224,33 +207,54 @@ ssize_t
secure_write(Port *port, void *ptr, size_t len)
{
ssize_t n;
+ int waitfor;
retry:
+ waitfor = 0;
#ifdef USE_SSL
if (port->ssl_in_use)
{
- n = be_tls_write(port, ptr, len);
+ n = be_tls_write(port, ptr, len, &waitfor);
}
else
#endif
{
n = secure_raw_write(port, ptr, len);
+ waitfor = WL_SOCKET_WRITEABLE;
}
- /* retry after processing interrupts */
- if (n < 0 && errno == EINTR)
+ if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
{
- /*
- * We tried to send data, the socket was full, and we were interrupted
- * while waiting for writability. We only process interrupts if we got
- * interrupted while writing and when in blocking mode. In other cases
- * it's better to allow the interrupts to be handled at higher layers.
- */
- ProcessClientWriteInterrupt(!port->noblock);
+ int w;
+
+ Assert(waitfor);
+ w = WaitLatchOrSocket(MyLatch,
+ WL_LATCH_SET | waitfor,
+ port->sock, 0);
+
+ /* Handle interrupt. */
+ if (w & WL_LATCH_SET)
+ {
+ ResetLatch(MyLatch);
+ ProcessClientWriteInterrupt(true);
+
+ /*
+ * We'll retry the write. Most likely it will return immediately
+ * because there's still no data available, and we'll wait
+ * for the socket to become ready again.
+ */
+ }
goto retry;
}
+ /*
+ * Process interrupts that happened while (or before) sending. Note that
+ * we signal that we're not blocking, which will prevent some types of
+ * interrupts from being processed.
+ */
+ ProcessClientWriteInterrupt(false);
+
return n;
}
@@ -259,8 +263,6 @@ secure_raw_write(Port *port, const void *ptr, size_t len)
{
ssize_t n;
-wloop:
-
#ifdef WIN32
pgwin32_noblock = true;
#endif
@@ -269,36 +271,5 @@ wloop:
pgwin32_noblock = false;
#endif
- if (n < 0 && !port->noblock && (errno == EWOULDBLOCK || errno == EAGAIN))
- {
- int w;
- int save_errno = errno;
-
- w = WaitLatchOrSocket(MyLatch,
- WL_LATCH_SET | WL_SOCKET_WRITEABLE,
- port->sock, 0);
-
- if (w & WL_LATCH_SET)
- {
- ResetLatch(MyLatch);
- /*
- * Force a return, so interrupts can be processed when not
- * (possibly) underneath a ssl library.
- */
- errno = EINTR;
- return -1;
- }
- else if (w & WL_SOCKET_WRITEABLE)
- {
- goto wloop;
- }
-
- /*
- * Restore errno, clobbered by WaitLatchOrSocket, so the caller can
- * react properly.
- */
- errno = save_errno;
- }
-
return n;
}
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 09dea4bbe16..34efac48651 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -185,7 +185,8 @@ pq_init(void)
/*
* In backends (as soon as forked) we operate the underlying socket in
* nonblocking mode and use latches to implement blocking semantics if
- * needed. That allows us to provide safely interruptible reads.
+ * needed. That allows us to provide safely interruptible reads and
+ * writes.
*
* Use COMMERROR on failure, because ERROR would try to send the error to
* the client, which might require changing the mode again, leading to
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index ccd70218ae6..cf520f545d9 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -209,8 +209,8 @@ typedef struct Port
extern void be_tls_init(void);
extern int be_tls_open_server(Port *port);
extern void be_tls_close(Port *port);
-extern ssize_t be_tls_read(Port *port, void *ptr, size_t len);
-extern ssize_t be_tls_write(Port *port, void *ptr, size_t len);
+extern ssize_t be_tls_read(Port *port, void *ptr, size_t len, int *waitfor);
+extern ssize_t be_tls_write(Port *port, void *ptr, size_t len, int *waitfor);
#endif