/src/interfaces/libpq/fe-connect.c
C | 5774 lines | 3676 code | 633 blank | 1465 comment | 1043 complexity | a035210d30d20ad9f15dd4a9a6cd2de5 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-2014, 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/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <ctype.h>
- #include <time.h>
- #include <unistd.h>
- #include "libpq-fe.h"
- #include "libpq-int.h"
- #include "fe-auth.h"
- #include "pg_config_paths.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 WIN32_ONLY_COMPILER /* 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
- #include <arpa/inet.h>
- #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
- #include "libpq/ip.h"
- #include "mb/pg_wchar.h"
- #ifndef FD_CLOEXEC
- #define FD_CLOEXEC 1
- #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"
- /*
- * fall back options if they are not specified by arguments or defined
- * by environment variables
- */
- #define DefaultHost "localhost"
- #define DefaultTty ""
- #define DefaultOption ""
- #define DefaultAuthtype ""
- #define DefaultPassword ""
- #ifdef USE_SSL
- #define DefaultSSLMode "prefer"
- #else
- #define DefaultSSLMode "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)},
- {"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-Debug-Options", "D", 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)},
- /*
- * 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", "1", 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)},
- {"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)},
- #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
- /* Kerberos and GSSAPI authentication support specifying the service name */
- {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
- "Kerberos-service-name", "", 20,
- offsetof(struct pg_conn, krbsrvname)},
- #endif
- #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
- /*
- * GSSAPI and SSPI both enabled, give a way to override which is used by
- * default
- */
- {"gsslib", "PGGSSLIB", NULL, NULL,
- "GSS-library", "", 7, /* sizeof("gssapi") = 7 */
- offsetof(struct pg_conn, gsslib)},
- #endif
- {"replication", NULL, NULL, NULL,
- "Replication", "D", 5,
- offsetof(struct pg_conn, replication)},
- /* 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 void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
- static void freePGconn(PGconn *conn);
- static void closePGconn(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, 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);
- /* 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.
- */
- void
- pqDropConnection(PGconn *conn)
- {
- /* Drop any SSL state */
- pqsecure_close(conn);
- /* Close the socket itself */
- if (conn->sock >= 0)
- closesocket(conn->sock);
- conn->sock = -1;
- /* Discard any unread/unsent data */
- conn->inStart = conn->inCursor = conn->inEnd = 0;
- conn->outCount = 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
- */
- fillPGconn(conn, connOptions);
- /*
- * 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;
- }
- static void
- fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
- {
- const internalPQconninfoOption *option;
- /*
- * Move option values into conn structure
- *
- * Don't put anything cute here --- intelligence should be in
- * connectOptions2 ...
- *
- * XXX: probably worth checking strdup() return value here...
- */
- for (option = PQconninfoOptions; option->keyword; option++)
- {
- const char *tmp = conninfo_getval(connOptions, option->keyword);
- if (tmp && option->connofs >= 0)
- {
- char **connmember = (char **) ((char *) conn + option->connofs);
- if (*connmember)
- free(*connmember);
- *connmember = tmp ? strdup(tmp) : NULL;
- }
- }
- }
- /*
- * 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
- */
- fillPGconn(conn, connOptions);
- /*
- * Free the option info - all is in conn now
- */
- PQconninfoFree(connOptions);
- return true;
- }
- /*
- * 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)
- {
- /*
- * If database name was not given, default it to equal user name
- */
- if ((conn->dbName == NULL || conn->dbName[0] == '\0')
- && conn->pguser != NULL)
- {
- if (conn->dbName)
- free(conn->dbName);
- conn->dbName = strdup(conn->pguser);
- }
- /*
- * Supply default password if none given
- */
- if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
- {
- if (conn->pgpass)
- free(conn->pgpass);
- conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport,
- conn->dbName, conn->pguser);
- if (conn->pgpass == NULL)
- conn->pgpass = strdup(DefaultPassword);
- else
- conn->dot_pgpass_used = true;
- }
- /*
- * Allow unix socket specification in the host name
- */
- if (conn->pghost && is_absolute_path(conn->pghost))
- {
- if (conn->pgunixsocket)
- free(conn->pgunixsocket);
- conn->pgunixsocket = conn->pghost;
- conn->pghost = NULL;
- }
- /*
- * 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);
- /*
- * 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)));
- }
- /*
- * 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;
- }
- /*
- * 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);
- }
- }
- /*
- * 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 (pgport && pgport[0] != '\0')
- {
- if (conn->pgport)
- free(conn->pgport);
- conn->pgport = strdup(pgport);
- }
- if (pgoptions && pgoptions[0] != '\0')
- {
- if (conn->pgoptions)
- free(conn->pgoptions);
- conn->pgoptions = strdup(pgoptions);
- }
- if (pgtty && pgtty[0] != '\0')
- {
- if (conn->pgtty)
- free(conn->pgtty);
- conn->pgtty = strdup(pgtty);
- }
- if (login && login[0] != '\0')
- {
- if (conn->pguser)
- free(conn->pguser);
- conn->pguser = strdup(login);
- }
- if (pwd && pwd[0] != '\0')
- {
- if (conn->pgpass)
- free(conn->pgpass);
- conn->pgpass = strdup(pwd);
- }
- /*
- * Compute derived options
- */
- if (!connectOptions2(conn))
- return conn;
- /*
- * Connect to the database
- */
- if (connectDBStart(conn))
- (void) connectDBComplete(conn);
- 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[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;
- }
- #endif
- return 1;
- }
- /* ----------
- * connectFailureMessage -
- * create a friendly error message on connection failure.
- * ----------
- */
- static void
- connectFailureMessage(PGconn *conn, int errorno)
- {
- char sebuf[256];
- #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;
- struct sockaddr_storage *addr = &conn->raddr.addr;
- /*
- * Optionally display the network address with the hostname. This is
- * useful to distinguish between IPv4 and IPv6 connections.
- */
- if (conn->pghostaddr != NULL)
- strlcpy(host_addr, conn->pghostaddr, NI_MAXHOST);
- else if (addr->ss_family == AF_INET)
- {
- if (inet_net_ntop(AF_INET,
- &((struct sockaddr_in *) addr)->sin_addr.s_addr,
- 32,
- host_addr, sizeof(host_addr)) == NULL)
- strcpy(host_addr, "???");
- }
- #ifdef HAVE_IPV6
- else if (addr->ss_family == AF_INET6)
- {
- if (inet_net_ntop(AF_INET6,
- &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
- 128,
- host_addr, sizeof(host_addr)) == NULL)
- strcpy(host_addr, "???");
- }
- #endif
- else
- strcpy(host_addr, "???");
- if (conn->pghostaddr && conn->pghostaddr[0] != '\0')
- displayed_host = conn->pghostaddr;
- else if (conn->pghost && conn->pghost[0] != '\0')
- displayed_host = conn->pghost;
- else
- displayed_host = DefaultHost;
- /*
- * 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->pghostaddr == NULL) &&
- (conn->pghost == NULL || strcmp(conn->pghost, 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,
- conn->pgport);
- 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,
- conn->pgport);
- }
- }
- /*
- * 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;
- }
- #ifndef WIN32
- /*
- * Set the keepalive idle timer.
- */
- static int
- setKeepalivesIdle(PGconn *conn)
- {
- int idle;
- if (conn->keepalives_idle == NULL)
- return 1;
- idle = atoi(conn->keepalives_idle);
- if (idle < 0)
- idle = 0;
- #ifdef TCP_KEEPIDLE
- if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPIDLE,
- (char *) &idle, sizeof(idle)) < 0)
- {
- char sebuf[256];
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(TCP_KEEPIDLE) failed: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
- #else
- #ifdef TCP_KEEPALIVE
- /* Darwin uses TCP_KEEPALIVE rather than TCP_KEEPIDLE */
- if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPALIVE,
- (char *) &idle, sizeof(idle)) < 0)
- {
- char sebuf[256];
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(TCP_KEEPALIVE) failed: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
- #endif
- #endif
- return 1;
- }
- /*
- * Set the keepalive interval.
- */
- static int
- setKeepalivesInterval(PGconn *conn)
- {
- int interval;
- if (conn->keepalives_interval == NULL)
- return 1;
- interval = atoi(conn->keepalives_interval);
- if (interval < 0)
- interval = 0;
- #ifdef TCP_KEEPINTVL
- if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL,
- (char *) &interval, sizeof(interval)) < 0)
- {
- char sebuf[256];
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(TCP_KEEPINTVL) failed: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
- #endif
- return 1;
- }
- /*
- * Set the count of lost keepalive packets that will trigger a connection
- * break.
- */
- static int
- setKeepalivesCount(PGconn *conn)
- {
- int count;
- if (conn->keepalives_count == NULL)
- return 1;
- count = atoi(conn->keepalives_count);
- if (count < 0)
- count = 0;
- #ifdef TCP_KEEPCNT
- if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPCNT,
- (char *) &count, sizeof(count)) < 0)
- {
- char sebuf[256];
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(TCP_KEEPCNT) failed: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
- #endif
- return 1;
- }
- #else /* Win32 */
- #ifdef SIO_KEEPALIVE_VALS
- /*
- * Enable keepalives and set the keepalive values on Win32,
- * where they are always set in one batch.
- */
- static int
- setKeepalivesWin32(PGconn *conn)
- {
- struct tcp_keepalive ka;
- DWORD retsize;
- int idle = 0;
- int interval = 0;
- if (conn->keepalives_idle)
- idle = atoi(conn->keepalives_idle);
- if (idle <= 0)
- idle = 2 * 60 * 60; /* 2 hours = default */
- if (conn->keepalives_interval)
- interval = atoi(conn->keepalives_interval);
- if (interval <= 0)
- interval = 1; /* 1 second = default */
- ka.onoff = 1;
- ka.keepalivetime = idle * 1000;
- ka.keepaliveinterval = interval * 1000;
- if (WSAIoctl(conn->sock,
- SIO_KEEPALIVE_VALS,
- (LPVOID) &ka,
- sizeof(ka),
- NULL,
- 0,
- &retsize,
- NULL,
- NULL)
- != 0)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"),
- WSAGetLastError());
- return 0;
- }
- return 1;
- }
- #endif /* SIO_KEEPALIVE_VALS */
- #endif /* WIN32 */
- /* ----------
- * connectDBStart -
- * Begin the process of making a connection to the backend.
- *
- * Returns 1 if successful, 0 if not.
- * ----------
- */
- static int
- connectDBStart(PGconn *conn)
- {
- int portnum;
- char portstr[MAXPGPATH];
- struct addrinfo *addrs = NULL;
- struct addrinfo hint;
- const char *node;
- int ret;
- if (!conn)
- return 0;
- if (!conn->options_valid)
- goto connect_errReturn;
- /* Ensure our buffers are empty */
- conn->inStart = conn->inCursor = conn->inEnd = 0;
- conn->outCount = 0;
- /*
- * Determine the parameters to pass to pg_getaddrinfo_all.
- */
- /* Initialize hint structure */
- MemSet(&hint, 0, sizeof(hint));
- hint.ai_socktype = SOCK_STREAM;
- hint.ai_family = AF_UNSPEC;
- /* Set up port number as a string */
- if (conn->pgport != NULL && conn->pgport[0] != '\0')
- {
- portnum = atoi(conn->pgport);
- if (portnum < 1 || portnum > 65535)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid port number: \"%s\"\n"),
- conn->pgport);
- conn->options_valid = false;
- goto connect_errReturn;
- }
- }
- else
- portnum = DEF_PGPORT;
- snprintf(portstr, sizeof(portstr), "%d", portnum);
- if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
- {
- /* Using pghostaddr avoids a hostname lookup */
- node = conn->pghostaddr;
- hint.ai_family = AF_UNSPEC;
- hint.ai_flags = AI_NUMERICHOST;
- }
- else if (conn->pghost != NULL && conn->pghost[0] != '\0')
- {
- /* Using pghost, so we have to look-up the hostname */
- node = conn->pghost;
- hint.ai_family = AF_UNSPEC;
- }
- else
- {
- #ifdef HAVE_UNIX_SOCKETS
- /* pghostaddr and pghost are NULL, so use Unix domain socket */
- node = NULL;
- hint.ai_family = AF_UNIX;
- UNIXSOCK_PATH(portstr, portnum, conn->pgunixsocket);
- if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"),
- portstr,
- (int) (UNIXSOCK_PATH_BUFLEN - 1));
- conn->options_valid = false;
- goto connect_errReturn;
- }
- #else
- /* Without Unix sockets, default to localhost instead */
- node = DefaultHost;
- hint.ai_family = AF_UNSPEC;
- #endif /* HAVE_UNIX_SOCKETS */
- }
- /* Use pg_getaddrinfo_all() to resolve the address */
- ret = pg_getaddrinfo_all(node, portstr, &hint, &addrs);
- 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)
- pg_freeaddrinfo_all(hint.ai_family, addrs);
- conn->options_valid = false;
- goto connect_errReturn;
- }
- #ifdef USE_SSL
- /* setup values based on SSL mode */
- if (conn->sslmode[0] == 'd') /* "disable" */
- conn->allow_ssl_try = false;
- else if (conn->sslmode[0] == 'a') /* "allow" */
- conn->wait_ssl_try = true;
- #endif
- /*
- * Set up to try to connect, with protocol 3.0 as the first attempt.
- */
- conn->addrlist = addrs;
- conn->addr_cur = addrs;
- conn->addrlist_family = hint.ai_family;
- conn->pversion = PG_PROTOCOL(3, 0);
- conn->send_appname = true;
- conn->status = CONNECTION_NEEDED;
- /*
- * The code for processing CONNECTION_NEEDED state is in PQconnectPoll(),
- * so that it can easily be re-executed if needed again during the
- * asynchronous startup process. However, we must run it once here,
- * because callers expect a success return from this routine to mean that
- * we are in PGRES_POLLING_WRITING connection state.
- */
- if (PQconnectPoll(conn) == PGRES_POLLING_WRITING)
- return 1;
- connect_errReturn:
- pqDropConnection(conn);
- conn->status = CONNECTION_BAD;
- return 0;
- }
- /*
- * connectDBComplete
- *
- * Block and complete a connection.
- *
- * Returns 1 on success, 0 on failure.
- */
- static int
- connectDBComplete(PGconn *conn)
- {
- PostgresPollingStatusType flag = PGRES_POLLING_WRITING;
- time_t finish_time = ((time_t) -1);
- if (conn == NULL || conn->status == CONNECTION_BAD)
- return 0;
- /*
- * Set up a time limit, if connect_timeout isn't zero.
- */
- if (conn->connect_timeout != NULL)
- {
- int timeout = atoi(conn->connect_timeout);
- if (timeout > 0)
- {
- /*
- * Rounding could cause connection to fail; need at least 2 secs
- */
- if (timeout < 2)
- timeout = 2;
- /* calculate the finish time based on start + timeout */
- finish_time = time(NULL) + timeout;
- }
- }
- for (;;)
- {
- /*
- * Wait, if necessary. Note that the initial state (just after
- * PQconnectStart) is to wait for the socket to select for writing.
- */
- 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:
- if (pqWaitTimed(1, 0, conn, finish_time))
- {
- conn->status = CONNECTION_BAD;
- return 0;
- }
- break;
- case PGRES_POLLING_WRITING:
- if (pqWaitTimed(0, 1, conn, finish_time))
- {
- conn->status = CONNECTION_BAD;
- return 0;
- }
- break;
- default:
- /* Just in case we failed to set it in PQconnectPoll */
- conn->status = CONNECTION_BAD;
- return 0;
- }
- /*
- * Now try to advance the state machine.
- */
- flag = PQconnectPoll(conn);
- }
- }
- /* ----------------
- * PQconnectPoll
- *
- * Poll an asynchronous connection.
- *
- * Returns a PostgresPollingStatusType.
- * Before calling this function, use select(2) to determine when data
- * has arrived..
- *
- * You must call PQfinish whether or not this fails.
- *
- * This function and PQconnectStart are intended to allow connections to be
- * made without blocking the execution of your program on remote I/O. However,
- * there are a number of caveats:
- *
- * o If you call PQtrace, ensure that the stream object into which you trace
- * will not block.
- * o If you do not supply an IP address for the remote host (i.e. you
- * supply a host name instead) then PQconnectStart will block on
- * gethostbyname. You will be fine if using Unix sockets (i.e. by
- * supplying neither a host name nor a host address).
- * o If your backend wants to use Kerberos authentication then you must
- * supply both a host name and a host address, otherwise this function
- * may block on gethostname.
- *
- * ----------------
- */
- PostgresPollingStatusType
- PQconnectPoll(PGconn *conn)
- {
- PGresult *res;
- char sebuf[256];
- int optval;
- if (conn == NULL)
- return PGRES_POLLING_FAILED;
- /* Get the new data */
- switch (conn->status)
- {
- /*
- * We really shouldn't have been polled in these two cases, but we
- * can handle it.
- */
- case CONNECTION_BAD:
- return PGRES_POLLING_FAILED;
- case CONNECTION_OK:
- return PGRES_POLLING_OK;
- /* These are reading states */
- case CONNECTION_AWAITING_RESPONSE:
- case CONNECTION_AUTH_OK:
- {
- /* Load waiting data */
- int n = pqReadData(conn);
- if (n < 0)
- goto error_return;
- if (n == 0)
- return PGRES_POLLING_READING;
- break;
- }
- /* These are writing states, so we just proceed. */
- case CONNECTION_STARTED:
- case CONNECTION_MADE:
- break;
- /* We allow pqSetenvPoll to decide whether to proceed. */
- case CONNECTION_SETENV:
- break;
- /* Special cases: proceed without waiting. */
- case CONNECTION_SSL_STARTUP:
- case CONNECTION_NEEDED:
- break;
- default:
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext(
- "invalid connection state, "
- "probably indicative of memory corruption\n"
- ));
- goto error_return;
- }
- keep_going: /* We will come back to here until there is
- * nothing left to do. */
- switch (conn->status)
- {
- case CONNECTION_NEEDED:
- {
- /*
- * Try to initiate a connection to one of the addresses
- * returned by pg_getaddrinfo_all(). conn->addr_cur is the
- * next one to try. We fail when we run out of addresses.
- */
- while (conn->addr_cur != NULL)
- {
- struct addrinfo *addr_cur = conn->addr_cur;
- /* Remember current address for possible error msg */
- memcpy(&conn->raddr.addr, addr_cur->ai_addr,
- addr_cur->ai_addrlen);
- conn->raddr.salen = addr_cur->ai_addrlen;
- /* Open a socket */
- conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
- if (conn->sock < 0)
- {
- /*
- * ignore socket() failure if we have more addresses
- * to try
- */
- if (addr_cur->ai_next != NULL)
- {
- 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;
- }
- /*
- * Select socket options: no delay of outgoing data for
- * TCP sockets, nonblock mode, close-on-exec. Fail if any
- * of this fails.
- */
- if (!IS_AF_UNIX(addr_cur->ai_family))
- {
- if (!connectNoDelay(conn))
- {
- pqDropConnection(conn);
- conn->addr_cur = addr_cur->ai_next;
- continue;
- }
- }
- if (!pg_set_noblock(conn->sock))
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not set socket to nonblocking mode: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- pqDropConnection(conn);
- conn->addr_cur = addr_cur->ai_next;
- continue;
- }
- #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)));
- pqDropConnection(conn);
- conn->addr_cur = addr_cur->ai_next;
- continue;
- }
- #endif /* F_SETFD */
- if (!IS_AF_UNIX(addr_cur->ai_family))
- {
- #ifndef WIN32
- int on = 1;
- #endif
- int usekeepalives = useKeepalives(conn);
- int err = 0;
- if (usekeepalives < 0)
- {
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("keepalives parameter must be an integer\n"));
- err = 1;
- }
- else if (usekeepalives == 0)
- {
- /* Do nothing */
- }
- #ifndef WIN32
- else if (setsockopt(conn->sock,
- SOL_SOCKET, SO_KEEPALIVE,
- (char *) &on, sizeof(on)) < 0)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(SO_KEEPALIVE) failed: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- err = 1;
- }
- else if (!setKeepalivesIdle(conn)
- || !setKeepalivesInterval(conn)
- || !setKeepalivesCount(conn))
- err = 1;
- #else /* WIN32 */
- #ifdef SIO_KEEPALIVE_VALS
- else if (!setKeepalivesWin32(conn))
- err = 1;
- #endif /* SIO_KEEPALIVE_VALS */
- #endif /* WIN32 */
- if (err)
- {
- pqDropConnection(conn);
- conn->addr_cur = addr_cur->ai_next;
- continue;
- }
- }
- /*----------
- * We have three methods of blocking SIGPIPE during
- * send() calls to this socket:
- *
- * - setsockopt(sock, SO_NOSIGPIPE)
- * - send(sock, ..., MSG_NOSIGNAL)
- * - setting the signal mask to SIG_IGN during send()
- *
- * The third method requires three syscalls per send,
- * so we prefer either of the first two, but they are
- * less portable. The state is tracked in the following
- * members of PGconn:
- *
- * conn->sigpipe_so - we have set up SO_NOSIGPIPE
- * conn->sigpipe_flag - we're specifying MSG_NOSIGNAL
- *
- * If we can use SO_NOSIGPIPE, then set sigpipe_so here
- * and we're done. Otherwise, set sigpipe_flag so that
- * we will try MSG_NOSIGNAL on sends. If we get an error
- * with MSG_NOSIGNAL, we'll clear that flag and revert to
- * signal masking.
- *----------
- */
- conn->sigpipe_so = false;
- #ifdef MSG_NOSIGNAL
- conn->sigpipe_flag = true;
- #else
- conn->sigpipe_flag = false;
- #endif /* MSG_NOSIGNAL */
- #ifdef SO_NOSIGPIPE
- optval = 1;
- if (setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE,
- (char *) &optval, sizeof(optval)) == 0)
- {
- conn->sigpipe_so = true;
- conn->sigpipe_flag = false;
- }
- #endif /* SO_NOSIGPIPE */
- /*
- * Start/make connection. This should not block, since we
- * are in nonblock mode. If it does, well, too bad.
- */
- if (connect(conn->sock, addr_cur->ai_addr,
- addr_cur->ai_addrlen) < 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. Tell caller to
- * wait for write-ready on socket.
- */
- conn->status = CONNECTION_STARTED;
- return PGRES_POLLING_WRITING;
- }
- /* otherwise, trouble */
- }
- else
- {
- /*
- * Hm, we're connected already --- seems the "nonblock
- * connection" wasn't. Advance the state machine and
- * go do the next stuff.
- */
- conn->status = CONNECTION_STARTED;
- goto keep_going;
- }
- /*
- * This connection failed --- set up error report, then
- * close socket (do it this way in case close() affects
- * the value of errno...). We will ignore the connect()
- * failure and keep going if there are more addresses.
- */
- connectFailureMessage(conn, SOCK_ERRNO);
- pqDropConnection(conn);
- /*
- * Try the next address, if any.
- */
- conn->addr_cur = addr_cur->ai_next;
- } /* loop over addresses */
- /*
- * Ooops, no more addresses. An appropriate error message is
- * already set up, so just set the right status.
- */
- goto error_return;
- }
- case CONNECTION_STARTED:
- {
- ACCEPT_TYPE_ARG3 optlen = sizeof(optval);
- /*
- * Write ready, since we've made it here, so the connection
- * has been made ... or has failed.
- */
- /*
- * Now check (using getsockopt) that there is not an error
- * state waiting for us on the socket.
- */
- 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;
- }
- else if (optval != 0)
- {
- /*
- * When using a nonblocking connect, we will typically see
- * connect failures at this point, so provide a friendly
- * error message.
- */
- connectFailureMessage(conn, optval);
- pqDropConnection(conn);
- /*
- * If more addresses remain, keep trying, just as in the
- * case where connect() returned failure immediately.
- */
- if (conn->addr_cur->ai_next != NULL)
- {
- conn->addr_cur = conn->addr_cur->ai_next;
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
- goto error_return;
- }
- /* Fill in the client address */
- conn->laddr.salen = sizeof(conn->laddr.addr);
- if (getsockname(conn->sock,
- (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;
- }
- /*
- * Make sure we can write before advancing to next step.
- */
- conn->status = CON…
Large files files are truncated, but you can click here to view the full file