in src/pgclient/src/interfaces/libpq/fe-connect.c [364:569]
static int parseServiceInfo(PQconninfoOption *options,
PQExpBuffer errorMessage);
static int parseServiceFile(const char *serviceFile,
const char *service,
PQconninfoOption *options,
PQExpBuffer errorMessage,
bool *group_found);
static char *pwdfMatchesString(char *buf, char *token);
static char *PasswordFromFile(char *hostname, char *port, char *dbname,
char *username);
static bool getPgPassFilename(char *pgpassfile);
static void dot_pg_pass_warning(PGconn *conn);
static void default_threadlock(int acquire);
// Defined in ClientSideCursorExecutor.h
struct _ClientSideCursorExecutor *createCscExecutor(PGconn *pConn);
struct _ClientSideCursorExecutor *releaseCscExecutor(struct _ClientSideCursorExecutor *pCscExecutor);
static int connect_using_proxy(int sock, PGconn* conn);
/* global variable because fe-auth.c needs to access it */
pgthreadlock_t pg_g_threadlock = default_threadlock;
/*
*
* Connects to a socket by proxy. Completes the SOCKS5 protocol.
*/
static
int connect_using_proxy(int sock, PGconn* conn)
{
RS_LOG_TRACE("FECNN", "%s", __func__);
char buffer[4096];
char *ptr = buffer;
int res;
int responseLen;
time_t now;
time_t finish_time;
/* Deprecated.
Generic connection/login timeout used in connectDBComplete and
PQconnectPoll should not be used for proxy timeout.
int timeout = conn->connect_timeout ? atoi(conn->connect_timeout) : -1;
*/
static const int PROXY_LOGIN_TIMEOUT_SECONDS = 5;
int timeout = PROXY_LOGIN_TIMEOUT_SECONDS;
// HTTP CONNECT method
const char* HTTP_CONNECT_REQUEST = "CONNECT ";
const size_t CONNECT_REQUEST_LEN = 8;
// Request header which contains the credentials to authenticate a user agent to a proxy server
const char* HTTP_PROXY_AUTH = "Proxy-Authorization: ";
const size_t HTTP_PROXY_AUTH_LEN = 21;
// Host: note that hostname and IP are different keys. Refer to class PGOClient.
// PGOUtils::IsIpAddress(m_settings.m_host) ? "hostaddr" : "host"
const char* destname = (conn->pghostaddr ? conn->pghostaddr :
(conn->pghost ? conn->pghost : "localhost"));
size_t destnamelen = strlen(destname);
// Port
const char* port = conn->pgport ? conn->pgport : "5439";
size_t portlen = strlen(port);
// Proxy authentication type
const char* authtype = conn->proxy_auth_type ? conn->proxy_auth_type : "Basic";
size_t authtypelen = strlen(authtype);
// Proxy credentials (base64 encoded string)
const char* credentials = conn->proxy_credentials ? conn->proxy_credentials : "";
size_t credentialslen = strlen(credentials);
if (!conn->proxy_host || !*conn->proxy_host)
{
return connect(sock, (struct sockaddr *) &conn->raddr.addr, conn->raddr.salen);
}
res = connect(sock, (struct sockaddr *) &conn->paddr.addr, conn->paddr.salen);
if (res < 0)
{
if (SOCK_ERRNO == EINPROGRESS ||
#ifdef WIN32
SOCK_ERRNO == EWOULDBLOCK ||
#endif
SOCK_ERRNO == EINTR)
{
/*
* This is fine - we're in non-blocking mode, and
* the connection is in progress.
* Poll until we can write.
*/
if (pqWaitTimed(0, 1, conn, -1) < 0)
{
return res;
}
}
else
{
conn->status = CONNECTION_BAD;
return res;
}
}
/*
* Send the HTTP connect request. Formatted:
* CONNECT <destination>:<port> HTTP/1.1\r\n
*/
memcpy(ptr, HTTP_CONNECT_REQUEST, CONNECT_REQUEST_LEN);
ptr += CONNECT_REQUEST_LEN;
memcpy(ptr, destname, destnamelen);
ptr += destnamelen;
*ptr++ = ':';
memcpy(ptr, port, portlen);
ptr += portlen;
*ptr++ = ' ';
memcpy(ptr, "HTTP/1.1", 8);
ptr += 8;
*ptr++ = '\r';
*ptr++ = '\n';
/*
* Add Proxy-Authorization request header if credentials is required. Formatted:
* Proxy-Authorization: <authtype> <credentials>\r\n
*/
if (credentialslen > 0)
{
memcpy(ptr, HTTP_PROXY_AUTH, HTTP_PROXY_AUTH_LEN);
ptr += HTTP_PROXY_AUTH_LEN;
memcpy(ptr, authtype, authtypelen);
ptr += authtypelen;
*ptr++ = ' ';
memcpy(ptr, credentials, credentialslen);
ptr += credentialslen;
*ptr++ = '\r';
*ptr++ = '\n';
}
/* Add CRLF to end the HTTP message */
*ptr++ = '\r';
*ptr++ = '\n';
/* Send these bytes to the server */
retry1:
if (send(sock, buffer, ptr - buffer, 0) < 0)
{
if (SOCK_ERRNO == EINTR)
/* Interrupted system call - we'll just try again */
goto retry1;
else
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send data to proxy server."));
conn->status = CONNECTION_BAD;
return -1;
}
}
/* 2. Get the response from the server.
Should be "HTTP/1.1 200 Connection established\r\n" */
retry2:
// Get the current time
time(&now);
// Add 5 seconds to the current time
finish_time = now + timeout;
if (pqWaitTimed(1, 0, conn, finish_time) < 0)
{
RS_LOG_WARN("FECNN", "could not receive data from proxy server. SOCK_ERRNO:%d", SOCK_ERRNO);
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not receive data from proxy server."));
conn->status = CONNECTION_BAD;
return -1;
}
if ((responseLen = recv(sock, buffer, sizeof(buffer), 0)) < 0)
{
if (SOCK_ERRNO == EINTR)
/* Interrupted system call - we'll just try again */
goto retry2;
else
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not receive data from proxy server."));
conn->status = CONNECTION_BAD;
RS_LOG_WARN("FECNN", "could not receive data from proxy server.2");
return -1;
}
}
buffer[responseLen] = '\0';
/* Check that we got HTTP code 200. (HTTP OK). */
/* We skip over the start of the header ("HTTP/1.1") (9 characters). */
/* Note that this also accepts "HTTP/1.0" */
/* We accept all 2xx HTTP codes (indicates success). */
if (responseLen < 9 + 3 ||
buffer[9] != '2')
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("Proxy returned error: %s"), buffer);
RS_LOG_ERROR("FECNN", "Proxy returned error: %s", buffer);
conn->status = CONNECTION_BAD;
return -1;
}
return 0;
}