Обсуждение: libpq ssl -> clear fallback looses error messages

Поиск
Список
Период
Сортировка

libpq ssl -> clear fallback looses error messages

От
Magnus Hagander
Дата:
I noticed while working on general fixes for the certificate handling
that if we have a connection being attempted with sslmode=prefer (which
happens to be our default), we will loose error messages.

Basically, if we fail the SSL connection, we will throw away the error
message and try a cleartext connection. Now, if the server is configured
to require SSL (using hostssl), you will get an error message that says
"there is no pg_hba, etc, SSL off". Which is totally misleading, because
I *tried* to connect with SSL, but failed.

If I set sslmode=require, the error message is properly reported.

AFAIK we don't actually have a way to pass back an intermediate result
here, but we really need to report this error *somehow*.

It may even be to the point that if we connect and get a client side SSL
error, we should just report it and abort, and only retry if the error
is actually a server error saying there is no pg_hba for SSL here?

(or I'm missing something obvious :-P)

//Magnus


Re: libpq ssl -> clear fallback looses error messages

От
Tom Lane
Дата:
Magnus Hagander <magnus@hagander.net> writes:
> I noticed while working on general fixes for the certificate handling
> that if we have a connection being attempted with sslmode=prefer (which
> happens to be our default), we will loose error messages.

Yeah, this came up awhile ago.  I don't see any easy solution that
isn't just moving the bad cases around ... although maybe moving them
away from the default/common cases could be a good thing anyway.

> Basically, if we fail the SSL connection, we will throw away the error
> message and try a cleartext connection.

Maybe the answer is to not throw away the first error message?  But
presenting both messages could be confusing too.
        regards, tom lane


Re: libpq ssl -> clear fallback looses error messages

От
Magnus Hagander
Дата:
Tom Lane wrote:
> Magnus Hagander <magnus@hagander.net> writes:
>> I noticed while working on general fixes for the certificate handling
>> that if we have a connection being attempted with sslmode=prefer (which
>> happens to be our default), we will loose error messages.
> 
> Yeah, this came up awhile ago.  I don't see any easy solution that
> isn't just moving the bad cases around ... although maybe moving them
> away from the default/common cases could be a good thing anyway.

I think it would.

>> Basically, if we fail the SSL connection, we will throw away the error
>> message and try a cleartext connection.
> 
> Maybe the answer is to not throw away the first error message?  But
> presenting both messages could be confusing too.

Do we have the infrastructure to report more than one error? I thought
we didn't...

//Magnus


Re: libpq ssl -> clear fallback looses error messages

От
Tom Lane
Дата:
Magnus Hagander <magnus@hagander.net> writes:
> Tom Lane wrote:
>> Maybe the answer is to not throw away the first error message?  But
>> presenting both messages could be confusing too.

> Do we have the infrastructure to report more than one error? I thought
> we didn't...

I was thinking of merging the reports into a single PGresult.  The
tricky part is to figure out how to present it, and also when to throw
away one of the two reports --- if one is obviously bogus you don't want
to distract the user with it.
        regards, tom lane


Re: libpq ssl -> clear fallback looses error messages

От
Magnus Hagander
Дата:
Tom Lane wrote:
> Magnus Hagander <magnus@hagander.net> writes:
>> Tom Lane wrote:
>>> Maybe the answer is to not throw away the first error message?  But
>>> presenting both messages could be confusing too.
> 
>> Do we have the infrastructure to report more than one error? I thought
>> we didn't...
> 
> I was thinking of merging the reports into a single PGresult.  The
> tricky part is to figure out how to present it, and also when to throw
> away one of the two reports --- if one is obviously bogus you don't want
> to distract the user with it.

Um, PGresult? These errors occur on connection, so it needs to go in PGconn.

I guess the easy way would be to just append the error reports with a \n
between them. I think it would be good to keep both error messages in
*all* cases. If the second connection succeeds, you will not get the
error condition anyway, and thus not look at the error message from the
first one.

//Magnus



Re: libpq ssl -> clear fallback looses error messages

От
Magnus Hagander
Дата:
Magnus Hagander wrote:
> Tom Lane wrote:
>> Magnus Hagander <magnus@hagander.net> writes:
>>> Tom Lane wrote:
>>>> Maybe the answer is to not throw away the first error message?  But
>>>> presenting both messages could be confusing too.
>>> Do we have the infrastructure to report more than one error? I thought
>>> we didn't...
>> I was thinking of merging the reports into a single PGresult.  The
>> tricky part is to figure out how to present it, and also when to throw
>> away one of the two reports --- if one is obviously bogus you don't want
>> to distract the user with it.
>
> Um, PGresult? These errors occur on connection, so it needs to go in PGconn.
>
> I guess the easy way would be to just append the error reports with a \n
> between them. I think it would be good to keep both error messages in
> *all* cases. If the second connection succeeds, you will not get the
> error condition anyway, and thus not look at the error message from the
> first one.

Here's an ugly attempt towards this. Though I'm unsure if we can change
the "const" on the PQerrorMessage parameter without messing with library
versions and such?

Another option could be to change all the calls that set the error
message to *append* to the error message instead. Thoughts on that?

//Magnus

*** a/src/interfaces/libpq/fe-connect.c
--- b/src/interfaces/libpq/fe-connect.c
***************
*** 243,248 **** static char *pwdfMatchesString(char *buf, char *token);
--- 243,249 ----
  static char *PasswordFromFile(char *hostname, char *port, char *dbname,
                   char *username);
  static void default_threadlock(int acquire);
+ static void save_error_messages(PGconn *conn);


  /* global variable because fe-auth.c needs to access it */
***************
*** 955,960 **** connectDBComplete(PGconn *conn)
--- 956,975 ----
      }
  }

+ /*
+  * save_error_message - save the current error message in a simple
+  * one element "stack"
+  */
+ static void
+ save_error_messages(PGconn *conn)
+ {
+     if (conn->errorMessage.len > 0)
+     {
+         initPQExpBuffer(&conn->errorMessageSaved);
+         appendPQExpBufferStr(&conn->errorMessageSaved, conn->errorMessage.data);
+     }
+ }
+
  /* ----------------
   *        PQconnectPoll
   *
***************
*** 1447,1452 **** keep_going:                        /* We will come back to here until there is
--- 1462,1469 ----
                          closesocket(conn->sock);
                          conn->sock = -1;
                          conn->status = CONNECTION_NEEDED;
+                         save_error_messages(conn);
+
                          goto keep_going;
                      }
                  }
***************
*** 1627,1632 **** keep_going:                        /* We will come back to here until there is
--- 1644,1650 ----
                          closesocket(conn->sock);
                          conn->sock = -1;
                          conn->status = CONNECTION_NEEDED;
+                         save_error_messages(conn);
                          goto keep_going;
                      }

***************
*** 1646,1651 **** keep_going:                        /* We will come back to here until there is
--- 1664,1670 ----
                          closesocket(conn->sock);
                          conn->sock = -1;
                          conn->status = CONNECTION_NEEDED;
+                         save_error_messages(conn);
                          goto keep_going;
                      }
  #endif
***************
*** 1947,1952 **** makeEmptyPGconn(void)
--- 1966,1972 ----
      conn->outBufSize = 16 * 1024;
      conn->outBuffer = (char *) malloc(conn->outBufSize);
      initPQExpBuffer(&conn->errorMessage);
+     conn->errorMessageSaved.data = NULL;
      initPQExpBuffer(&conn->workBuffer);

      if (conn->inBuffer == NULL ||
***************
*** 2023,2028 **** freePGconn(PGconn *conn)
--- 2043,2050 ----
      if (conn->outBuffer)
          free(conn->outBuffer);
      termPQExpBuffer(&conn->errorMessage);
+     if (conn->errorMessageSaved.data)
+         termPQExpBuffer(&conn->errorMessageSaved);
      termPQExpBuffer(&conn->workBuffer);

      free(conn);
***************
*** 3538,3549 **** PQserverVersion(const PGconn *conn)
  }

  char *
! PQerrorMessage(const PGconn *conn)
  {
      if (!conn)
          return libpq_gettext("connection pointer is NULL\n");

!     return conn->errorMessage.data;
  }

  int
--- 3560,3584 ----
  }

  char *
! PQerrorMessage(PGconn *conn)
  {
      if (!conn)
          return libpq_gettext("connection pointer is NULL\n");

!     if (conn->errorMessageSaved.data)
!     {
!         /*
!          * Merge the two error message strings together.
!          * Stick the result in errorMessage, and make sure to
!          * get rid of the saved one so we don't keep adding everything.
!          */
!         appendPQExpBufferStr(&conn->errorMessageSaved, conn->errorMessage.data);
!
!         resetPQExpBuffer(&conn->errorMessage);
!         return conn->errorMessageSaved.data;
!     }
!     else
!         return conn->errorMessage.data;
  }

  int
*** a/src/interfaces/libpq/libpq-fe.h
--- b/src/interfaces/libpq/libpq-fe.h
***************
*** 287,293 **** extern const char *PQparameterStatus(const PGconn *conn,
                    const char *paramName);
  extern int    PQprotocolVersion(const PGconn *conn);
  extern int    PQserverVersion(const PGconn *conn);
! extern char *PQerrorMessage(const PGconn *conn);
  extern int    PQsocket(const PGconn *conn);
  extern int    PQbackendPID(const PGconn *conn);
  extern int    PQconnectionNeedsPassword(const PGconn *conn);
--- 287,293 ----
                    const char *paramName);
  extern int    PQprotocolVersion(const PGconn *conn);
  extern int    PQserverVersion(const PGconn *conn);
! extern char *PQerrorMessage(PGconn *conn);
  extern int    PQsocket(const PGconn *conn);
  extern int    PQbackendPID(const PGconn *conn);
  extern int    PQconnectionNeedsPassword(const PGconn *conn);
*** a/src/interfaces/libpq/libpq-int.h
--- b/src/interfaces/libpq/libpq-int.h
***************
*** 402,407 **** struct pg_conn
--- 402,408 ----

      /* Buffer for current error message */
      PQExpBufferData errorMessage;        /* expansible string */
+     PQExpBufferData errorMessageSaved;  /* saved in some retried operations */

      /* Buffer for receiving various parts of messages */
      PQExpBufferData workBuffer; /* expansible string */

Re: libpq ssl -> clear fallback looses error messages

От
Tom Lane
Дата:
Magnus Hagander <magnus@hagander.net> writes:
> Here's an ugly attempt towards this. Though I'm unsure if we can change
> the "const" on the PQerrorMessage parameter without messing with library
> versions and such?

That's a bad idea in any case --- PQerrorMessage shouldn't be changing
the state of anything.  The merging needs to happen earlier.

> Another option could be to change all the calls that set the error
> message to *append* to the error message instead. Thoughts on that?

That might work ... but we'd probably have to add some "clear the
message" calls in places.  It might be worth trying to restrict this
convention to the connection-startup code.
        regards, tom lane


Re: libpq ssl -> clear fallback looses error messages

От
Magnus Hagander
Дата:
Tom Lane wrote:
> Magnus Hagander <magnus@hagander.net> writes:
>> Here's an ugly attempt towards this. Though I'm unsure if we can change
>> the "const" on the PQerrorMessage parameter without messing with library
>> versions and such?
>
> That's a bad idea in any case --- PQerrorMessage shouldn't be changing
> the state of anything.  The merging needs to happen earlier.

That was my general thought, but since I'd written it, I just threw it
out there..


>> Another option could be to change all the calls that set the error
>> message to *append* to the error message instead. Thoughts on that?
>
> That might work ... but we'd probably have to add some "clear the
> message" calls in places.  It might be worth trying to restrict this
> convention to the connection-startup code.

How about something like this?

//Magnus
*** a/src/interfaces/libpq/fe-connect.c
--- b/src/interfaces/libpq/fe-connect.c
***************
*** 699,705 **** connectNoDelay(PGconn *conn)
      {
          char        sebuf[256];

!         printfPQExpBuffer(&conn->errorMessage,
              libpq_gettext("could not set socket to TCP no delay mode: %s\n"),
                            SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
          return 0;
--- 699,705 ----
      {
          char        sebuf[256];

!         appendPQExpBuffer(&conn->errorMessage,
              libpq_gettext("could not set socket to TCP no delay mode: %s\n"),
                            SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
          return 0;
***************
*** 729,735 **** connectFailureMessage(PGconn *conn, int errorno)
                             NULL, 0,
                             service, sizeof(service),
                             NI_NUMERICSERV);
!         printfPQExpBuffer(&conn->errorMessage,
                            libpq_gettext("could not connect to server: %s\n"
                              "\tIs the server running locally and accepting\n"
                              "\tconnections on Unix domain socket \"%s\"?\n"),
--- 729,735 ----
                             NULL, 0,
                             service, sizeof(service),
                             NI_NUMERICSERV);
!         appendPQExpBuffer(&conn->errorMessage,
                            libpq_gettext("could not connect to server: %s\n"
                              "\tIs the server running locally and accepting\n"
                              "\tconnections on Unix domain socket \"%s\"?\n"),
***************
*** 739,745 **** connectFailureMessage(PGconn *conn, int errorno)
      else
  #endif   /* HAVE_UNIX_SOCKETS */
      {
!         printfPQExpBuffer(&conn->errorMessage,
                            libpq_gettext("could not connect to server: %s\n"
                       "\tIs the server running on host \"%s\" and accepting\n"
                                          "\tTCP/IP connections on port %s?\n"),
--- 739,745 ----
      else
  #endif   /* HAVE_UNIX_SOCKETS */
      {
!         appendPQExpBuffer(&conn->errorMessage,
                            libpq_gettext("could not connect to server: %s\n"
                       "\tIs the server running on host \"%s\" and accepting\n"
                                          "\tTCP/IP connections on port %s?\n"),
***************
*** 829,839 **** connectDBStart(PGconn *conn)
      if (ret || !addrs)
      {
          if (node)
!             printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
                                node, gai_strerror(ret));
          else
!             printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
                                portstr, gai_strerror(ret));
          if (addrs)
--- 829,839 ----
      if (ret || !addrs)
      {
          if (node)
!             appendPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
                                node, gai_strerror(ret));
          else
!             appendPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
                                portstr, gai_strerror(ret));
          if (addrs)
***************
*** 924,929 **** connectDBComplete(PGconn *conn)
--- 924,931 ----
          switch (flag)
          {
              case PGRES_POLLING_OK:
+                 /* Reset stored error messages since we now have a working connection */
+                 resetPQExpBuffer(&conn->errorMessage);
                  return 1;        /* success! */

              case PGRES_POLLING_READING:
***************
*** 1033,1039 **** PQconnectPoll(PGconn *conn)
              break;

          default:
!             printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext(
                                              "invalid connection state, "
                                   "probably indicative of memory corruption\n"
--- 1035,1041 ----
              break;

          default:
!             appendPQExpBuffer(&conn->errorMessage,
                                libpq_gettext(
                                              "invalid connection state, "
                                   "probably indicative of memory corruption\n"
***************
*** 1077,1083 **** keep_going:                        /* We will come back to here until there is
                              conn->addr_cur = addr_cur->ai_next;
                              continue;
                          }
!                         printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("could not create socket: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                          break;
--- 1079,1085 ----
                              conn->addr_cur = addr_cur->ai_next;
                              continue;
                          }
!                         appendPQExpBuffer(&conn->errorMessage,
                                libpq_gettext("could not create socket: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                          break;
***************
*** 1100,1106 **** keep_going:                        /* We will come back to here until there is
                      }
                      if (!pg_set_noblock(conn->sock))
                      {
!                         printfPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("could not set socket to non-blocking mode: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                          closesocket(conn->sock);
--- 1102,1108 ----
                      }
                      if (!pg_set_noblock(conn->sock))
                      {
!                         appendPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("could not set socket to non-blocking mode: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                          closesocket(conn->sock);
***************
*** 1112,1118 **** keep_going:                        /* We will come back to here until there is
  #ifdef F_SETFD
                      if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1)
                      {
!                         printfPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("could not set socket to close-on-exec mode: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                          closesocket(conn->sock);
--- 1114,1120 ----
  #ifdef F_SETFD
                      if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1)
                      {
!                         appendPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("could not set socket to close-on-exec mode: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                          closesocket(conn->sock);
***************
*** 1199,1205 **** keep_going:                        /* We will come back to here until there is
                  if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR,
                                 (char *) &optval, &optlen) == -1)
                  {
!                     printfPQExpBuffer(&conn->errorMessage,
                      libpq_gettext("could not get socket error status: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                      goto error_return;
--- 1201,1207 ----
                  if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR,
                                 (char *) &optval, &optlen) == -1)
                  {
!                     appendPQExpBuffer(&conn->errorMessage,
                      libpq_gettext("could not get socket error status: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                      goto error_return;
***************
*** 1237,1243 **** keep_going:                        /* We will come back to here until there is
                                  (struct sockaddr *) & conn->laddr.addr,
                                  &conn->laddr.salen) < 0)
                  {
!                     printfPQExpBuffer(&conn->errorMessage,
                                        libpq_gettext("could not get client address from socket: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                      goto error_return;
--- 1239,1245 ----
                                  (struct sockaddr *) & conn->laddr.addr,
                                  &conn->laddr.salen) < 0)
                  {
!                     appendPQExpBuffer(&conn->errorMessage,
                                        libpq_gettext("could not get client address from socket: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                      goto error_return;
***************
*** 1281,1287 **** keep_going:                        /* We will come back to here until there is
                      pv = htonl(NEGOTIATE_SSL_CODE);
                      if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK)
                      {
!                         printfPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("could not send SSL negotiation packet: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                          goto error_return;
--- 1283,1289 ----
                      pv = htonl(NEGOTIATE_SSL_CODE);
                      if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK)
                      {
!                         appendPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("could not send SSL negotiation packet: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                          goto error_return;
***************
*** 1303,1308 **** keep_going:                        /* We will come back to here until there is
--- 1305,1311 ----
                                                          EnvironmentOptions);
                  if (!startpacket)
                  {
+                     /* will not appendbuffer here, since it's likely to also run out of memory */
                      printfPQExpBuffer(&conn->errorMessage,
                                        libpq_gettext("out of memory\n"));
                      goto error_return;
***************
*** 1316,1322 **** keep_going:                        /* We will come back to here until there is
                   */
                  if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK)
                  {
!                     printfPQExpBuffer(&conn->errorMessage,
                          libpq_gettext("could not send startup packet: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                      free(startpacket);
--- 1319,1325 ----
                   */
                  if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK)
                  {
!                     appendPQExpBuffer(&conn->errorMessage,
                          libpq_gettext("could not send startup packet: %s\n"),
                              SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                      free(startpacket);
***************
*** 1381,1387 **** keep_going:                        /* We will come back to here until there is
                          if (conn->sslmode[0] == 'r')    /* "require" */
                          {
                              /* Require SSL, but server does not want it */
!                             printfPQExpBuffer(&conn->errorMessage,
                                                libpq_gettext("server does not support SSL, but SSL was required\n"));
                              goto error_return;
                          }
--- 1384,1390 ----
                          if (conn->sslmode[0] == 'r')    /* "require" */
                          {
                              /* Require SSL, but server does not want it */
!                             appendPQExpBuffer(&conn->errorMessage,
                                                libpq_gettext("server does not support SSL, but SSL was required\n"));
                              goto error_return;
                          }
***************
*** 1398,1404 **** keep_going:                        /* We will come back to here until there is
                          if (conn->sslmode[0] == 'r')    /* "require" */
                          {
                              /* Require SSL, but server is too old */
!                             printfPQExpBuffer(&conn->errorMessage,
                                                libpq_gettext("server does not support SSL, but SSL was required\n"));
                              goto error_return;
                          }
--- 1401,1407 ----
                          if (conn->sslmode[0] == 'r')    /* "require" */
                          {
                              /* Require SSL, but server is too old */
!                             appendPQExpBuffer(&conn->errorMessage,
                                                libpq_gettext("server does not support SSL, but SSL was required\n"));
                              goto error_return;
                          }
***************
*** 1414,1420 **** keep_going:                        /* We will come back to here until there is
                      }
                      else
                      {
!                         printfPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("received invalid response to SSL negotiation: %c\n"),
                                            SSLok);
                          goto error_return;
--- 1417,1423 ----
                      }
                      else
                      {
!                         appendPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("received invalid response to SSL negotiation: %c\n"),
                                            SSLok);
                          goto error_return;
***************
*** 1489,1495 **** keep_going:                        /* We will come back to here until there is
                   */
                  if (!(beresp == 'R' || beresp == 'E'))
                  {
!                     printfPQExpBuffer(&conn->errorMessage,
                                        libpq_gettext(
                                        "expected authentication request from "
                                                  "server, but received %c\n"),
--- 1492,1498 ----
                   */
                  if (!(beresp == 'R' || beresp == 'E'))
                  {
!                     appendPQExpBuffer(&conn->errorMessage,
                                        libpq_gettext(
                                        "expected authentication request from "
                                                  "server, but received %c\n"),
***************
*** 1522,1528 **** keep_going:                        /* We will come back to here until there is
                   */
                  if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
                  {
!                     printfPQExpBuffer(&conn->errorMessage,
                                        libpq_gettext(
                                        "expected authentication request from "
                                                  "server, but received %c\n"),
--- 1525,1531 ----
                   */
                  if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
                  {
!                     appendPQExpBuffer(&conn->errorMessage,
                                        libpq_gettext(
                                        "expected authentication request from "
                                                  "server, but received %c\n"),
***************
*** 1534,1540 **** keep_going:                        /* We will come back to here until there is
                  {
                      /* Handle error from a pre-3.0 server */
                      conn->inCursor = conn->inStart + 1; /* reread data */
!                     if (pqGets(&conn->errorMessage, conn))
                      {
                          /* We'll come back when there is more data */
                          return PGRES_POLLING_READING;
--- 1537,1543 ----
                  {
                      /* Handle error from a pre-3.0 server */
                      conn->inCursor = conn->inStart + 1; /* reread data */
!                     if (pqGets_append(&conn->errorMessage, conn))
                      {
                          /* We'll come back when there is more data */
                          return PGRES_POLLING_READING;
***************
*** 1601,1607 **** keep_going:                        /* We will come back to here until there is
                      }
                      else
                      {
!                         if (pqGets(&conn->errorMessage, conn))
                          {
                              /* We'll come back when there is more data */
                              return PGRES_POLLING_READING;
--- 1604,1610 ----
                      }
                      else
                      {
!                         if (pqGets_append(&conn->errorMessage, conn))
                          {
                              /* We'll come back when there is more data */
                              return PGRES_POLLING_READING;
***************
*** 1788,1794 **** keep_going:                        /* We will come back to here until there is
                  if (res)
                  {
                      if (res->resultStatus != PGRES_FATAL_ERROR)
!                         printfPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("unexpected message from server during startup\n"));

                      /*
--- 1791,1797 ----
                  if (res)
                  {
                      if (res->resultStatus != PGRES_FATAL_ERROR)
!                         appendPQExpBuffer(&conn->errorMessage,
                                            libpq_gettext("unexpected message from server during startup\n"));

                      /*
***************
*** 1855,1861 **** keep_going:                        /* We will come back to here until there is
              return PGRES_POLLING_OK;

          default:
!             printfPQExpBuffer(&conn->errorMessage,
                                libpq_gettext(
                                              "invalid connection state %c, "
                                   "probably indicative of memory corruption\n"
--- 1858,1864 ----
              return PGRES_POLLING_OK;

          default:
!             appendPQExpBuffer(&conn->errorMessage,
                                libpq_gettext(
                                              "invalid connection state %c, "
                                   "probably indicative of memory corruption\n"
*** a/src/interfaces/libpq/fe-misc.c
--- b/src/interfaces/libpq/fe-misc.c
***************
*** 106,119 **** pqPutc(char c, PGconn *conn)


  /*
!  * pqGets:
   * get a null-terminated string from the connection,
   * and store it in an expansible PQExpBuffer.
   * If we run out of memory, all of the string is still read,
   * but the excess characters are silently discarded.
   */
! int
! pqGets(PQExpBuffer buf, PGconn *conn)
  {
      /* Copy conn data to locals for faster search loop */
      char       *inBuffer = conn->inBuffer;
--- 106,119 ----


  /*
!  * pqGets[_append]:
   * get a null-terminated string from the connection,
   * and store it in an expansible PQExpBuffer.
   * If we run out of memory, all of the string is still read,
   * but the excess characters are silently discarded.
   */
! static int
! pqGets_internal(PQExpBuffer buf, PGconn *conn, bool resetbuffer)
  {
      /* Copy conn data to locals for faster search loop */
      char       *inBuffer = conn->inBuffer;
***************
*** 129,135 **** pqGets(PQExpBuffer buf, PGconn *conn)

      slen = inCursor - conn->inCursor;

!     resetPQExpBuffer(buf);
      appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen);

      conn->inCursor = ++inCursor;
--- 129,137 ----

      slen = inCursor - conn->inCursor;

!     if (resetbuffer)
!         resetPQExpBuffer(buf);
!
      appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen);

      conn->inCursor = ++inCursor;
***************
*** 141,146 **** pqGets(PQExpBuffer buf, PGconn *conn)
--- 143,160 ----
      return 0;
  }

+ int
+ pqGets(PQExpBuffer buf, PGconn *conn)
+ {
+     return pqGets_internal(buf, conn, true);
+ }
+
+ int
+ pqGets_append(PQExpBuffer buf, PGconn *conn)
+ {
+     return pqGets_internal(buf, conn, false);
+ }
+

  /*
   * pqPuts: write a null-terminated string to the current message
*** a/src/interfaces/libpq/fe-protocol3.c
--- b/src/interfaces/libpq/fe-protocol3.c
***************
*** 853,859 **** pqGetErrorNotice3(PGconn *conn, bool isError)
              goto fail;
          pqClearAsyncResult(conn);
          conn->result = res;
-         resetPQExpBuffer(&conn->errorMessage);
          appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
      }
      else
--- 853,858 ----
*** a/src/interfaces/libpq/libpq-int.h
--- b/src/interfaces/libpq/libpq-int.h
***************
*** 519,524 **** extern int    pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn);
--- 519,525 ----
  extern int    pqGetc(char *result, PGconn *conn);
  extern int    pqPutc(char c, PGconn *conn);
  extern int    pqGets(PQExpBuffer buf, PGconn *conn);
+ extern int    pqGets_append(PQExpBuffer buf, PGconn *conn);
  extern int    pqPuts(const char *s, PGconn *conn);
  extern int    pqGetnchar(char *s, size_t len, PGconn *conn);
  extern int    pqPutnchar(const char *s, size_t len, PGconn *conn);