psql: Show IP address in \conninfo

When hostaddr is given, the actual IP address that psql is connected to
can be totally unexpected for the given host.  The more verbose output
we now generate makes things clearer.  Since the "host" and "hostaddr"
parts of the conninfo could come from different sources (say, one of
them is in the service specification or a URI-style conninfo and the
other is not), this is not as silly as it may first appear.  This is
also definitely useful if the hostname resolves to multiple addresses.

Author: Fabien Coelho
Reviewed-by: Pavel Stehule, Arthur Zakirov
Discussion: https://postgr.es/m/alpine.DEB.2.21.1810261532380.27686@lancre
	https://postgr.es/m/alpine.DEB.2.21.1808201323020.13832@lancre
This commit is contained in:
Alvaro Herrera 2018-11-19 14:34:12 -03:00
parent 7ee5f88e65
commit 6e5f8d489a
6 changed files with 172 additions and 41 deletions

View File

@ -1735,6 +1735,36 @@ char *PQhost(const PGconn *conn);
</listitem>
</varlistentry>
<varlistentry id="libpq-pqhostaddr">
<term>
<function>PQhostaddr</function>
<indexterm>
<primary>PQhostaddr</primary>
</indexterm>
</term>
<listitem>
<para>
Returns the server IP address of the active connection.
This can be the address that a host name resolved to,
or an IP address provided through the <literal>hostaddr</literal>
parameter.
<synopsis>
char *PQhostaddr(const PGconn *conn);
</synopsis>
</para>
<para>
<function>PQhostaddr</function> returns <symbol>NULL</symbol> if the
<parameter>conn</parameter> argument is <symbol>NULL</symbol>.
Otherwise, if there is an error producing the host information
(perhaps if the connection has not been fully established or
there was an error), it returns an empty string.
</para>
</listitem>
</varlistentry>
<varlistentry id="libpq-pqport">
<term>
<function>PQport</function>

View File

@ -596,14 +596,30 @@ exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
else
{
char *host = PQhost(pset.db);
char *hostaddr = PQhostaddr(pset.db);
/* If the host is an absolute path, the connection is via socket */
/*
* If the host is an absolute path, the connection is via socket
* unless overriden by hostaddr
*/
if (is_absolute_path(host))
printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), host, PQport(pset.db));
{
if (hostaddr && *hostaddr)
printf(_("You are connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), hostaddr, PQport(pset.db));
else
printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), host, PQport(pset.db));
}
else
printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), host, PQport(pset.db));
{
if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
db, PQuser(pset.db), host, hostaddr, PQport(pset.db));
else
printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
db, PQuser(pset.db), host, PQport(pset.db));
}
printSSLInfo();
}
}
@ -2854,6 +2870,7 @@ do_connect(enum trivalue reuse_previous_specification,
PGconn *o_conn = pset.db,
*n_conn;
char *password = NULL;
char *hostaddr = NULL;
bool keep_password;
bool has_connection_string;
bool reuse_previous;
@ -2894,12 +2911,27 @@ do_connect(enum trivalue reuse_previous_specification,
}
/* grab missing values from the old connection */
if (!user && reuse_previous)
user = PQuser(o_conn);
if (!host && reuse_previous)
host = PQhost(o_conn);
if (!port && reuse_previous)
port = PQport(o_conn);
if (reuse_previous)
{
if (!user)
user = PQuser(o_conn);
if (host && strcmp(host, PQhost(o_conn)) == 0)
{
/*
* if we are targetting the same host, reuse its hostaddr for
* consistency
*/
hostaddr = PQhostaddr(o_conn);
}
if (!host)
{
host = PQhost(o_conn);
/* also set hostaddr for consistency */
hostaddr = PQhostaddr(o_conn);
}
if (!port)
port = PQport(o_conn);
}
/*
* Any change in the parameters read above makes us discard the password.
@ -2961,13 +2993,18 @@ do_connect(enum trivalue reuse_previous_specification,
while (true)
{
#define PARAMS_ARRAY_SIZE 8
#define PARAMS_ARRAY_SIZE 9
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
int paramnum = -1;
keywords[++paramnum] = "host";
values[paramnum] = host;
if (hostaddr && *hostaddr)
{
keywords[++paramnum] = "hostaddr";
values[paramnum] = hostaddr;
}
keywords[++paramnum] = "port";
values[paramnum] = port;
keywords[++paramnum] = "user";
@ -3071,14 +3108,27 @@ do_connect(enum trivalue reuse_previous_specification,
param_is_newly_set(PQport(o_conn), PQport(pset.db)))
{
char *host = PQhost(pset.db);
char *hostaddr = PQhostaddr(pset.db);
/* If the host is an absolute path, the connection is via socket */
if (is_absolute_path(host))
printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
{
if (hostaddr && *hostaddr)
printf(_("You are now connected to database \"%s\" as user \"%s\" on address \"%s\" at port \"%s\".\n"),
PQdb(pset.db), PQuser(pset.db), hostaddr, PQport(pset.db));
else
printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
}
else
printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
{
if (hostaddr && *hostaddr && strcmp(host, hostaddr) != 0)
printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" (address \"%s\") at port \"%s\".\n"),
PQdb(pset.db), PQuser(pset.db), host, hostaddr, PQport(pset.db));
else
printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
}
}
else
printf(_("You are now connected to database \"%s\" as user \"%s\".\n"),

View File

@ -173,3 +173,4 @@ PQsetErrorContextVisibility 170
PQresultVerboseErrorMessage 171
PQencryptPasswordConn 172
PQresultMemorySize 173
PQhostaddr 174

View File

@ -1471,6 +1471,39 @@ connectNoDelay(PGconn *conn)
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 (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, host_addr_len);
else if (addr->ss_family == AF_INET)
{
if (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 (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 -
@ -1504,34 +1537,12 @@ connectFailureMessage(PGconn *conn, int errorno)
char host_addr[NI_MAXHOST];
const char *displayed_host;
const char *displayed_port;
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->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
strlcpy(host_addr, conn->connhost[conn->whichhost].hostaddr, 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, "???");
getHostaddr(conn, host_addr, NI_MAXHOST);
/* To which host and port were we actually connecting? */
if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
@ -1548,14 +1559,14 @@ connectFailureMessage(PGconn *conn, int errorno)
* 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_host, host_addr,
displayed_port);
else
appendPQExpBuffer(&conn->errorMessage,
@ -2286,6 +2297,7 @@ keep_going: /* We will come back to here until there is
*/
{
struct addrinfo *addr_cur = conn->addr_cur;
char host_addr[NI_MAXHOST];
/*
* Advance to next possible host, if we've tried all of
@ -2302,6 +2314,21 @@ keep_going: /* We will come back to here until there is
addr_cur->ai_addrlen);
conn->raddr.salen = addr_cur->ai_addrlen;
/* set connip */
if (conn->connip != NULL)
{
free(conn->connip);
conn->connip = NULL;
}
getHostaddr(conn, host_addr, NI_MAXHOST);
if (strlen(host_addr) > 0)
conn->connip = strdup(host_addr);
/*
* purposely ignore strdup failure; not a big problem if
* it fails anyway.
*/
conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
if (conn->sock == PGINVALID_SOCKET)
{
@ -3665,6 +3692,8 @@ freePGconn(PGconn *conn)
free(conn->sslcompression);
if (conn->requirepeer)
free(conn->requirepeer);
if (conn->connip)
free(conn->connip);
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
if (conn->krbsrvname)
free(conn->krbsrvname);
@ -6172,6 +6201,25 @@ PQhost(const PGconn *conn)
return "";
}
char *
PQhostaddr(const PGconn *conn)
{
if (!conn)
return NULL;
if (conn->connhost != NULL)
{
if (conn->connhost[conn->whichhost].hostaddr != NULL &&
conn->connhost[conn->whichhost].hostaddr[0] != '\0')
return conn->connhost[conn->whichhost].hostaddr;
if (conn->connip != NULL)
return conn->connip;
}
return "";
}
char *
PQport(const PGconn *conn)
{

View File

@ -312,6 +312,7 @@ extern char *PQdb(const PGconn *conn);
extern char *PQuser(const PGconn *conn);
extern char *PQpass(const PGconn *conn);
extern char *PQhost(const PGconn *conn);
extern char *PQhostaddr(const PGconn *conn);
extern char *PQport(const PGconn *conn);
extern char *PQtty(const PGconn *conn);
extern char *PQoptions(const PGconn *conn);

View File

@ -397,6 +397,7 @@ struct pg_conn
int nconnhost; /* # of hosts named in conn string */
int whichhost; /* host we're currently trying/connected to */
pg_conn_host *connhost; /* details about each named host */
char *connip; /* IP address for current network connection */
/* Connection data */
pgsocket sock; /* FD for socket, PGINVALID_SOCKET if