ssl-write-retry-2.patch
text/x-patch
Filename: ssl-write-retry-2.patch
Type: text/x-patch
Part: 0
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 9e4807e..8dc0226 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2792,6 +2792,10 @@ freePGconn(PGconn *conn)
if (conn->gsslib)
free(conn->gsslib);
#endif
+#if defined(USE_SSL)
+ if (conn->sslOutBufPtr)
+ free(conn->sslOutBufPtr);
+#endif
/* Note that conn->Pfdebug is not ours to close or free */
if (conn->last_query)
free(conn->last_query);
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 17dde4a..d0c7812 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -781,6 +781,8 @@ pqSendSome(PGconn *conn, int len)
char *ptr = conn->outBuffer;
int remaining = conn->outCount;
int result = 0;
+ int sent = 0;
+ bool shiftOutBuffer = true;
if (conn->sock < 0)
{
@@ -789,23 +791,34 @@ pqSendSome(PGconn *conn, int len)
return -1;
}
+#ifdef USE_SSL
+ if (conn->sslRetryPos)
+ {
+ /*
+ * We have some leftovers from previous SSL write attempts, these need
+ * to be sent before the regular output buffer.
+ */
+ len = remaining = conn->sslRetrySize;
+ ptr = conn->sslRetryPos;
+ shiftOutBuffer = false;
+ }
+#endif
+
/* while there's still data to send */
while (len > 0)
{
- int sent;
char sebuf[256];
+ int write_size = len;
-#ifndef WIN32
- sent = pqsecure_write(conn, ptr, len);
-#else
-
+#ifdef WIN32
/*
* Windows can fail on large sends, per KB article Q201213. The
* failure-point appears to be different in different versions of
* Windows, but 64k should always be safe.
*/
- sent = pqsecure_write(conn, ptr, Min(len, 65536));
+ write_size = Min(len, 65536);
#endif
+ sent = pqsecure_write(conn, ptr, write_size);
if (sent < 0)
{
@@ -857,6 +870,38 @@ pqSendSome(PGconn *conn, int len)
return -1;
}
}
+#ifdef USE_SSL
+ else if (sent == 0 && conn->ssl && !conn->sslRetryPos)
+ {
+ /*
+ * pqsecure_write indicates that a SSL write needs to be retried.
+ * With non-blocking connections we need to ensure that the buffer
+ * passed to the SSL_write() retry is the the exact same buffer as
+ * in previous write -- see SSL_write(3SSL) for more on this. For
+ * this we need to save the the original buffer and allocate a new
+ * buffer for outgoing data.
+ */
+ conn->sslOutBufPtr = conn->outBuffer;
+ conn->sslRetryPos = ptr;
+ conn->sslRetrySize = write_size;
+
+ /*
+ * Allocate a new output buffer and prepare to move the remainder
+ * of the original outBuffer there.
+ */
+ remaining -= write_size;
+ ptr += write_size;
+
+ conn->outBufSize = Max(16 * 1024, remaining);
+ conn->outBuffer = malloc(conn->outBufSize);
+ if (conn->outBuffer == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ "cannot allocate memory for output buffer\n");
+ return -1;
+ }
+ }
+#endif
else
{
ptr += sent;
@@ -903,10 +948,36 @@ pqSendSome(PGconn *conn, int len)
}
}
- /* shift the remaining contents of the buffer */
- if (remaining > 0)
- memmove(conn->outBuffer, ptr, remaining);
- conn->outCount = remaining;
+#ifdef USE_SSL
+ if (conn->sslRetryPos && sent > 0)
+ {
+ /*
+ * A SSL write was successfully retried, free the retry buffer and
+ * switch to the regular outBuffer. Note that we expect that the entire
+ * buffer was written since SSL partial writes ought to be disabled.
+ */
+ free(conn->sslOutBufPtr);
+ conn->sslRetryPos = NULL;
+ conn->sslOutBufPtr = NULL;
+ conn->sslRetrySize = 0;
+
+ /*
+ * Indicate that a retry is needed if we still have some data left in
+ * output buffers. We are in non-blocking mode, so this shouldn't
+ * be a surprise.
+ */
+ if (conn->outCount > 0)
+ result = 1;
+ }
+#endif
+
+ /* Shift the remaining contents of the buffer. */
+ if (shiftOutBuffer)
+ {
+ if (remaining > 0)
+ memmove(conn->outBuffer, ptr, remaining);
+ conn->outCount = remaining;
+ }
return result;
}
@@ -924,7 +995,11 @@ pqFlush(PGconn *conn)
if (conn->Pfdebug)
fflush(conn->Pfdebug);
+#ifdef USE_SSL
+ if (conn->outCount > 0 || conn->sslRetrySize > 0)
+#else
if (conn->outCount > 0)
+#endif
return pqSendSome(conn, conn->outCount);
return 0;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index d56ef5d..da1f2da 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -407,6 +407,10 @@ struct pg_conn
X509 *peer; /* X509 cert of server */
char peer_dn[256 + 1]; /* peer distinguished name */
char peer_cn[SM_USER + 1]; /* peer common name */
+
+ char *sslRetryPos; /* last buffer address passed SSL write */
+ char *sslOutBufPtr; /* pointer to the original outBuffer */
+ int sslRetrySize; /* last buffer length passed SSL write */
#ifdef USE_SSL_ENGINE
ENGINE *engine; /* SSL engine, if any */
#else