/src/interfaces/libpq/fe-connect.c
C | 7279 lines | 4622 code | 786 blank | 1871 comment | 1367 complexity | 5023b5d7cee9d978784fb714b05997c0 MD5 | raw file
Possible License(s): AGPL-3.0
Large files files are truncated, but you can click here to view the full file
- /*-------------------------------------------------------------------------
- *
- * fe-connect.c
- * functions related to setting up a connection to the backend
- *
- * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-connect.c
- *
- *-------------------------------------------------------------------------
- */
- #include "postgres_fe.h"
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <ctype.h>
- #include <time.h>
- #include <unistd.h>
- #include "common/ip.h"
- #include "common/link-canary.h"
- #include "common/scram-common.h"
- #include "common/string.h"
- #include "fe-auth.h"
- #include "libpq-fe.h"
- #include "libpq-int.h"
- #include "mb/pg_wchar.h"
- #include "pg_config_paths.h"
- #include "port/pg_bswap.h"
- #ifdef WIN32
- #include "win32.h"
- #ifdef _WIN32_IE
- #undef _WIN32_IE
- #endif
- #define _WIN32_IE 0x0500
- #ifdef near
- #undef near
- #endif
- #define near
- #include <shlobj.h>
- #ifdef _MSC_VER /* mstcpip.h is missing on mingw */
- #include <mstcpip.h>
- #endif
- #else
- #include <sys/socket.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #ifdef HAVE_NETINET_TCP_H
- #include <netinet/tcp.h>
- #endif
- #endif
- #ifdef ENABLE_THREAD_SAFETY
- #ifdef WIN32
- #include "pthread-win32.h"
- #else
- #include <pthread.h>
- #endif
- #endif
- #ifdef USE_LDAP
- #ifdef WIN32
- #include <winldap.h>
- #else
- /* OpenLDAP deprecates RFC 1823, but we want standard conformance */
- #define LDAP_DEPRECATED 1
- #include <ldap.h>
- typedef struct timeval LDAP_TIMEVAL;
- #endif
- static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
- PQExpBuffer errorMessage);
- #endif
- #ifndef WIN32
- #define PGPASSFILE ".pgpass"
- #else
- #define PGPASSFILE "pgpass.conf"
- #endif
- /*
- * Pre-9.0 servers will return this SQLSTATE if asked to set
- * application_name in a startup packet. We hard-wire the value rather
- * than looking into errcodes.h since it reflects historical behavior
- * rather than that of the current code.
- */
- #define ERRCODE_APPNAME_UNKNOWN "42704"
- /* This is part of the protocol so just define it */
- #define ERRCODE_INVALID_PASSWORD "28P01"
- /* This too */
- #define ERRCODE_CANNOT_CONNECT_NOW "57P03"
- /*
- * Cope with the various platform-specific ways to spell TCP keepalive socket
- * options. This doesn't cover Windows, which as usual does its own thing.
- */
- #if defined(TCP_KEEPIDLE)
- /* TCP_KEEPIDLE is the name of this option on Linux and *BSD */
- #define PG_TCP_KEEPALIVE_IDLE TCP_KEEPIDLE
- #define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPIDLE"
- #elif defined(TCP_KEEPALIVE_THRESHOLD)
- /* TCP_KEEPALIVE_THRESHOLD is the name of this option on Solaris >= 11 */
- #define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE_THRESHOLD
- #define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE_THRESHOLD"
- #elif defined(TCP_KEEPALIVE) && defined(__darwin__)
- /* TCP_KEEPALIVE is the name of this option on macOS */
- /* Caution: Solaris has this symbol but it means something different */
- #define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE
- #define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE"
- #endif
- /*
- * fall back options if they are not specified by arguments or defined
- * by environment variables
- */
- #define DefaultHost "localhost"
- #define DefaultTty ""
- #define DefaultOption ""
- #define DefaultAuthtype ""
- #ifdef USE_SSL
- #define DefaultChannelBinding "prefer"
- #else
- #define DefaultChannelBinding "disable"
- #endif
- #define DefaultTargetSessionAttrs "any"
- #ifdef USE_SSL
- #define DefaultSSLMode "prefer"
- #else
- #define DefaultSSLMode "disable"
- #endif
- #ifdef ENABLE_GSS
- #include "fe-gssapi-common.h"
- #define DefaultGSSMode "prefer"
- #else
- #define DefaultGSSMode "disable"
- #endif
- /* ----------
- * Definition of the conninfo parameters and their fallback resources.
- *
- * If Environment-Var and Compiled-in are specified as NULL, no
- * fallback is available. If after all no value can be determined
- * for an option, an error is returned.
- *
- * The value for the username is treated specially in conninfo_add_defaults.
- * If the value is not obtained any other way, the username is determined
- * by pg_fe_getauthname().
- *
- * The Label and Disp-Char entries are provided for applications that
- * want to use PQconndefaults() to create a generic database connection
- * dialog. Disp-Char is defined as follows:
- * "" Normal input field
- * "*" Password field - hide value
- * "D" Debug option - don't show by default
- *
- * PQconninfoOptions[] is a constant static array that we use to initialize
- * a dynamically allocated working copy. All the "val" fields in
- * PQconninfoOptions[] *must* be NULL. In a working copy, non-null "val"
- * fields point to malloc'd strings that should be freed when the working
- * array is freed (see PQconninfoFree).
- *
- * The first part of each struct is identical to the one in libpq-fe.h,
- * which is required since we memcpy() data between the two!
- * ----------
- */
- typedef struct _internalPQconninfoOption
- {
- char *keyword; /* The keyword of the option */
- char *envvar; /* Fallback environment variable name */
- char *compiled; /* Fallback compiled in default value */
- char *val; /* Option's current value, or NULL */
- char *label; /* Label for field in connect dialog */
- char *dispchar; /* Indicates how to display this field in a
- * connect dialog. Values are: "" Display
- * entered value as is "*" Password field -
- * hide value "D" Debug option - don't show
- * by default */
- int dispsize; /* Field size in characters for dialog */
- /* ---
- * Anything above this comment must be synchronized with
- * PQconninfoOption in libpq-fe.h, since we memcpy() data
- * between them!
- * ---
- */
- off_t connofs; /* Offset into PGconn struct, -1 if not there */
- } internalPQconninfoOption;
- static const internalPQconninfoOption PQconninfoOptions[] = {
- /*
- * "authtype" is no longer used, so mark it "don't show". We keep it in
- * the array so as not to reject conninfo strings from old apps that might
- * still try to set it.
- */
- {"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL,
- "Database-Authtype", "D", 20, -1},
- {"service", "PGSERVICE", NULL, NULL,
- "Database-Service", "", 20, -1},
- {"user", "PGUSER", NULL, NULL,
- "Database-User", "", 20,
- offsetof(struct pg_conn, pguser)},
- {"password", "PGPASSWORD", NULL, NULL,
- "Database-Password", "*", 20,
- offsetof(struct pg_conn, pgpass)},
- {"passfile", "PGPASSFILE", NULL, NULL,
- "Database-Password-File", "", 64,
- offsetof(struct pg_conn, pgpassfile)},
- {"channel_binding", "PGCHANNELBINDING", NULL, NULL,
- "Channel-Binding", "", 8, /* sizeof("require") == 8 */
- offsetof(struct pg_conn, channel_binding)},
- {"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL,
- "Connect-timeout", "", 10, /* strlen(INT32_MAX) == 10 */
- offsetof(struct pg_conn, connect_timeout)},
- {"dbname", "PGDATABASE", NULL, NULL,
- "Database-Name", "", 20,
- offsetof(struct pg_conn, dbName)},
- {"host", "PGHOST", NULL, NULL,
- "Database-Host", "", 40,
- offsetof(struct pg_conn, pghost)},
- {"hostaddr", "PGHOSTADDR", NULL, NULL,
- "Database-Host-IP-Address", "", 45,
- offsetof(struct pg_conn, pghostaddr)},
- {"port", "PGPORT", DEF_PGPORT_STR, NULL,
- "Database-Port", "", 6,
- offsetof(struct pg_conn, pgport)},
- {"client_encoding", "PGCLIENTENCODING", NULL, NULL,
- "Client-Encoding", "", 10,
- offsetof(struct pg_conn, client_encoding_initial)},
- /*
- * "tty" is no longer used either, but keep it present for backwards
- * compatibility.
- */
- {"tty", "PGTTY", DefaultTty, NULL,
- "Backend-Debug-TTY", "D", 40,
- offsetof(struct pg_conn, pgtty)},
- {"options", "PGOPTIONS", DefaultOption, NULL,
- "Backend-Options", "", 40,
- offsetof(struct pg_conn, pgoptions)},
- {"application_name", "PGAPPNAME", NULL, NULL,
- "Application-Name", "", 64,
- offsetof(struct pg_conn, appname)},
- {"fallback_application_name", NULL, NULL, NULL,
- "Fallback-Application-Name", "", 64,
- offsetof(struct pg_conn, fbappname)},
- {"keepalives", NULL, NULL, NULL,
- "TCP-Keepalives", "", 1, /* should be just '0' or '1' */
- offsetof(struct pg_conn, keepalives)},
- {"keepalives_idle", NULL, NULL, NULL,
- "TCP-Keepalives-Idle", "", 10, /* strlen(INT32_MAX) == 10 */
- offsetof(struct pg_conn, keepalives_idle)},
- {"keepalives_interval", NULL, NULL, NULL,
- "TCP-Keepalives-Interval", "", 10, /* strlen(INT32_MAX) == 10 */
- offsetof(struct pg_conn, keepalives_interval)},
- {"keepalives_count", NULL, NULL, NULL,
- "TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */
- offsetof(struct pg_conn, keepalives_count)},
- {"tcp_user_timeout", NULL, NULL, NULL,
- "TCP-User-Timeout", "", 10, /* strlen(INT32_MAX) == 10 */
- offsetof(struct pg_conn, pgtcp_user_timeout)},
- /*
- * ssl options are allowed even without client SSL support because the
- * client can still handle SSL modes "disable" and "allow". Other
- * parameters have no effect on non-SSL connections, so there is no reason
- * to exclude them since none of them are mandatory.
- */
- {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
- "SSL-Mode", "", 12, /* sizeof("verify-full") == 12 */
- offsetof(struct pg_conn, sslmode)},
- {"sslcompression", "PGSSLCOMPRESSION", "0", NULL,
- "SSL-Compression", "", 1,
- offsetof(struct pg_conn, sslcompression)},
- {"sslcert", "PGSSLCERT", NULL, NULL,
- "SSL-Client-Cert", "", 64,
- offsetof(struct pg_conn, sslcert)},
- {"sslkey", "PGSSLKEY", NULL, NULL,
- "SSL-Client-Key", "", 64,
- offsetof(struct pg_conn, sslkey)},
- {"sslpassword", NULL, NULL, NULL,
- "SSL-Client-Key-Password", "*", 20,
- offsetof(struct pg_conn, sslpassword)},
- {"sslrootcert", "PGSSLROOTCERT", NULL, NULL,
- "SSL-Root-Certificate", "", 64,
- offsetof(struct pg_conn, sslrootcert)},
- {"sslcrl", "PGSSLCRL", NULL, NULL,
- "SSL-Revocation-List", "", 64,
- offsetof(struct pg_conn, sslcrl)},
- {"requirepeer", "PGREQUIREPEER", NULL, NULL,
- "Require-Peer", "", 10,
- offsetof(struct pg_conn, requirepeer)},
- {"ssl_min_protocol_version", "PGSSLMINPROTOCOLVERSION", NULL, NULL,
- "SSL-Minimum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */
- offsetof(struct pg_conn, ssl_min_protocol_version)},
- {"ssl_max_protocol_version", "PGSSLMAXPROTOCOLVERSION", NULL, NULL,
- "SSL-Maximum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */
- offsetof(struct pg_conn, ssl_max_protocol_version)},
- /*
- * As with SSL, all GSS options are exposed even in builds that don't have
- * support.
- */
- {"gssencmode", "PGGSSENCMODE", DefaultGSSMode, NULL,
- "GSSENC-Mode", "", 8, /* sizeof("disable") == 8 */
- offsetof(struct pg_conn, gssencmode)},
- /* Kerberos and GSSAPI authentication support specifying the service name */
- {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
- "Kerberos-service-name", "", 20,
- offsetof(struct pg_conn, krbsrvname)},
- {"gsslib", "PGGSSLIB", NULL, NULL,
- "GSS-library", "", 7, /* sizeof("gssapi") == 7 */
- offsetof(struct pg_conn, gsslib)},
- {"replication", NULL, NULL, NULL,
- "Replication", "D", 5,
- offsetof(struct pg_conn, replication)},
- {"target_session_attrs", "PGTARGETSESSIONATTRS",
- DefaultTargetSessionAttrs, NULL,
- "Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
- offsetof(struct pg_conn, target_session_attrs)},
- /* Terminating entry --- MUST BE LAST */
- {NULL, NULL, NULL, NULL,
- NULL, NULL, 0}
- };
- static const PQEnvironmentOption EnvironmentOptions[] =
- {
- /* common user-interface settings */
- {
- "PGDATESTYLE", "datestyle"
- },
- {
- "PGTZ", "timezone"
- },
- /* internal performance-related settings */
- {
- "PGGEQO", "geqo"
- },
- {
- NULL, NULL
- }
- };
- /* The connection URI must start with either of the following designators: */
- static const char uri_designator[] = "postgresql://";
- static const char short_uri_designator[] = "postgres://";
- static bool connectOptions1(PGconn *conn, const char *conninfo);
- static bool connectOptions2(PGconn *conn);
- static int connectDBStart(PGconn *conn);
- static int connectDBComplete(PGconn *conn);
- static PGPing internal_ping(PGconn *conn);
- static PGconn *makeEmptyPGconn(void);
- static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
- static void freePGconn(PGconn *conn);
- static void closePGconn(PGconn *conn);
- static void release_conn_addrinfo(PGconn *conn);
- static void sendTerminateConn(PGconn *conn);
- static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
- static PQconninfoOption *parse_connection_string(const char *conninfo,
- PQExpBuffer errorMessage, bool use_defaults);
- static int uri_prefix_length(const char *connstr);
- static bool recognized_connection_string(const char *connstr);
- static PQconninfoOption *conninfo_parse(const char *conninfo,
- PQExpBuffer errorMessage, bool use_defaults);
- static PQconninfoOption *conninfo_array_parse(const char *const *keywords,
- const char *const *values, PQExpBuffer errorMessage,
- bool use_defaults, int expand_dbname);
- static bool conninfo_add_defaults(PQconninfoOption *options,
- PQExpBuffer errorMessage);
- static PQconninfoOption *conninfo_uri_parse(const char *uri,
- PQExpBuffer errorMessage, bool use_defaults);
- static bool conninfo_uri_parse_options(PQconninfoOption *options,
- const char *uri, PQExpBuffer errorMessage);
- static bool conninfo_uri_parse_params(char *params,
- PQconninfoOption *connOptions,
- PQExpBuffer errorMessage);
- static char *conninfo_uri_decode(const char *str, PQExpBuffer errorMessage);
- static bool get_hexdigit(char digit, int *value);
- static const char *conninfo_getval(PQconninfoOption *connOptions,
- const char *keyword);
- static PQconninfoOption *conninfo_storeval(PQconninfoOption *connOptions,
- const char *keyword, const char *value,
- PQExpBuffer errorMessage, bool ignoreMissing, bool uri_decode);
- static PQconninfoOption *conninfo_find(PQconninfoOption *connOptions,
- const char *keyword);
- static void defaultNoticeReceiver(void *arg, const PGresult *res);
- static void defaultNoticeProcessor(void *arg, const char *message);
- 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, const char *token);
- static char *passwordFromFile(const char *hostname, const char *port, const char *dbname,
- const char *username, const char *pgpassfile);
- static void pgpassfileWarning(PGconn *conn);
- static void default_threadlock(int acquire);
- static bool sslVerifyProtocolVersion(const char *version);
- static bool sslVerifyProtocolRange(const char *min, const char *max);
- /* global variable because fe-auth.c needs to access it */
- pgthreadlock_t pg_g_threadlock = default_threadlock;
- /*
- * pqDropConnection
- *
- * Close any physical connection to the server, and reset associated
- * state inside the connection object. We don't release state that
- * would be needed to reconnect, though, nor local state that might still
- * be useful later.
- *
- * We can always flush the output buffer, since there's no longer any hope
- * of sending that data. However, unprocessed input data might still be
- * valuable, so the caller must tell us whether to flush that or not.
- */
- void
- pqDropConnection(PGconn *conn, bool flushInput)
- {
- /* Drop any SSL state */
- pqsecure_close(conn);
- /* Close the socket itself */
- if (conn->sock != PGINVALID_SOCKET)
- closesocket(conn->sock);
- conn->sock = PGINVALID_SOCKET;
- /* Optionally discard any unread data */
- if (flushInput)
- conn->inStart = conn->inCursor = conn->inEnd = 0;
- /* Always discard any unsent data */
- conn->outCount = 0;
- /* Free authentication/encryption state */
- #ifdef ENABLE_GSS
- {
- OM_uint32 min_s;
- if (conn->gctx)
- gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER);
- if (conn->gtarg_nam)
- gss_release_name(&min_s, &conn->gtarg_nam);
- if (conn->gss_SendBuffer)
- {
- free(conn->gss_SendBuffer);
- conn->gss_SendBuffer = NULL;
- }
- if (conn->gss_RecvBuffer)
- {
- free(conn->gss_RecvBuffer);
- conn->gss_RecvBuffer = NULL;
- }
- if (conn->gss_ResultBuffer)
- {
- free(conn->gss_ResultBuffer);
- conn->gss_ResultBuffer = NULL;
- }
- }
- #endif
- #ifdef ENABLE_SSPI
- if (conn->sspitarget)
- {
- free(conn->sspitarget);
- conn->sspitarget = NULL;
- }
- if (conn->sspicred)
- {
- FreeCredentialsHandle(conn->sspicred);
- free(conn->sspicred);
- conn->sspicred = NULL;
- }
- if (conn->sspictx)
- {
- DeleteSecurityContext(conn->sspictx);
- free(conn->sspictx);
- conn->sspictx = NULL;
- }
- conn->usesspi = 0;
- #endif
- if (conn->sasl_state)
- {
- /*
- * XXX: if support for more authentication mechanisms is added, this
- * needs to call the right 'free' function.
- */
- pg_fe_scram_free(conn->sasl_state);
- conn->sasl_state = NULL;
- }
- }
- /*
- * pqDropServerData
- *
- * Clear all connection state data that was received from (or deduced about)
- * the server. This is essential to do between connection attempts to
- * different servers, else we may incorrectly hold over some data from the
- * old server.
- *
- * It would be better to merge this into pqDropConnection, perhaps, but
- * right now we cannot because that function is called immediately on
- * detection of connection loss (cf. pqReadData, for instance). This data
- * should be kept until we are actually starting a new connection.
- */
- static void
- pqDropServerData(PGconn *conn)
- {
- PGnotify *notify;
- pgParameterStatus *pstatus;
- /* Forget pending notifies */
- notify = conn->notifyHead;
- while (notify != NULL)
- {
- PGnotify *prev = notify;
- notify = notify->next;
- free(prev);
- }
- conn->notifyHead = conn->notifyTail = NULL;
- /* Reset ParameterStatus data, as well as variables deduced from it */
- pstatus = conn->pstatus;
- while (pstatus != NULL)
- {
- pgParameterStatus *prev = pstatus;
- pstatus = pstatus->next;
- free(prev);
- }
- conn->pstatus = NULL;
- conn->client_encoding = PG_SQL_ASCII;
- conn->std_strings = false;
- conn->sversion = 0;
- /* Drop large-object lookup data */
- if (conn->lobjfuncs)
- free(conn->lobjfuncs);
- conn->lobjfuncs = NULL;
- /* Reset assorted other per-connection state */
- conn->last_sqlstate[0] = '\0';
- conn->auth_req_received = false;
- conn->password_needed = false;
- conn->write_failed = false;
- if (conn->write_err_msg)
- free(conn->write_err_msg);
- conn->write_err_msg = NULL;
- conn->be_pid = 0;
- conn->be_key = 0;
- }
- /*
- * Connecting to a Database
- *
- * There are now six different ways a user of this API can connect to the
- * database. Two are not recommended for use in new code, because of their
- * lack of extensibility with respect to the passing of options to the
- * backend. These are PQsetdb and PQsetdbLogin (the former now being a macro
- * to the latter).
- *
- * If it is desired to connect in a synchronous (blocking) manner, use the
- * function PQconnectdb or PQconnectdbParams. The former accepts a string of
- * option = value pairs (or a URI) which must be parsed; the latter takes two
- * NULL terminated arrays instead.
- *
- * To connect in an asynchronous (non-blocking) manner, use the functions
- * PQconnectStart or PQconnectStartParams (which differ in the same way as
- * PQconnectdb and PQconnectdbParams) and PQconnectPoll.
- *
- * Internally, the static functions connectDBStart, connectDBComplete
- * are part of the connection procedure.
- */
- /*
- * PQconnectdbParams
- *
- * establishes a connection to a postgres backend through the postmaster
- * using connection information in two arrays.
- *
- * The keywords array is defined as
- *
- * const char *params[] = {"option1", "option2", NULL}
- *
- * The values array is defined as
- *
- * const char *values[] = {"value1", "value2", NULL}
- *
- * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
- * if a memory allocation failed.
- * If the status field of the connection returned is CONNECTION_BAD,
- * then some fields may be null'ed out instead of having valid values.
- *
- * You should call PQfinish (if conn is not NULL) regardless of whether this
- * call succeeded.
- */
- PGconn *
- PQconnectdbParams(const char *const *keywords,
- const char *const *values,
- int expand_dbname)
- {
- PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname);
- if (conn && conn->status != CONNECTION_BAD)
- (void) connectDBComplete(conn);
- return conn;
- }
- /*
- * PQpingParams
- *
- * check server status, accepting parameters identical to PQconnectdbParams
- */
- PGPing
- PQpingParams(const char *const *keywords,
- const char *const *values,
- int expand_dbname)
- {
- PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname);
- PGPing ret;
- ret = internal_ping(conn);
- PQfinish(conn);
- return ret;
- }
- /*
- * PQconnectdb
- *
- * establishes a connection to a postgres backend through the postmaster
- * using connection information in a string.
- *
- * The conninfo string is either a whitespace-separated list of
- *
- * option = value
- *
- * definitions or a URI (refer to the documentation for details.) Value
- * might be a single value containing no whitespaces or a single quoted
- * string. If a single quote should appear anywhere in the value, it must be
- * escaped with a backslash like \'
- *
- * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
- * if a memory allocation failed.
- * If the status field of the connection returned is CONNECTION_BAD,
- * then some fields may be null'ed out instead of having valid values.
- *
- * You should call PQfinish (if conn is not NULL) regardless of whether this
- * call succeeded.
- */
- PGconn *
- PQconnectdb(const char *conninfo)
- {
- PGconn *conn = PQconnectStart(conninfo);
- if (conn && conn->status != CONNECTION_BAD)
- (void) connectDBComplete(conn);
- return conn;
- }
- /*
- * PQping
- *
- * check server status, accepting parameters identical to PQconnectdb
- */
- PGPing
- PQping(const char *conninfo)
- {
- PGconn *conn = PQconnectStart(conninfo);
- PGPing ret;
- ret = internal_ping(conn);
- PQfinish(conn);
- return ret;
- }
- /*
- * PQconnectStartParams
- *
- * Begins the establishment of a connection to a postgres backend through the
- * postmaster using connection information in a struct.
- *
- * See comment for PQconnectdbParams for the definition of the string format.
- *
- * Returns a PGconn*. If NULL is returned, a malloc error has occurred, and
- * you should not attempt to proceed with this connection. If the status
- * field of the connection returned is CONNECTION_BAD, an error has
- * occurred. In this case you should call PQfinish on the result, (perhaps
- * inspecting the error message first). Other fields of the structure may not
- * be valid if that occurs. If the status field is not CONNECTION_BAD, then
- * this stage has succeeded - call PQconnectPoll, using select(2) to see when
- * this is necessary.
- *
- * See PQconnectPoll for more info.
- */
- PGconn *
- PQconnectStartParams(const char *const *keywords,
- const char *const *values,
- int expand_dbname)
- {
- PGconn *conn;
- PQconninfoOption *connOptions;
- /*
- * Allocate memory for the conn structure
- */
- conn = makeEmptyPGconn();
- if (conn == NULL)
- return NULL;
- /*
- * Parse the conninfo arrays
- */
- connOptions = conninfo_array_parse(keywords, values,
- &conn->errorMessage,
- true, expand_dbname);
- if (connOptions == NULL)
- {
- conn->status = CONNECTION_BAD;
- /* errorMessage is already set */
- return conn;
- }
- /*
- * Move option values into conn structure
- */
- if (!fillPGconn(conn, connOptions))
- {
- PQconninfoFree(connOptions);
- return conn;
- }
- /*
- * Free the option info - all is in conn now
- */
- PQconninfoFree(connOptions);
- /*
- * Compute derived options
- */
- if (!connectOptions2(conn))
- return conn;
- /*
- * Connect to the database
- */
- if (!connectDBStart(conn))
- {
- /* Just in case we failed to set it in connectDBStart */
- conn->status = CONNECTION_BAD;
- }
- return conn;
- }
- /*
- * PQconnectStart
- *
- * Begins the establishment of a connection to a postgres backend through the
- * postmaster using connection information in a string.
- *
- * See comment for PQconnectdb for the definition of the string format.
- *
- * Returns a PGconn*. If NULL is returned, a malloc error has occurred, and
- * you should not attempt to proceed with this connection. If the status
- * field of the connection returned is CONNECTION_BAD, an error has
- * occurred. In this case you should call PQfinish on the result, (perhaps
- * inspecting the error message first). Other fields of the structure may not
- * be valid if that occurs. If the status field is not CONNECTION_BAD, then
- * this stage has succeeded - call PQconnectPoll, using select(2) to see when
- * this is necessary.
- *
- * See PQconnectPoll for more info.
- */
- PGconn *
- PQconnectStart(const char *conninfo)
- {
- PGconn *conn;
- /*
- * Allocate memory for the conn structure
- */
- conn = makeEmptyPGconn();
- if (conn == NULL)
- return NULL;
- /*
- * Parse the conninfo string
- */
- if (!connectOptions1(conn, conninfo))
- return conn;
- /*
- * Compute derived options
- */
- if (!connectOptions2(conn))
- return conn;
- /*
- * Connect to the database
- */
- if (!connectDBStart(conn))
- {
- /* Just in case we failed to set it in connectDBStart */
- conn->status = CONNECTION_BAD;
- }
- return conn;
- }
- /*
- * Move option values into conn structure
- *
- * Don't put anything cute here --- intelligence should be in
- * connectOptions2 ...
- *
- * Returns true on success. On failure, returns false and sets error message.
- */
- static bool
- fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
- {
- const internalPQconninfoOption *option;
- for (option = PQconninfoOptions; option->keyword; option++)
- {
- if (option->connofs >= 0)
- {
- const char *tmp = conninfo_getval(connOptions, option->keyword);
- if (tmp)
- {
- char **connmember = (char **) ((char *) conn + option->connofs);
- if (*connmember)
- free(*connmember);
- *connmember = strdup(tmp);
- if (*connmember == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
- }
- }
- }
- }
- return true;
- }
- /*
- * connectOptions1
- *
- * Internal subroutine to set up connection parameters given an already-
- * created PGconn and a conninfo string. Derived settings should be
- * processed by calling connectOptions2 next. (We split them because
- * PQsetdbLogin overrides defaults in between.)
- *
- * Returns true if OK, false if trouble (in which case errorMessage is set
- * and so is conn->status).
- */
- static bool
- connectOptions1(PGconn *conn, const char *conninfo)
- {
- PQconninfoOption *connOptions;
- /*
- * Parse the conninfo string
- */
- connOptions = parse_connection_string(conninfo, &conn->errorMessage, true);
- if (connOptions == NULL)
- {
- conn->status = CONNECTION_BAD;
- /* errorMessage is already set */
- return false;
- }
- /*
- * Move option values into conn structure
- */
- if (!fillPGconn(conn, connOptions))
- {
- conn->status = CONNECTION_BAD;
- PQconninfoFree(connOptions);
- return false;
- }
- /*
- * Free the option info - all is in conn now
- */
- PQconninfoFree(connOptions);
- return true;
- }
- /*
- * Count the number of elements in a simple comma-separated list.
- */
- static int
- count_comma_separated_elems(const char *input)
- {
- int n;
- n = 1;
- for (; *input != '\0'; input++)
- {
- if (*input == ',')
- n++;
- }
- return n;
- }
- /*
- * Parse a simple comma-separated list.
- *
- * On each call, returns a malloc'd copy of the next element, and sets *more
- * to indicate whether there are any more elements in the list after this,
- * and updates *startptr to point to the next element, if any.
- *
- * On out of memory, returns NULL.
- */
- static char *
- parse_comma_separated_list(char **startptr, bool *more)
- {
- char *p;
- char *s = *startptr;
- char *e;
- int len;
- /*
- * Search for the end of the current element; a comma or end-of-string
- * acts as a terminator.
- */
- e = s;
- while (*e != '\0' && *e != ',')
- ++e;
- *more = (*e == ',');
- len = e - s;
- p = (char *) malloc(sizeof(char) * (len + 1));
- if (p)
- {
- memcpy(p, s, len);
- p[len] = '\0';
- }
- *startptr = e + 1;
- return p;
- }
- /*
- * connectOptions2
- *
- * Compute derived connection options after absorbing all user-supplied info.
- *
- * Returns true if OK, false if trouble (in which case errorMessage is set
- * and so is conn->status).
- */
- static bool
- connectOptions2(PGconn *conn)
- {
- int i;
- /*
- * Allocate memory for details about each host to which we might possibly
- * try to connect. For that, count the number of elements in the hostaddr
- * or host options. If neither is given, assume one host.
- */
- conn->whichhost = 0;
- if (conn->pghostaddr && conn->pghostaddr[0] != '\0')
- conn->nconnhost = count_comma_separated_elems(conn->pghostaddr);
- else if (conn->pghost && conn->pghost[0] != '\0')
- conn->nconnhost = count_comma_separated_elems(conn->pghost);
- else
- conn->nconnhost = 1;
- conn->connhost = (pg_conn_host *)
- calloc(conn->nconnhost, sizeof(pg_conn_host));
- if (conn->connhost == NULL)
- goto oom_error;
- /*
- * We now have one pg_conn_host structure per possible host. Fill in the
- * host and hostaddr fields for each, by splitting the parameter strings.
- */
- if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
- {
- char *s = conn->pghostaddr;
- bool more = true;
- for (i = 0; i < conn->nconnhost && more; i++)
- {
- conn->connhost[i].hostaddr = parse_comma_separated_list(&s, &more);
- if (conn->connhost[i].hostaddr == NULL)
- goto oom_error;
- }
- /*
- * If hostaddr was given, the array was allocated according to the
- * number of elements in the hostaddr list, so it really should be the
- * right size.
- */
- Assert(!more);
- Assert(i == conn->nconnhost);
- }
- if (conn->pghost != NULL && conn->pghost[0] != '\0')
- {
- char *s = conn->pghost;
- bool more = true;
- for (i = 0; i < conn->nconnhost && more; i++)
- {
- conn->connhost[i].host = parse_comma_separated_list(&s, &more);
- if (conn->connhost[i].host == NULL)
- goto oom_error;
- }
- /* Check for wrong number of host items. */
- if (more || i != conn->nconnhost)
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not match %d host names to %d hostaddr values\n"),
- count_comma_separated_elems(conn->pghost), conn->nconnhost);
- return false;
- }
- }
- /*
- * Now, for each host slot, identify the type of address spec, and fill in
- * the default address if nothing was given.
- */
- for (i = 0; i < conn->nconnhost; i++)
- {
- pg_conn_host *ch = &conn->connhost[i];
- if (ch->hostaddr != NULL && ch->hostaddr[0] != '\0')
- ch->type = CHT_HOST_ADDRESS;
- else if (ch->host != NULL && ch->host[0] != '\0')
- {
- ch->type = CHT_HOST_NAME;
- #ifdef HAVE_UNIX_SOCKETS
- if (is_absolute_path(ch->host))
- ch->type = CHT_UNIX_SOCKET;
- #endif
- }
- else
- {
- if (ch->host)
- free(ch->host);
- #ifdef HAVE_UNIX_SOCKETS
- if (DEFAULT_PGSOCKET_DIR[0])
- {
- ch->host = strdup(DEFAULT_PGSOCKET_DIR);
- ch->type = CHT_UNIX_SOCKET;
- }
- else
- #endif
- {
- ch->host = strdup(DefaultHost);
- ch->type = CHT_HOST_NAME;
- }
- if (ch->host == NULL)
- goto oom_error;
- }
- }
- /*
- * Next, work out the port number corresponding to each host name.
- *
- * Note: unlike the above for host names, this could leave the port fields
- * as null or empty strings. We will substitute DEF_PGPORT whenever we
- * read such a port field.
- */
- if (conn->pgport != NULL && conn->pgport[0] != '\0')
- {
- char *s = conn->pgport;
- bool more = true;
- for (i = 0; i < conn->nconnhost && more; i++)
- {
- conn->connhost[i].port = parse_comma_separated_list(&s, &more);
- if (conn->connhost[i].port == NULL)
- goto oom_error;
- }
- /*
- * If exactly one port was given, use it for every host. Otherwise,
- * there must be exactly as many ports as there were hosts.
- */
- if (i == 1 && !more)
- {
- for (i = 1; i < conn->nconnhost; i++)
- {
- conn->connhost[i].port = strdup(conn->connhost[0].port);
- if (conn->connhost[i].port == NULL)
- goto oom_error;
- }
- }
- else if (more || i != conn->nconnhost)
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not match %d port numbers to %d hosts\n"),
- count_comma_separated_elems(conn->pgport), conn->nconnhost);
- return false;
- }
- }
- /*
- * If user name was not given, fetch it. (Most likely, the fetch will
- * fail, since the only way we get here is if pg_fe_getauthname() failed
- * during conninfo_add_defaults(). But now we want an error message.)
- */
- if (conn->pguser == NULL || conn->pguser[0] == '\0')
- {
- if (conn->pguser)
- free(conn->pguser);
- conn->pguser = pg_fe_getauthname(&conn->errorMessage);
- if (!conn->pguser)
- {
- conn->status = CONNECTION_BAD;
- return false;
- }
- }
- /*
- * If database name was not given, default it to equal user name
- */
- if (conn->dbName == NULL || conn->dbName[0] == '\0')
- {
- if (conn->dbName)
- free(conn->dbName);
- conn->dbName = strdup(conn->pguser);
- if (!conn->dbName)
- goto oom_error;
- }
- /*
- * If password was not given, try to look it up in password file. Note
- * that the result might be different for each host/port pair.
- */
- if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
- {
- /* If password file wasn't specified, use ~/PGPASSFILE */
- if (conn->pgpassfile == NULL || conn->pgpassfile[0] == '\0')
- {
- char homedir[MAXPGPATH];
- if (pqGetHomeDirectory(homedir, sizeof(homedir)))
- {
- if (conn->pgpassfile)
- free(conn->pgpassfile);
- conn->pgpassfile = malloc(MAXPGPATH);
- if (!conn->pgpassfile)
- goto oom_error;
- snprintf(conn->pgpassfile, MAXPGPATH, "%s/%s",
- homedir, PGPASSFILE);
- }
- }
- if (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0')
- {
- for (i = 0; i < conn->nconnhost; i++)
- {
- /*
- * Try to get a password for this host from file. We use host
- * for the hostname search key if given, else hostaddr (at
- * least one of them is guaranteed nonempty by now).
- */
- const char *pwhost = conn->connhost[i].host;
- if (pwhost == NULL || pwhost[0] == '\0')
- pwhost = conn->connhost[i].hostaddr;
- conn->connhost[i].password =
- passwordFromFile(pwhost,
- conn->connhost[i].port,
- conn->dbName,
- conn->pguser,
- conn->pgpassfile);
- }
- }
- }
- /*
- * validate channel_binding option
- */
- if (conn->channel_binding)
- {
- if (strcmp(conn->channel_binding, "disable") != 0
- && strcmp(conn->channel_binding, "prefer") != 0
- && strcmp(conn->channel_binding, "require") != 0)
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid channel_binding value: \"%s\"\n"),
- conn->channel_binding);
- return false;
- }
- }
- else
- {
- conn->channel_binding = strdup(DefaultChannelBinding);
- if (!conn->channel_binding)
- goto oom_error;
- }
- /*
- * validate sslmode option
- */
- if (conn->sslmode)
- {
- if (strcmp(conn->sslmode, "disable") != 0
- && strcmp(conn->sslmode, "allow") != 0
- && strcmp(conn->sslmode, "prefer") != 0
- && strcmp(conn->sslmode, "require") != 0
- && strcmp(conn->sslmode, "verify-ca") != 0
- && strcmp(conn->sslmode, "verify-full") != 0)
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid sslmode value: \"%s\"\n"),
- conn->sslmode);
- return false;
- }
- #ifndef USE_SSL
- switch (conn->sslmode[0])
- {
- case 'a': /* "allow" */
- case 'p': /* "prefer" */
- /*
- * warn user that an SSL connection will never be negotiated
- * since SSL was not compiled in?
- */
- break;
- case 'r': /* "require" */
- case 'v': /* "verify-ca" or "verify-full" */
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"),
- conn->sslmode);
- return false;
- }
- #endif
- }
- else
- {
- conn->sslmode = strdup(DefaultSSLMode);
- if (!conn->sslmode)
- goto oom_error;
- }
- /*
- * Validate TLS protocol versions for ssl_min_protocol_version and
- * ssl_max_protocol_version.
- */
- if (!sslVerifyProtocolVersion(conn->ssl_min_protocol_version))
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid ssl_min_protocol_version value: \"%s\"\n"),
- conn->ssl_min_protocol_version);
- return false;
- }
- if (!sslVerifyProtocolVersion(conn->ssl_max_protocol_version))
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid ssl_max_protocol_version value: \"%s\"\n"),
- conn->ssl_max_protocol_version);
- return false;
- }
- /*
- * Check if the range of SSL protocols defined is correct. This is done
- * at this early step because this is independent of the SSL
- * implementation used, and this avoids unnecessary cycles with an
- * already-built SSL context when the connection is being established, as
- * it would be doomed anyway.
- */
- if (!sslVerifyProtocolRange(conn->ssl_min_protocol_version,
- conn->ssl_max_protocol_version))
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid SSL protocol version range\n"));
- return false;
- }
- /*
- * validate gssencmode option
- */
- if (conn->gssencmode)
- {
- if (strcmp(conn->gssencmode, "disable") != 0 &&
- strcmp(conn->gssencmode, "prefer") != 0 &&
- strcmp(conn->gssencmode, "require") != 0)
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid gssencmode value: \"%s\"\n"),
- conn->gssencmode);
- return false;
- }
- #ifndef ENABLE_GSS
- if (strcmp(conn->gssencmode, "require") == 0)
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"),
- conn->gssencmode);
- return false;
- }
- #endif
- }
- else
- {
- conn->gssencmode = strdup(DefaultGSSMode);
- if (!conn->gssencmode)
- goto oom_error;
- }
- /*
- * Resolve special "auto" client_encoding from the locale
- */
- if (conn->client_encoding_initial &&
- strcmp(conn->client_encoding_initial, "auto") == 0)
- {
- free(conn->client_encoding_initial);
- conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true)));
- if (!conn->client_encoding_initial)
- goto oom_error;
- }
- /*
- * Validate target_session_attrs option.
- */
- if (conn->target_session_attrs)
- {
- if (strcmp(conn->target_session_attrs, "any") != 0
- && strcmp(conn->target_session_attrs, "read-write") != 0)
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid target_session_attrs value: \"%s\"\n"),
- conn->target_session_attrs);
- return false;
- }
- }
- /*
- * Only if we get this far is it appropriate to try to connect. (We need a
- * state flag, rather than just the boolean result of this function, in
- * case someone tries to PQreset() the PGconn.)
- */
- conn->options_valid = true;
- return true;
- oom_error:
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
- }
- /*
- * PQconndefaults
- *
- * Construct a default connection options array, which identifies all the
- * available options and shows any default values that are available from the
- * environment etc. On error (eg out of memory), NULL is returned.
- *
- * Using this function, an application may determine all possible options
- * and their current default values.
- *
- * NOTE: as of PostgreSQL 7.0, the returned array is dynamically allocated
- * and should be freed when no longer needed via PQconninfoFree(). (In prior
- * versions, the returned array was static, but that's not thread-safe.)
- * Pre-7.0 applications that use this function will see a small memory leak
- * until they are updated to call PQconninfoFree.
- */
- PQconninfoOption *
- PQconndefaults(void)
- {
- PQExpBufferData errorBuf;
- PQconninfoOption *connOptions;
- /* We don't actually report any errors here, but callees want a buffer */
- initPQExpBuffer(&errorBuf);
- if (PQExpBufferDataBroken(errorBuf))
- return NULL; /* out of memory already :-( */
- connOptions = conninfo_init(&errorBuf);
- if (connOptions != NULL)
- {
- /* pass NULL errorBuf to ignore errors */
- if (!conninfo_add_defaults(connOptions, NULL))
- {
- PQconninfoFree(connOptions);
- connOptions = NULL;
- }
- }
- termPQExpBuffer(&errorBuf);
- return connOptions;
- }
- /* ----------------
- * PQsetdbLogin
- *
- * establishes a connection to a postgres backend through the postmaster
- * at the specified host and port.
- *
- * returns a PGconn* which is needed for all subsequent libpq calls
- *
- * if the status field of the connection returned is CONNECTION_BAD,
- * then only the errorMessage is likely to be useful.
- * ----------------
- */
- PGconn *
- PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
- const char *pgtty, const char *dbName, const char *login,
- const char *pwd)
- {
- PGconn *conn;
- /*
- * Allocate memory for the conn structure
- */
- conn = makeEmptyPGconn();
- if (conn == NULL)
- return NULL;
- /*
- * If the dbName parameter contains what looks like a connection string,
- * parse it into conn struct using connectOptions1.
- */
- if (dbName && recognized_connection_string(dbName))
- {
- if (!connectOptions1(conn, dbName))
- return conn;
- }
- else
- {
- /*
- * Old-style path: first, parse an empty conninfo string in order to
- * set up the same defaults that PQconnectdb() would use.
- */
- if (!connectOptions1(conn, ""))
- return conn;
- /* Insert dbName parameter value into struct */
- if (dbName && dbName[0] != '\0')
- {
- if (conn->dbName)
- free(conn->dbName);
- conn->dbName = strdup(dbName);
- if (!conn->dbName)
- goto oom_error;
- }
- }
- /*
- * Insert remaining parameters into struct, overriding defaults (as well
- * as any conflicting data from dbName taken as a conninfo).
- */
- if (pghost && pghost[0] != '\0')
- {
- if (conn->pghost)
- free(conn->pghost);
- conn->pghost = strdup(pghost);
- if (!conn->pghost)
- goto oom_error;
- }
- if (pgport && pgport[0] != '\0')
- {
- if (conn->pgport)
- free(conn->pgport);
- conn->pgport = strdup(pgport);
- if (!conn->pgport)
- goto oom_error;
- }
- if (pgoptions && pgoptions[0] != '\0')
- {
- if (conn->pgoptions)
- free(conn->pgoptions);
- conn->pgoptions = strdup(pgoptions);
- if (!conn->pgoptions)
- goto oom_error;
- }
- if (pgtty && pgtty[0] != '\0')
- {
- if (conn->pgtty)
- free(conn->pgtty);
- conn->pgtty = strdup(pgtty);
- if (!conn->pgtty)
- goto oom_error;
- }
- if (login && login[0] != '\0')
- {
- if (conn->pguser)
- free(conn->pguser);
- conn->pguser = strdup(login);
- if (!conn->pguser)
- goto oom_error;
- }
- if (pwd && pwd[0] != '\0')
- {
- if (conn->pgpass)
- free(conn->pgpass);
- conn->pgpass = strdup(pwd);
- if (!conn->pgpass)
- goto oom_error;
- }
- /*
- * Compute derived options
- */
- if (!connectOptions2(conn))
- return conn;
- /*
- * Connect to the database
- */
- if (connectDBStart(conn))
- (void) connectDBComplete(conn);
- return conn;
- oom_error:
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return conn;
- }
- /* ----------
- * connectNoDelay -
- * Sets the TCP_NODELAY socket option.
- * Returns 1 if successful, 0 if not.
- * ----------
- */
- static int
- connectNoDelay(PGconn *conn)
- {
- #ifdef TCP_NODELAY
- int on = 1;
- if (setsockopt(conn->sock, IPPROTO_TCP, TCP_NODELAY,
- (char *) &on,
- sizeof(on)) < 0)
- {
- char sebuf[PG_STRERROR_R_BUFLEN];
- 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;
- }
- #endif
- return 1;
- }
- /* ----------
- * Write currently connected IP address into host_addr (of len host_addr_len).
- * If unable to, set it to the empty string.
- * ----------
- */
- static void
- getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
- {
- struct sockaddr_storage *addr = &conn->raddr.addr;
- if (addr->ss_family == AF_INET)
- {
- if (pg_inet_net_ntop(AF_INET,
- &((struct sockaddr_in *) addr)->sin_addr.s_addr,
- 32,
- host_addr, host_addr_len) == NULL)
- host_addr[0] = '\0';
- }
- #ifdef HAVE_IPV6
- else if (addr->ss_family == AF_INET6)
- {
- if (pg_inet_net_ntop(AF_INET6,
- &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
- 128,
- host_addr, host_addr_len) == NULL)
- host_addr[0] = '\0';
- }
- #endif
- else
- host_addr[0] = '\0';
- }
- /* ----------
- * connectFailureMessage -
- * create a friendly error message on connection failure.
- * ----------
- */
- static void
- connectFailureMessage(PGconn *conn, int errorno)
- {
- char sebuf[PG_STRERROR_R_BUFLEN];
- #ifdef HAVE_UNIX_SOCKETS
- if (IS_AF_UNIX(conn->raddr.addr.ss_family))
- {
- char service[NI_MAXHOST];
- pg_getnameinfo_all(&conn->raddr.addr, conn->raddr.salen,
- 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"),
- SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
- service);
- }
- else
- #endif /* HAVE_UNIX_SOCKETS */
- {
- char host_addr[NI_MAXHOST];
- const char *displayed_host;
- const char *displayed_port;
- /*
- * Optionally display the network address with the hostname. This is
- * useful to distinguish between IPv4 and IPv6 connections.
- */
- getHostaddr(conn, host_addr, NI_MAXHOST);
- /* To which host and port were we actually connecting? */
- if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
- displayed_host = conn->connhost[conn->whichhost].hostaddr;
- else
- displayed_host = conn->connhost[conn->whichhost].host;
- displayed_port = conn->connhost[conn->whichhost].port;
- if (displayed_port == NULL || displayed_port[0] == '\0')
- displayed_port = DEF_PGPORT_STR;
- /*
- * If the user did not supply an IP address using 'hostaddr', and
- * 'host' was missing or does not match our lookup, display the
- * looked-up IP address.
- */
- if (conn->connhost[conn->whichhost].type != CHT_HOST_ADDRESS &&
- strlen(host_addr) > 0 &&
- strcmp(displayed_host, host_addr) != 0)
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not connect to server: %s\n"
- "\tIs the server running on host \"%s\" (%s) and accepting\n"
- "\tTCP/IP connections on port %s?\n"),
- SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
- displayed_host, host_addr,
- displayed_port);
- else
- 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"),
- SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
- displayed_host,
- displayed_port);
- }
- }
- /*
- * Should we use keepalives? Returns 1 if yes, 0 if no, and -1 if
- * conn->keepalives is set to a value which is not parseable as an
- * integer.
- */
- static int
- useKeepalives(PGconn *conn)
- {
- char *ep;
- int val;
- if (conn->keepalives == NULL)
- return 1;
- val = strtol(conn->keepalives, &ep, 10);
- if (*ep)
- return -1;
- return val != 0 ? 1 : 0;
- }
- /*
- * Parse and try to interpret "value" as an integer value, and if successful,
- * store it in *result, complaining if there is any trailing garbage or an
- * overflow. This allows any number of leading and trailing whitespaces.
- */
- static bool
- parse_int_param(const char *value, int *result, PGconn *conn,
- const char *context)
- {
- char *end;
- long numval;
- Assert(value != NULL);
- *result = 0;
- /* strtol(3) skips leading whitespaces */
- errno = 0;
- numval = strtol(value, &end, 10);
- /*
- * If no progress was done during the parsing or an error happened, fail.
- * This tests properly for overflows of the result.
- */
- if (value == end || errno != 0 || numval != (int) numval)
- goto error;
- /*
- * Skip any trailing whitespace; if anything but whitespace remains before
- * the terminating character, fail
- */
- while (*end != '\0' && isspace((unsigned char) *end))
- end++;
- if (*end != '\0')
- goto error;
- *result = numval;
- return true;
- error:
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid integer value \"%s\" for connection option \"%s\"\n"),
- value, context);
- return false;
- }
- #ifndef WIN32
- /*
- * Set the keepalive idle timer.
- */
- static int
- setKeepalivesIdle(PGconn *conn)
- {
- int idle;
- if (conn->keepalives_idle == NULL)
- return 1;
- if (!parse_int_param(conn->keepalives_idle, &idle, conn,
- "keepalives_idle"))
- return 0;
- if (idle < 0)
- idle = 0;
- #ifdef PG_TCP_KEEPALIVE_IDLE
- if (setsockopt(conn->sock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
- (char *) &idle, sizeof(idle)) < 0)
- {
- char sebuf[PG_STRERROR_R_BUFLEN];
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(%s) failed: %s\n"),
- PG_TCP_KEEPALIVE_IDLE_STR,
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
- #endif
- return 1;
- }
- /*
- * Set the keepalive interval.
- */
- static int
- setKeepalivesInterval(PGconn *conn)
- {
- int interval;
- if (conn->keepalives_interval == NULL)
- return 1;
- if (!parse_int_param(conn->keepalives_interval, &interval, conn,
- "keepalives_interval"))
- return 0;
- if (interval < 0)
- interval = 0;
- #ifdef TCP_KEEPINTVL
- if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL,
- (char *) &interval, sizeof(interval)) < 0)
- {
- char sebuf[PG_STRERROR_R_BUFLEN];
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(%s) failed: %s\n"),
- "TCP_KEEPINTVL",
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
- #endif
- return 1;
- }
- /*
- * Se…
Large files files are truncated, but you can click here to view the full file