with variable-width fields. No more truncation of long user names.
Also, libpq can now send its environment-variable-driven SET commands
as part of the startup packet, saving round trips to server.
<!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.117 2003/03/25 16:15:37 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.118 2003/04/17 22:26:00 tgl Exp $
-->
<chapter id="libpq">
<term><literal>tty</literal></term>
<listitem>
<para>
- A file or <acronym>TTY</acronym> for optional debug output from the server.
+ Ignored (formerly, this specified where to send server debug output).
</para>
</listitem>
</varlistentry>
<listitem>
<para>
Returns the debug <acronym>TTY</acronym> of the connection.
+ (This is obsolete, since the server no longer pays attention
+ to the <acronym>TTY</acronym> setting, but the function remains
+ for backwards compatibility.)
<synopsis>
char *PQtty(const PGconn *conn);
</synopsis>
</listitem>
<listitem>
<para>
-<envar>PGTTY</envar> sets the file or <acronym>TTY</> on which debugging
-messages from the server are displayed.
-</para>
-</listitem>
-<listitem>
-<para>
<envar>PGREQUIRESSL</envar> sets whether or not the connection must be
made over <acronym>SSL</acronym>. If set to
<quote>1</quote>, <application>libpq</>
pgport = NULL; /* port of the backend server */
pgoptions = NULL; /* special options to start up the backend
* server */
- pgtty = NULL; /* debugging tty for the backend server */
+ pgtty = NULL; /* unused */
dbName = "template1";
/* make a connection to the database */
pgport = NULL; /* port of the backend server */
pgoptions = NULL; /* special options to start up the backend
* server */
- pgtty = NULL; /* debugging tty for the backend server */
+ pgtty = NULL; /* unused */
dbName = getenv("USER"); /* change this to the name of your test
* database */
pgport = NULL; /* port of the backend server */
pgoptions = NULL; /* special options to start up the backend
* server */
- pgtty = NULL; /* debugging tty for the backend server */
+ pgtty = NULL; /* unused */
dbName = getenv("USER"); /* change this to the name of your test
* database */
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.97 2003/02/14 14:05:00 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.98 2003/04/17 22:26:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "libpq/crypt.h"
#include "libpq/hba.h"
#include "libpq/libpq.h"
-#include "libpq/password.h"
#include "libpq/pqcomm.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
}
elog(FATAL, "%s authentication failed for user \"%s\"",
- authmethod, port->user);
+ authmethod, port->user_name);
/* doesn't return */
}
elog(FATAL,
"No pg_hba.conf entry for host %s, user %s, database %s",
- hostinfo, port->user, port->database);
+ hostinfo, port->user_name, port->database_name);
break;
}
* not allocated */
/* Optionally, one can set the service name in pg_hba.conf */
- if (port->auth_arg[0] == '\0')
- retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@", &pam_passw_conv, &pamh);
+ if (port->auth_arg && port->auth_arg[0] != '\0')
+ retval = pam_start(port->auth_arg, "pgsql@",
+ &pam_passw_conv, &pamh);
else
- retval = pam_start(port->auth_arg, "pgsql@", &pam_passw_conv, &pamh);
+ retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@",
+ &pam_passw_conv, &pamh);
if (retval != PAM_SUCCESS)
{
/* Do not echo password to logs, for security. */
elog(DEBUG5, "received password packet");
- result = md5_crypt_verify(port, port->user, buf.data);
+ result = md5_crypt_verify(port, port->user_name, buf.data);
pfree(buf.data);
return result;
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.51 2002/12/05 18:52:42 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.52 2003/04/17 22:26:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* pg_shadow plain, double-encrypt */
char *crypt_pwd2 = palloc(MD5_PASSWD_LEN + 1);
- if (!EncryptMD5(shadow_pass, port->user, strlen(port->user),
+ if (!EncryptMD5(shadow_pass,
+ port->user_name,
+ strlen(port->user_name),
crypt_pwd2))
{
pfree(crypt_pwd);
pfree(crypt_pwd2);
return STATUS_ERROR;
}
- if (!EncryptMD5(crypt_pwd2 + strlen("md5"), port->md5Salt,
- sizeof(port->md5Salt), crypt_pwd))
+ if (!EncryptMD5(crypt_pwd2 + strlen("md5"),
+ port->md5Salt,
+ sizeof(port->md5Salt),
+ crypt_pwd))
{
pfree(crypt_pwd);
pfree(crypt_pwd2);
{
/* Encrypt user-supplied password to match MD5 in pg_shadow */
crypt_client_pass = palloc(MD5_PASSWD_LEN + 1);
- if (!EncryptMD5(client_pass, port->user, strlen(port->user),
+ if (!EncryptMD5(client_pass,
+ port->user_name,
+ strlen(port->user_name),
crypt_client_pass))
{
pfree(crypt_client_pass);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.98 2003/04/13 04:07:17 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.99 2003/04/17 22:26:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Scan the rest of a host record (after the mask field)
- * and return the interpretation of it as *userauth_p, auth_arg, and
+ * and return the interpretation of it as *userauth_p, *auth_arg_p, and
* *error_p. line points to the next token of the line.
*/
static void
-parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg,
+parse_hba_auth(List *line, UserAuth *userauth_p, char **auth_arg_p,
bool *error_p)
{
char *token;
+ *auth_arg_p = NULL;
+
if (!line)
*error_p = true;
else
if (!*error_p)
{
/* Get the authentication argument token, if any */
- if (!line)
- auth_arg[0] = '\0';
- else
+ if (line)
{
- StrNCpy(auth_arg, lfirst(line), MAX_AUTH_ARG - 1);
+ token = lfirst(line);
+ *auth_arg_p = pstrdup(token);
/* If there is more on the line, it is an error */
if (lnext(line))
*error_p = true;
goto hba_syntax;
/* Read the rest of the line. */
- parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
+ parse_hba_auth(line, &port->auth_method, &port->auth_arg, error_p);
if (*error_p)
goto hba_syntax;
line = lnext(line);
if (!line)
goto hba_syntax;
- parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
+ parse_hba_auth(line, &port->auth_method, &port->auth_arg, error_p);
if (*error_p)
goto hba_syntax;
else
goto hba_syntax;
- if (!check_db(port->database, port->user, db))
+ if (!check_db(port->database_name, port->user_name, db))
return;
- if (!check_user(port->user, user))
+ if (!check_user(port->user_name, user))
return;
/* Success */
bool found_entry = false,
error = false;
- if (usermap_name[0] == '\0')
+ if (usermap_name == NULL || usermap_name[0] == '\0')
{
elog(LOG, "check_ident_usermap: hba configuration file does not "
"have the usermap field filled in in the entry that pertains "
return STATUS_ERROR;
}
- if (check_ident_usermap(port->auth_arg, port->user, ident_user))
+ if (check_ident_usermap(port->auth_arg, port->user_name, ident_user))
return STATUS_OK;
else
return STATUS_ERROR;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.310 2003/04/06 22:45:22 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.311 2003/04/17 22:26:01 tgl Exp $
*
* NOTES
*
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "bootstrap/bootstrap.h"
-
#include "pgstat.h"
+
#define INVALID_SOCK (-1)
-#define ARGV_SIZE 64
#ifdef HAVE_SIGPROCMASK
sigset_t UnBlockSig,
static int
ProcessStartupPacket(Port *port, bool SSLdone)
{
- StartupPacket *packet;
enum CAC_state cac;
int32 len;
void *buf;
+ ProtocolVersion proto;
+ MemoryContext oldcontext;
if (pq_getbytes((char *) &len, 4) == EOF)
{
len = ntohl(len);
len -= 4;
- if (len < sizeof(ProtocolVersion) || len > sizeof(StartupPacket))
+ if (len < (int32) sizeof(ProtocolVersion) ||
+ len > MAX_STARTUP_PACKET_LENGTH)
elog(FATAL, "invalid length of startup packet");
- /* Ensure we see zeroes for any bytes not sent */
- buf = palloc0(sizeof(StartupPacket));
+ /*
+ * Allocate at least the size of an old-style startup packet, plus one
+ * extra byte, and make sure all are zeroes. This ensures we will have
+ * null termination of all strings, in both fixed- and variable-length
+ * packet layouts.
+ */
+ if (len <= (int32) sizeof(StartupPacket))
+ buf = palloc0(sizeof(StartupPacket) + 1);
+ else
+ buf = palloc0(len + 1);
if (pq_getbytes(buf, len) == EOF)
{
return STATUS_ERROR;
}
- packet = buf;
-
/*
* The first field is either a protocol version number or a special
* request code.
*/
- port->proto = ntohl(packet->protoVersion);
+ port->proto = proto = ntohl(*((ProtocolVersion *) buf));
- if (port->proto == CANCEL_REQUEST_CODE)
+ if (proto == CANCEL_REQUEST_CODE)
{
- processCancelRequest(port, packet);
+ processCancelRequest(port, buf);
return 127; /* XXX */
}
- if (port->proto == NEGOTIATE_SSL_CODE && !SSLdone)
+ if (proto == NEGOTIATE_SSL_CODE && !SSLdone)
{
char SSLok;
/* Check we can handle the protocol the frontend is using. */
- if (PG_PROTOCOL_MAJOR(port->proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
- PG_PROTOCOL_MAJOR(port->proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
- (PG_PROTOCOL_MAJOR(port->proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
- PG_PROTOCOL_MINOR(port->proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
- elog(FATAL, "unsupported frontend protocol");
+ if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
+ PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
+ (PG_PROTOCOL_MAJOR(proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
+ PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
+ elog(FATAL, "unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u",
+ PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto),
+ PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST),
+ PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
+ PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST));
/*
- * Get the parameters from the startup packet as C strings. The
- * packet destination was cleared first so a short packet has zeros
- * silently added.
+ * XXX temporary for 3.0 protocol development: we are using the minor
+ * number as a test-version number. Insist it match exactly so people
+ * don't get burnt by using yesterday's libpq with today's server.
+ * XXX this must go away before release!!!
*/
- StrNCpy(port->database, packet->database, sizeof(port->database));
- StrNCpy(port->user, packet->user, sizeof(port->user));
- StrNCpy(port->options, packet->options, sizeof(port->options));
- StrNCpy(port->tty, packet->tty, sizeof(port->tty));
-
- /* The database defaults to the user name. */
- if (port->database[0] == '\0')
- StrNCpy(port->database, packet->user, sizeof(port->database));
+ if (PG_PROTOCOL_MAJOR(proto) == 3 &&
+ PG_PROTOCOL_MINOR(proto) != PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))
+ elog(FATAL, "Your development libpq is out of sync with the server");
/*
- * Truncate given database and user names to length of a Postgres
- * name. This avoids lookup failures when overlength names are given.
+ * Now fetch parameters out of startup packet and save them into the
+ * Port structure. All data structures attached to the Port struct
+ * must be allocated in TopMemoryContext so that they won't disappear
+ * when we pass them to PostgresMain (see DoBackend). We need not worry
+ * about leaking this storage on failure, since we aren't in the postmaster
+ * process anymore.
*/
- if ((int) sizeof(port->database) >= NAMEDATALEN)
- port->database[NAMEDATALEN - 1] = '\0';
- if ((int) sizeof(port->user) >= NAMEDATALEN)
- port->user[NAMEDATALEN - 1] = '\0';
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+ if (PG_PROTOCOL_MAJOR(proto) >= 3)
+ {
+ int32 offset = sizeof(ProtocolVersion);
+
+ /*
+ * Scan packet body for name/option pairs. We can assume any
+ * string beginning within the packet body is null-terminated,
+ * thanks to zeroing extra byte above.
+ */
+ port->guc_options = NIL;
+
+ while (offset < len)
+ {
+ char *nameptr = ((char *) buf) + offset;
+ int32 valoffset;
+ char *valptr;
+
+ if (*nameptr == '\0')
+ break; /* found packet terminator */
+ valoffset = offset + strlen(nameptr) + 1;
+ if (valoffset >= len)
+ break; /* missing value, will complain below */
+ valptr = ((char *) buf) + valoffset;
+
+ if (strcmp(nameptr, "database") == 0)
+ port->database_name = pstrdup(valptr);
+ else if (strcmp(nameptr, "user") == 0)
+ port->user_name = pstrdup(valptr);
+ else if (strcmp(nameptr, "options") == 0)
+ port->cmdline_options = pstrdup(valptr);
+ else
+ {
+ /* Assume it's a generic GUC option */
+ port->guc_options = lappend(port->guc_options,
+ pstrdup(nameptr));
+ port->guc_options = lappend(port->guc_options,
+ pstrdup(valptr));
+ }
+ offset = valoffset + strlen(valptr) + 1;
+ }
+ /*
+ * If we didn't find a packet terminator exactly at the end of the
+ * given packet length, complain.
+ */
+ if (offset != len-1)
+ elog(FATAL, "invalid startup packet layout: expected terminator as last byte");
+ }
+ else
+ {
+ /*
+ * Get the parameters from the old-style, fixed-width-fields startup
+ * packet as C strings. The packet destination was cleared first so a
+ * short packet has zeros silently added. We have to be prepared to
+ * truncate the pstrdup result for oversize fields, though.
+ */
+ StartupPacket *packet = (StartupPacket *) buf;
+
+ port->database_name = pstrdup(packet->database);
+ if (strlen(port->database_name) > sizeof(packet->database))
+ port->database_name[sizeof(packet->database)] = '\0';
+ port->user_name = pstrdup(packet->user);
+ if (strlen(port->user_name) > sizeof(packet->user))
+ port->user_name[sizeof(packet->user)] = '\0';
+ port->cmdline_options = pstrdup(packet->options);
+ if (strlen(port->cmdline_options) > sizeof(packet->options))
+ port->cmdline_options[sizeof(packet->options)] = '\0';
+ port->guc_options = NIL;
+ }
/* Check a user name was given. */
- if (port->user[0] == '\0')
+ if (port->user_name == NULL || port->user_name[0] == '\0')
elog(FATAL, "no PostgreSQL user name specified in startup packet");
+ /* The database defaults to the user name. */
+ if (port->database_name == NULL || port->database_name[0] == '\0')
+ port->database_name = pstrdup(port->user_name);
+
if (Db_user_namespace)
{
/*
* string or they may fake as a local user of another database
* attaching to this database.
*/
- if (strchr(port->user, '@') == port->user + strlen(port->user) - 1)
- *strchr(port->user, '@') = '\0';
+ if (strchr(port->user_name, '@') ==
+ port->user_name + strlen(port->user_name) - 1)
+ *strchr(port->user_name, '@') = '\0';
else
{
/* Append '@' and dbname */
- char hold_user[SM_DATABASE_USER + 1];
+ char *db_user;
- snprintf(hold_user, SM_DATABASE_USER + 1, "%s@%s", port->user,
- port->database);
- strcpy(port->user, hold_user);
+ db_user = palloc(strlen(port->user_name) +
+ strlen(port->database_name) + 2);
+ sprintf(db_user, "%s@%s", port->user_name, port->database_name);
+ port->user_name = db_user;
}
}
+ /*
+ * Truncate given database and user names to length of a Postgres
+ * name. This avoids lookup failures when overlength names are given.
+ */
+ if (strlen(port->database_name) >= NAMEDATALEN)
+ port->database_name[NAMEDATALEN - 1] = '\0';
+ if (strlen(port->user_name) >= NAMEDATALEN)
+ port->user_name[NAMEDATALEN - 1] = '\0';
+
+ /*
+ * Done putting stuff in TopMemoryContext.
+ */
+ MemoryContextSwitchTo(oldcontext);
+
/*
* If we're going to reject the connection due to database state, say
* so now instead of wasting cycles on an authentication exchange.
DoBackend(Port *port)
{
char *remote_host;
- char *av[ARGV_SIZE * 2];
- int ac = 0;
- char debugbuf[ARGV_SIZE];
- char protobuf[ARGV_SIZE];
- char dbbuf[ARGV_SIZE];
- char optbuf[ARGV_SIZE];
- char ttybuf[ARGV_SIZE];
+ char **av;
+ int maxac;
+ int ac;
+ char debugbuf[32];
+ char protobuf[32];
int i;
int status;
struct timeval now;
* title for ps. It's good to do this as early as possible in
* startup.
*/
- init_ps_display(port->user, port->database, remote_host);
+ init_ps_display(port->user_name, port->database_name, remote_host);
set_ps_display("authentication");
/*
if (Log_connections)
elog(LOG, "connection authorized: user=%s database=%s",
- port->user, port->database);
+ port->user_name, port->database_name);
/*
* Don't want backend to be able to see the postmaster random number
* The layout of the command line is
* postgres [secure switches] -p databasename [insecure switches]
* where the switches after -p come from the client request.
+ *
+ * The maximum possible number of commandline arguments that could come
+ * from ExtraOptions or port->cmdline_options is (strlen + 1) / 2; see
+ * split_opts().
* ----------------
*/
+ maxac = 10; /* for fixed args supplied below */
+ maxac += (strlen(ExtraOptions) + 1) / 2;
+ if (port->cmdline_options)
+ maxac += (strlen(port->cmdline_options) + 1) / 2;
+
+ av = (char **) MemoryContextAlloc(TopMemoryContext,
+ maxac * sizeof(char *));
+ ac = 0;
av[ac++] = "postgres";
*/
if (debug_flag > 0)
{
- sprintf(debugbuf, "-d%d", debug_flag);
+ snprintf(debugbuf, sizeof(debugbuf), "-d%d", debug_flag);
av[ac++] = debugbuf;
}
split_opts(av, &ac, ExtraOptions);
/* Tell the backend what protocol the frontend is using. */
- sprintf(protobuf, "-v%u", port->proto);
+ snprintf(protobuf, sizeof(protobuf), "-v%u", port->proto);
av[ac++] = protobuf;
/*
* database to use. -p marks the end of secure switches.
*/
av[ac++] = "-p";
-
- StrNCpy(dbbuf, port->database, ARGV_SIZE);
- av[ac++] = dbbuf;
+ av[ac++] = port->database_name;
/*
* Pass the (insecure) option switches from the connection request.
+ * (It's OK to mangle port->cmdline_options now.)
*/
- StrNCpy(optbuf, port->options, ARGV_SIZE);
- split_opts(av, &ac, optbuf);
-
- /*
- * Pass the (insecure) debug output file request.
- *
- * NOTE: currently, this is useless code, since the backend will not
- * honor an insecure -o switch. I left it here since the backend
- * could be modified to allow insecure -o, given adequate checking
- * that the specified filename is something safe to write on.
- */
- if (port->tty[0])
- {
- StrNCpy(ttybuf, port->tty, ARGV_SIZE);
- av[ac++] = "-o";
- av[ac++] = ttybuf;
- }
+ if (port->cmdline_options)
+ split_opts(av, &ac, port->cmdline_options);
av[ac] = (char *) NULL;
+ Assert(ac < maxac);
+
/*
* Release postmaster's working memory context so that backend can
* recycle the space. Note this does not trash *MyProcPort, because
* ConnCreate() allocated that space with malloc() ... else we'd need
- * to copy the Port data here.
+ * to copy the Port data here. Also, subsidiary data such as the
+ * username isn't lost either; see ProcessStartupPacket().
*/
MemoryContextSwitchTo(TopMemoryContext);
MemoryContextDelete(PostmasterContext);
ClientAuthInProgress = false; /* client_min_messages is active
* now */
- return (PostgresMain(ac, av, port->user));
+ return (PostgresMain(ac, av, port->user_name));
}
/*
if ((pid = fork()) == 0) /* child */
{
const char *statmsg;
- char *av[ARGV_SIZE * 2];
+ char *av[10];
int ac = 0;
- char nbbuf[ARGV_SIZE];
- char dbbuf[ARGV_SIZE];
- char xlbuf[ARGV_SIZE];
+ char nbbuf[32];
+ char xlbuf[32];
#ifdef LINUX_PROFILE
setitimer(ITIMER_PROF, &prof_itimer, NULL);
/* Set up command-line arguments for subprocess */
av[ac++] = "postgres";
- sprintf(nbbuf, "-B%d", NBuffers);
+ snprintf(nbbuf, sizeof(nbbuf), "-B%d", NBuffers);
av[ac++] = nbbuf;
- sprintf(xlbuf, "-x%d", xlop);
+ snprintf(xlbuf, sizeof(xlbuf), "-x%d", xlop);
av[ac++] = xlbuf;
av[ac++] = "-p";
-
- StrNCpy(dbbuf, "template1", ARGV_SIZE);
- av[ac++] = dbbuf;
+ av[ac++] = "template1";
av[ac] = (char *) NULL;
+ Assert(ac < lengthof(av));
+
BootstrapMain(ac, av);
ExitPostmaster(0);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.320 2003/03/24 18:33:52 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.321 2003/04/17 22:26:01 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
if (debug_flag >= 5)
SetConfigOption("debug_print_rewritten", "true", ctx, gucsource);
+ /*
+ * Process any additional GUC variable settings passed in startup packet.
+ */
+ if (MyProcPort != NULL)
+ {
+ List *gucopts = MyProcPort->guc_options;
+
+ while (gucopts)
+ {
+ char *name,
+ *value;
+
+ name = lfirst(gucopts);
+ gucopts = lnext(gucopts);
+ value = lfirst(gucopts);
+ gucopts = lnext(gucopts);
+ SetConfigOption(name, value, PGC_BACKEND, PGC_S_CLIENT);
+ }
+ }
+
/*
* Post-processing for command line options.
*/
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
- puts("$Revision: 1.320 $ $Date: 2003/03/24 18:33:52 $\n");
+ puts("$Revision: 1.321 $ $Date: 2003/04/17 22:26:01 $\n");
}
/*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: auth.h,v 1.21 2002/06/20 20:29:49 momjian Exp $
+ * $Id: auth.h,v 1.22 2003/04/17 22:26:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*----------------------------------------------------------------
*/
-void ClientAuthentication(Port *port);
+extern void ClientAuthentication(Port *port);
#define PG_KRB4_VERSION "PGVER4.1" /* at most KRB_SENDAUTH_VLEN chars */
#define PG_KRB5_VERSION "PGVER5.1"
* Interface to hba.c
*
*
- * $Id: hba.h,v 1.32 2002/04/04 04:25:54 momjian Exp $
+ * $Id: hba.h,v 1.33 2003/04/17 22:26:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define IDENT_PORT 113
/* Standard TCP port number for Ident service. Assigned by IANA */
-#define MAX_AUTH_ARG 80 /* Max size of an authentication arg */
-
typedef enum UserAuth
{
uaReject,
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-be.h,v 1.34 2002/08/29 03:22:01 tgl Exp $
+ * $Id: libpq-be.h,v 1.35 2003/04/17 22:26:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef LIBPQ_BE_H
#define LIBPQ_BE_H
-#include <sys/types.h>
-
#include "libpq/hba.h"
#include "libpq/pqcomm.h"
/*
* This is used by the postmaster in its communication with frontends. It
* contains all state information needed during this communication before the
- * backend is run.
+ * backend is run. The Port structure is kept in malloc'd memory and is
+ * still available when a backend is running (see MyProcPort). The data
+ * it points to must also be malloc'd, or else palloc'd in TopMemoryContext,
+ * so that it survives into PostgresMain execution!
*/
typedef struct Port
{
int sock; /* File descriptor */
+ ProtocolVersion proto; /* FE/BE protocol version */
SockAddr laddr; /* local addr (postmaster) */
SockAddr raddr; /* remote addr (client) */
- char md5Salt[4]; /* Password salt */
- char cryptSalt[2]; /* Password salt */
/*
- * Information that needs to be held during the fe/be authentication
- * handshake.
+ * Information that needs to be saved from the startup packet and passed
+ * into backend execution. "char *" fields are NULL if not set.
+ * guc_options points to a List of alternating option names and values.
*/
+ char *database_name;
+ char *user_name;
+ char *cmdline_options;
+ List *guc_options;
- ProtocolVersion proto;
- char database[SM_DATABASE + 1];
- char user[SM_DATABASE_USER + 1];
- char options[SM_OPTIONS + 1];
- char tty[SM_TTY + 1];
- char auth_arg[MAX_AUTH_ARG];
+ /*
+ * Information that needs to be held during the authentication cycle.
+ */
UserAuth auth_method;
+ char *auth_arg;
+ char md5Salt[4]; /* Password salt */
+ char cryptSalt[2]; /* Password salt */
/*
* SSL structures
+++ /dev/null
-#ifndef PASSWORD_H
-#define PASSWORD_H
-
-int verify_password(const Port *port, const char *user, const char *password);
-
-#endif
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pqcomm.h,v 1.75 2003/01/06 09:58:36 petere Exp $
+ * $Id: pqcomm.h,v 1.76 2003/04/17 22:26:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PQCOMM_H
#define PQCOMM_H
-#include <sys/types.h>
#ifdef WIN32
#include <winsock.h>
/* workaround for clashing defines of "ERROR" */
* functionality).
*
* If a backend supports version m.n of the protocol it must actually support
- * versions m.0..n]. Backend support for version m-1 can be dropped after a
+ * versions m.[0..n]. Backend support for version m-1 can be dropped after a
* `reasonable' length of time.
*
* A frontend isn't required to support anything other than the current
/* The earliest and latest frontend/backend protocol version supported. */
#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0)
-#define PG_PROTOCOL_LATEST PG_PROTOCOL(2,0)
+#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,100) /* XXX temporary value */
-/*
- * All packets sent to the postmaster start with the length. This is omitted
- * from the different packet definitions specified below.
- */
+typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
-typedef uint32 PacketLen;
+typedef ProtocolVersion MsgType;
/*
- * Startup message parameters sizes. These must not be changed without changing
- * the protocol version. These are all strings that are '\0' terminated only if
- * there is room.
+ * Packet lengths are 4 bytes in network byte order.
+ *
+ * The initial length is omitted from the packet layouts appearing below.
*/
+typedef uint32 PacketLen;
+
+
/*
- * FIXME: remove the fixed size limitations on the database name, user
- * name, and options fields and use a variable length field instead. The
- * actual limits on database & user name will then be NAMEDATALEN, which
- * can be changed without changing the FE/BE protocol. -neilc,2002/08/27
+ * Old-style startup packet layout with fixed-width fields. This is used in
+ * protocol 1.0 and 2.0, but not in later versions. Note that the fields
+ * in this layout are '\0' terminated only if there is room.
*/
#define SM_DATABASE 64
#define SM_UNUSED 64
#define SM_TTY 64
-typedef uint32 ProtocolVersion; /* Fe/Be protocol version number */
-
-typedef ProtocolVersion MsgType;
-
-
typedef struct StartupPacket
{
ProtocolVersion protoVersion; /* Protocol version */
extern bool Db_user_namespace;
-/* These are the authentication requests sent by the backend. */
+/*
+ * In protocol 3.0 and later, the startup packet length is not fixed, but
+ * we set an arbitrary limit on it anyway. This is just to prevent simple
+ * denial-of-service attacks via sending enough data to run the server
+ * out of memory.
+ */
+#define MAX_STARTUP_PACKET_LENGTH 10000
+
+
+/* These are the authentication request codes sent by the backend. */
#define AUTH_REQ_OK 0 /* User is authenticated */
#define AUTH_REQ_KRB4 1 /* Kerberos V4 */
typedef uint32 AuthRequest;
-/* A client can also send a cancel-current-operation request to the postmaster.
+/*
+ * A client can also send a cancel-current-operation request to the postmaster.
* This is uglier than sending it directly to the client's backend, but it
* avoids depending on out-of-band communication facilities.
- */
-
-/* The cancel request code must not match any protocol version number
+ *
+ * The cancel request code must not match any protocol version number
* we're ever likely to use. This random choice should do.
*/
#define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678)
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.74 2003/03/10 22:28:21 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.75 2003/04/17 22:26:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
default:
return STATUS_ERROR;
}
- ret = pqPacketSend(conn, crypt_pwd, strlen(crypt_pwd) + 1);
+ ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
if (areq == AUTH_REQ_MD5)
free(crypt_pwd);
return ret;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.231 2003/04/04 20:42:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.232 2003/04/17 22:26:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#endif
-#define NOTIFYLIST_INITIAL_SIZE 10
-#define NOTIFYLIST_GROWBY 10
-
#define PGPASSFILE ".pgpass"
/* ----------
{"port", "PGPORT", DEF_PGPORT_STR, NULL,
"Database-Port", "", 6},
+ /*
+ * "tty" is no longer used either, but keep it present for backwards
+ * compatibility.
+ */
{"tty", "PGTTY", DefaultTty, NULL,
"Backend-Debug-TTY", "D", 40},
PQExpBuffer errorMessage);
static char *conninfo_getval(PQconninfoOption *connOptions,
const char *keyword);
+static int build_startup_packet(const PGconn *conn, char *packet);
static void defaultNoticeProcessor(void *arg, const char *message);
static int parseServiceInfo(PQconninfoOption *options,
PQExpBuffer errorMessage);
-char *pwdfMatchesString(char *buf, char *token);
-char *PasswordFromFile(char *hostname, char *port, char *dbname,
- char *username);
+static char *pwdfMatchesString(char *buf, char *token);
+static char *PasswordFromFile(char *hostname, char *port, char *dbname,
+ char *username);
/*
* Connecting to a Database
* is NULL or a null string.
*
* PGTTY identifies tty to which to send messages if <pgtty> argument
- * is NULL or a null string.
+ * is NULL or a null string. (No longer used by backend.)
*
* PGOPTIONS identifies connection options if <pgoptions> argument is
* NULL or a null string.
{
int portnum;
char portstr[64];
-#ifdef USE_SSL
- StartupPacket np; /* Used to negotiate SSL connection */
- char SSLok;
-#endif
struct addrinfo *addrs = NULL;
struct addrinfo *addr_cur = NULL;
struct addrinfo hint;
/* Attempt to negotiate SSL usage */
if (conn->allow_ssl_try)
{
- memset((char *) &np, 0, sizeof(np));
- np.protoVersion = htonl(NEGOTIATE_SSL_CODE);
- if (pqPacketSend(conn, (char *) &np, sizeof(StartupPacket)) != STATUS_OK)
+ ProtocolVersion pv;
+ char SSLok;
+
+ pv = htonl(NEGOTIATE_SSL_CODE);
+ if (pqPacketSend(conn, 0, &pv, sizeof(ProtocolVersion)) != STATUS_OK)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send SSL negotiation packet: %s\n"),
case CONNECTION_MADE:
{
- StartupPacket sp;
+ char *startpacket;
+ int packetlen;
/*
- * Initialize the startup packet.
+ * Build the startup packet.
*/
-
- MemSet((char *) &sp, 0, sizeof(StartupPacket));
-
- sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_LIBPQ);
-
- strncpy(sp.user, conn->pguser, SM_USER);
- strncpy(sp.database, conn->dbName, SM_DATABASE);
- strncpy(sp.tty, conn->pgtty, SM_TTY);
-
- if (conn->pgoptions)
- strncpy(sp.options, conn->pgoptions, SM_OPTIONS);
+ packetlen = build_startup_packet(conn, NULL);
+ startpacket = (char *) malloc(packetlen);
+ if (!startpacket)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ goto error_return;
+ }
+ packetlen = build_startup_packet(conn, startpacket);
/*
* Send the startup packet.
* Theoretically, this could block, but it really shouldn't
* since we only got here if the socket is write-ready.
*/
-
- if (pqPacketSend(conn, (char *) &sp,
- sizeof(StartupPacket)) != STATUS_OK)
+ if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send startup packet: %s\n"),
SOCK_STRERROR(SOCK_ERRNO));
+ free(startpacket);
goto error_return;
}
+ free(startpacket);
+
conn->status = CONNECTION_AWAITING_RESPONSE;
return PGRES_POLLING_READING;
}
return false;
conn->setenv_state = SETENV_STATE_ENCODINGS_SEND;
- conn->next_eo = EnvironmentOptions;
return true;
}
{
/* These are reading states */
case SETENV_STATE_ENCODINGS_WAIT:
- case SETENV_STATE_OPTION_WAIT:
{
/* Load waiting data */
int n = pqReadData(conn);
/* These are writing states, so we just proceed. */
case SETENV_STATE_ENCODINGS_SEND:
- case SETENV_STATE_OPTION_SEND:
break;
/* Should we raise an error if called when not active? */
conn->client_encoding = encoding;
/* Move on to setting the environment options */
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
+ conn->setenv_state = SETENV_STATE_IDLE;
}
break;
}
* NULL result indicates that the query is
* finished
*/
- /* Move on to setting the environment options */
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
- }
- break;
- }
-
- case SETENV_STATE_OPTION_SEND:
- {
- /* Send an Environment Option */
- char setQuery[100]; /* note length limits in
- * sprintf's below */
-
- if (conn->next_eo->envName)
- {
- const char *val;
-
- if ((val = getenv(conn->next_eo->envName)))
- {
- if (strcasecmp(val, "default") == 0)
- sprintf(setQuery, "SET %s = %.60s",
- conn->next_eo->pgName, val);
- else
- sprintf(setQuery, "SET %s = '%.60s'",
- conn->next_eo->pgName, val);
-#ifdef CONNECTDEBUG
- printf("Use environment variable %s to send %s\n",
- conn->next_eo->envName, setQuery);
-#endif
- if (!PQsendQuery(conn, setQuery))
- goto error_return;
-
- conn->setenv_state = SETENV_STATE_OPTION_WAIT;
- }
- else
- conn->next_eo++;
- }
- else
- {
- /* No more options to send, so we are done. */
conn->setenv_state = SETENV_STATE_IDLE;
}
break;
}
- case SETENV_STATE_OPTION_WAIT:
- {
- if (PQisBusy(conn))
- return PGRES_POLLING_READING;
-
- res = PQgetResult(conn);
-
- if (res)
- {
- if (PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- PQclear(res);
- goto error_return;
- }
- PQclear(res);
- /* Keep reading until PQgetResult returns NULL */
- }
- else
- {
- /*
- * NULL result indicates that the query is
- * finished
- */
- /* Send the next option */
- conn->next_eo++;
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
- }
- break;
- }
-
case SETENV_STATE_IDLE:
return PGRES_POLLING_OK;
/*
* pqPacketSend() -- send a single-packet message.
- * this is like PacketSend(), defined in backend/libpq/pqpacket.c
+ *
+ * pack_type: the single-byte message type code. (Pass zero for startup
+ * packets, which have no message type code.)
+ *
+ * buf, buf_len: contents of message. The given length includes only what
+ * is in buf; the message type and message length fields are added here.
*
* RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
* SIDE_EFFECTS: may block.
*/
int
-pqPacketSend(PGconn *conn, const char *buf, size_t len)
+pqPacketSend(PGconn *conn, char pack_type,
+ const void *buf, size_t buf_len)
{
- /* Send the total packet size. */
-
- if (pqPutInt(4 + len, 4, conn))
+ /* Send the message type. */
+ if (pack_type != 0)
+ if (pqPutc(pack_type, conn))
+ return STATUS_ERROR;
+
+ /* Send the (self-inclusive) message length word. */
+ if (pqPutInt(buf_len + 4, 4, conn))
return STATUS_ERROR;
- /* Send the packet itself. */
-
- if (pqPutnchar(buf, len, conn))
+ /* Send the message body. */
+ if (pqPutnchar(buf, buf_len, conn))
return STATUS_ERROR;
+ /* Flush to ensure backend gets it. */
if (pqFlush(conn))
return STATUS_ERROR;
}
+/*
+ * Build a startup packet given a filled-in PGconn structure.
+ *
+ * We need to figure out how much space is needed, then fill it in.
+ * To avoid duplicate logic, this routine is called twice: the first time
+ * (with packet == NULL) just counts the space needed, the second time
+ * (with packet == allocated space) fills it in. Return value is the number
+ * of bytes used.
+ */
+static int
+build_startup_packet(const PGconn *conn, char *packet)
+{
+ int packet_len = 0;
+ const struct EnvironmentOptions *next_eo;
+
+ /* Protocol version comes first. */
+ if (packet)
+ {
+ ProtocolVersion pv = htonl(PG_PROTOCOL_LIBPQ);
+
+ memcpy(packet + packet_len, &pv, sizeof(ProtocolVersion));
+ }
+ packet_len += sizeof(ProtocolVersion);
+
+ /* Add user name, database name, options */
+ if (conn->pguser)
+ {
+ if (packet)
+ strcpy(packet + packet_len, "user");
+ packet_len += strlen("user") + 1;
+ if (packet)
+ strcpy(packet + packet_len, conn->pguser);
+ packet_len += strlen(conn->pguser) + 1;
+ }
+ if (conn->dbName)
+ {
+ if (packet)
+ strcpy(packet + packet_len, "database");
+ packet_len += strlen("database") + 1;
+ if (packet)
+ strcpy(packet + packet_len, conn->dbName);
+ packet_len += strlen(conn->dbName) + 1;
+ }
+ if (conn->pgoptions)
+ {
+ if (packet)
+ strcpy(packet + packet_len, "options");
+ packet_len += strlen("options") + 1;
+ if (packet)
+ strcpy(packet + packet_len, conn->pgoptions);
+ packet_len += strlen(conn->pgoptions) + 1;
+ }
+
+ /* Add any environment-driven GUC settings needed */
+ for (next_eo = EnvironmentOptions; next_eo->envName; next_eo++)
+ {
+ const char *val;
+
+ if ((val = getenv(next_eo->envName)) != NULL)
+ {
+ if (strcasecmp(val, "default") != 0)
+ {
+ if (packet)
+ strcpy(packet + packet_len, next_eo->pgName);
+ packet_len += strlen(next_eo->pgName) + 1;
+ if (packet)
+ strcpy(packet + packet_len, val);
+ packet_len += strlen(val) + 1;
+ }
+ }
+ }
+
+ /* Add trailing terminator */
+ if (packet)
+ packet[packet_len] = '\0';
+ packet_len++;
+
+ return packet_len;
+}
+
+
/* =========== accessor functions for PGconn ========= */
char *
PQdb(const PGconn *conn)
fprintf(stderr, "%s", message);
}
-/* returns a pointer to the next token or NULL if the current
- * token doesn't match */
-char *
+/*
+ * returns a pointer to the next token or NULL if the current
+ * token doesn't match
+ */
+static char *
pwdfMatchesString(char *buf, char *token)
{
char *tbuf,
}
/* Get a password from the password file. Return value is malloc'd. */
-char *
+static char *
PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
{
FILE *fp;
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-int.h,v 1.60 2002/10/16 02:55:30 momjian Exp $
+ * $Id: libpq-int.h,v 1.61 2003/04/17 22:26:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* pqcomm.h describe what the backend knows, not what libpq knows.
*/
-#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(2,0)
+#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,100) /* XXX temporary value */
/*
* POSTGRES backend dependent Constants.
/* PGSetenvStatusType defines the state of the PQSetenv state machine */
typedef enum
{
- SETENV_STATE_OPTION_SEND, /* About to send an Environment Option */
- SETENV_STATE_OPTION_WAIT, /* Waiting for above send to complete */
SETENV_STATE_ENCODINGS_SEND, /* About to send an "encodings" query */
SETENV_STATE_ENCODINGS_WAIT, /* Waiting for query to complete */
SETENV_STATE_IDLE
/* Status for sending environment info. Used during PQSetenv only. */
PGSetenvStatusType setenv_state;
- const struct EnvironmentOptions *next_eo;
#ifdef USE_SSL
bool allow_ssl_try; /* Allowed to try SSL negotiation */
/* === in fe-connect.c === */
-extern int pqPacketSend(PGconn *conn, const char *buf, size_t len);
+extern int pqPacketSend(PGconn *conn, char pack_type,
+ const void *buf, size_t buf_len);
/* === in fe-exec.c === */
--
-- INTERVAL
--
-SET DATESTYLE = DEFAULT;
+SET DATESTYLE = 'ISO';
-- check acceptance of "time zone style"
SELECT INTERVAL '01:00' AS "One hour";
One hour
-- INTERVAL
--
-SET DATESTYLE = DEFAULT;
+SET DATESTYLE = 'ISO';
-- check acceptance of "time zone style"
SELECT INTERVAL '01:00' AS "One hour";