/* * GTMPQconninfoParse * * Parse a string like PQconnectGTM() would do and return the * resulting connection options array. NULL is returned on failure. * The result contains only options specified directly in the string, * not any possible default values. * * If errmsg isn't NULL, *errmsg is set to NULL on success, or a malloc'd * string on failure (use PQfreemem to free it). In out-of-memory conditions * both *errmsg and the result could be NULL. * * NOTE: the returned array is dynamically allocated and should * be freed when no longer needed via GTMPQconninfoFree(). */ GTMPQconninfoOption * GTMPQconninfoParse(const char *conninfo, char **errmsg) { PQExpBufferData errorBuf; GTMPQconninfoOption *connOptions; if (errmsg) *errmsg = NULL; /* default */ initGTMPQExpBuffer(&errorBuf); if (PQExpBufferDataBroken(errorBuf)) return NULL; /* out of memory already :-( */ connOptions = conninfo_parse(conninfo, &errorBuf, false); if (connOptions == NULL && errmsg) *errmsg = errorBuf.data; else termGTMPQExpBuffer(&errorBuf); return connOptions; }
/* * Attempt to read an Error or Notice response message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'E' or 'N' message type has already been consumed. * Exit: returns 0 if successfully consumed message. * returns EOF if not enough data. */ static int pqGetErrorNotice2(PGconn *conn, bool isError) { PGresult *res = NULL; PQExpBufferData workBuf; char *startp; char *splitp; /* * Since the message might be pretty long, we create a temporary * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended * for stuff that is expected to be short. */ initPQExpBuffer(&workBuf); if (pqGets(&workBuf, conn)) goto failure; /* * Make a PGresult to hold the message. We temporarily lie about the * result status, so that PQmakeEmptyPGresult doesn't uselessly copy * conn->errorMessage. * * NB: This allocation can fail, if you run out of memory. The rest of the * function handles that gracefully, and we still try to set the error * message as the connection's error message. */ res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); if (res) { res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; res->errMsg = pqResultStrdup(res, workBuf.data); } /* * Break the message into fields. We can't do very much here, but we can * split the severity code off, and remove trailing newlines. Also, we use * the heuristic that the primary message extends only to the first * newline --- anything after that is detail message. (In some cases it'd * be better classed as hint, but we can hardly be expected to guess that * here.) */ while (workBuf.len > 0 && workBuf.data[workBuf.len - 1] == '\n') workBuf.data[--workBuf.len] = '\0'; splitp = strstr(workBuf.data, ": "); if (splitp) { /* what comes before the colon is severity */ *splitp = '\0'; pqSaveMessageField(res, PG_DIAG_SEVERITY, workBuf.data); startp = splitp + 3; } else { /* can't find a colon? oh well... */ startp = workBuf.data; } splitp = strchr(startp, '\n'); if (splitp) { /* what comes before the newline is primary message */ *splitp++ = '\0'; pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); /* the rest is detail; strip any leading whitespace */ while (*splitp && isspace((unsigned char) *splitp)) splitp++; pqSaveMessageField(res, PG_DIAG_MESSAGE_DETAIL, splitp); } else { /* single-line message, so all primary */ pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); } /* * Either save error as current async result, or just emit the notice. * Also, if it's an error and we were in a transaction block, assume the * server has now gone to error-in-transaction state. */ if (isError) { pqClearAsyncResult(conn); conn->result = res; resetPQExpBuffer(&conn->errorMessage); if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg) appendPQExpBufferStr(&conn->errorMessage, res->errMsg); else printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); if (conn->xactStatus == PQTRANS_INTRANS) conn->xactStatus = PQTRANS_INERROR; } else { if (res) { if (res->noticeHooks.noticeRec != NULL) (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); PQclear(res); } } termPQExpBuffer(&workBuf); return 0; failure: if (res) PQclear(res); termPQExpBuffer(&workBuf); return EOF; }
/* * Create a recovery.conf file in memory using a PQExpBuffer */ static void GenerateRecoveryConf(PGconn *conn) { PQconninfoOption *connOptions; PQconninfoOption *option; PQExpBufferData conninfo_buf; char *escaped; recoveryconfcontents = createPQExpBuffer(); if (!recoveryconfcontents) { fprintf(stderr, _("%s: out of memory\n"), progname); disconnect_and_exit(1); } connOptions = PQconninfo(conn); if (connOptions == NULL) { fprintf(stderr, _("%s: out of memory\n"), progname); disconnect_and_exit(1); } appendPQExpBufferStr(recoveryconfcontents, "standby_mode = 'on'\n"); initPQExpBuffer(&conninfo_buf); for (option = connOptions; option && option->keyword; option++) { /* * Do not emit this setting if: - the setting is "replication", * "dbname" or "fallback_application_name", since these would be * overridden by the libpqwalreceiver module anyway. - not set or * empty. */ if (strcmp(option->keyword, "replication") == 0 || strcmp(option->keyword, "dbname") == 0 || strcmp(option->keyword, "fallback_application_name") == 0 || (option->val == NULL) || (option->val != NULL && option->val[0] == '\0')) continue; /* Separate key-value pairs with spaces */ if (conninfo_buf.len != 0) appendPQExpBufferStr(&conninfo_buf, " "); /* * Write "keyword=value" pieces, the value string is escaped and/or * quoted if necessary. */ escaped = escapeConnectionParameter(option->val); appendPQExpBuffer(&conninfo_buf, "%s=%s", option->keyword, escaped); free(escaped); } /* * Escape the connection string, so that it can be put in the config file. * Note that this is different from the escaping of individual connection * options above! */ escaped = escape_quotes(conninfo_buf.data); appendPQExpBuffer(recoveryconfcontents, "primary_conninfo = '%s'\n", escaped); free(escaped); if (PQExpBufferBroken(recoveryconfcontents) || PQExpBufferDataBroken(conninfo_buf)) { fprintf(stderr, _("%s: out of memory\n"), progname); disconnect_and_exit(1); } termPQExpBuffer(&conninfo_buf); PQconninfoFree(connOptions); }
/* * Initialize SASL authentication exchange. */ static int pg_SASL_init(PGconn *conn, int payloadlen) { char *initialresponse = NULL; int initialresponselen; bool done; bool success; const char *selected_mechanism; PQExpBufferData mechanism_buf; initPQExpBuffer(&mechanism_buf); if (conn->sasl_state) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("duplicate SASL authentication request\n")); goto error; } /* * Parse the list of SASL authentication mechanisms in the * AuthenticationSASL message, and select the best mechanism that we * support. (Only SCRAM-SHA-256 is supported at the moment.) */ selected_mechanism = NULL; for (;;) { if (pqGets(&mechanism_buf, conn)) { printfPQExpBuffer(&conn->errorMessage, "fe_sendauth: invalid authentication request from server: invalid list of authentication mechanisms\n"); goto error; } if (PQExpBufferDataBroken(mechanism_buf)) goto oom_error; /* An empty string indicates end of list */ if (mechanism_buf.data[0] == '\0') break; /* * If we have already selected a mechanism, just skip through the rest * of the list. */ if (selected_mechanism) continue; /* * Do we support this mechanism? */ if (strcmp(mechanism_buf.data, SCRAM_SHA_256_NAME) == 0) { char *password; conn->password_needed = true; password = conn->connhost[conn->whichhost].password; if (password == NULL) password = conn->pgpass; if (password == NULL || password[0] == '\0') { printfPQExpBuffer(&conn->errorMessage, PQnoPasswordSupplied); goto error; } conn->sasl_state = pg_fe_scram_init(conn->pguser, password); if (!conn->sasl_state) goto oom_error; selected_mechanism = SCRAM_SHA_256_NAME; } } if (!selected_mechanism) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("none of the server's SASL authentication mechanisms are supported\n")); goto error; } /* Get the mechanism-specific Initial Client Response, if any */ pg_fe_scram_exchange(conn->sasl_state, NULL, -1, &initialresponse, &initialresponselen, &done, &success, &conn->errorMessage); if (done && !success) goto error; /* * Build a SASLInitialResponse message, and send it. */ if (pqPutMsgStart('p', true, conn)) goto error; if (pqPuts(selected_mechanism, conn)) goto error; if (initialresponse) { if (pqPutInt(initialresponselen, 4, conn)) goto error; if (pqPutnchar(initialresponse, initialresponselen, conn)) goto error; } if (pqPutMsgEnd(conn)) goto error; if (pqFlush(conn)) goto error; termPQExpBuffer(&mechanism_buf); if (initialresponse) free(initialresponse); return STATUS_OK; error: termPQExpBuffer(&mechanism_buf); if (initialresponse) free(initialresponse); return STATUS_ERROR; oom_error: termPQExpBuffer(&mechanism_buf); if (initialresponse) free(initialresponse); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; }