/* * PQfn - Send a function call to the POSTGRES backend. * * See fe-exec.c for documentation. */ PGresult * pqFunctionCall2(PGconn *conn, Oid fnid, int *result_buf, int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs) { bool needInput = false; ExecStatusType status = PGRES_FATAL_ERROR; char id; int i; /* PQfn already validated connection state */ if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ pqPuts(" ", conn) < 0 || /* dummy string */ pqPutInt(fnid, 4, conn) != 0 || /* function id */ pqPutInt(nargs, 4, conn) != 0) /* # of args */ { pqHandleSendFailure(conn); return NULL; } for (i = 0; i < nargs; ++i) { /* len.int4 + contents */ if (pqPutInt(args[i].len, 4, conn)) { pqHandleSendFailure(conn); return NULL; } if (args[i].isint) { if (pqPutInt(args[i].u.integer, 4, conn)) { pqHandleSendFailure(conn); return NULL; } } else { if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) { pqHandleSendFailure(conn); return NULL; } } } if (pqPutMsgEnd(conn) < 0 || pqFlush(conn)) { pqHandleSendFailure(conn); return NULL; } for (;;) { if (needInput) { /* Wait for some data to arrive (or for the channel to close) */ if (pqWait(TRUE, FALSE, conn) || pqReadData(conn) < 0) break; } /* * Scan the message. If we run out of data, loop around to try again. */ conn->inCursor = conn->inStart; needInput = true; if (pqGetc(&id, conn)) continue; /* * We should see V or E response to the command, but might get N * and/or A notices first. We also need to swallow the final Z before * returning. */ switch (id) { case 'V': /* function result */ if (pqGetc(&id, conn)) continue; if (id == 'G') { /* function returned nonempty value */ if (pqGetInt(actual_result_len, 4, conn)) continue; if (result_is_int) { if (pqGetInt(result_buf, 4, conn)) continue; } else { if (pqGetnchar((char *) result_buf, *actual_result_len, conn)) continue; } if (pqGetc(&id, conn)) /* get the last '0' */ continue; } if (id == '0') { /* correctly finished function result message */ status = PGRES_COMMAND_OK; } else { /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); conn->inStart = conn->inCursor; return pqPrepareAsyncResult(conn); } break; case 'E': /* error return */ if (pqGetErrorNotice2(conn, true)) continue; status = PGRES_FATAL_ERROR; break; case 'A': /* notify message */ /* handle notify and go back to processing return values */ if (getNotify(conn)) continue; break; case 'N': /* notice */ /* handle notice and go back to processing return values */ if (pqGetErrorNotice2(conn, false)) continue; break; case 'Z': /* backend is ready for new query */ /* consume the message and exit */ conn->inStart = conn->inCursor; /* if we saved a result object (probably an error), use it */ if (conn->result) return pqPrepareAsyncResult(conn); return PQmakeEmptyPGresult(conn, status); default: /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); conn->inStart = conn->inCursor; return pqPrepareAsyncResult(conn); } /* Completed this message, keep going */ conn->inStart = conn->inCursor; needInput = false; } /* * We fall out of the loop only upon failing to read data. * conn->errorMessage has been set by pqWait or pqReadData. We want to * append it to any already-received error message. */ pqSaveErrorResult(conn); return pqPrepareAsyncResult(conn); }
/* * 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; }