Hello,
This is a follow-up to another patch I posted about libpq confusing documentation & psql resulting behavior under host/hostaddr settings.
Although the first mostly documentation patch did not gather much enthousiasm, I still think both issues deserve a fix.
About updating psql's behavior, without this patch: sh> psql "host=foo hostaddr=127.0.0.1" psql> \conninfo You are connected to database "fabien" as user "fabien" on host "foo" at port "5432". # NOPE, I'm really connected to localhost, foo does not even exist # Other apparent inconsistencies are possible when hostaddr overrides # "host" which is an socket directory or an IP. psql> \c template1 could not translate host name "foo" to address: Name or service not known Previous connection kept # hmmm.... what is the meaning of reusing a connection? # this issue was pointed out by Arthur Zakirov After the patch: sh> psql "host=foo hostaddr=127.0.0.1" psql> \conninfo You are connected to database "fabien" as user "fabien" on host "foo" (address "127.0.0.1") at port "5432". # better psql> \c template1 You are now connected to database "template1" as user "fabien". # thanksThe patch adds a PQhostaddr() function to libpq which reports the "hostaddr" setting or the current server ip. The function is used by psql for \conninfo and when reusing parameters for \connect.
The messages are slightly more verbose because the IP is output. I think that user asking for conninfo should survive to the more precise data. This also comes handy if a host name resolves to several IPs (eg IPv6 and IPv4, or several IPs...).
-- Fabien.
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 82a440531b..310792e58d 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1735,6 +1735,35 @@ 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 actual server IP address of the active connection. + This can be the address 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> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 5b4d54a442..d6f3a981b3 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -596,14 +596,23 @@ 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 +2863,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 +2904,22 @@ 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 +2981,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 +3096,23 @@ 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"), diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt index 4359fae30d..cc9ee9ce6b 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -173,3 +173,4 @@ PQsetErrorContextVisibility 170 PQresultVerboseErrorMessage 171 PQencryptPasswordConn 172 PQresultMemorySize 173 +PQhostaddr 174 diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index d001bc513d..8e3acaf05d 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -1471,6 +1471,34 @@ connectNoDelay(PGconn *conn) return 1; } +static void +getHostaddr(PGconn *conn, char host_addr[NI_MAXHOST]) +{ + struct sockaddr_storage *addr = &conn->raddr.addr; + + 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, NI_MAXHOST) == 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, NI_MAXHOST) == NULL) + strcpy(host_addr, "???"); + } +#endif + else + strcpy(host_addr, "???"); +} /* ---------- * connectFailureMessage - @@ -1504,34 +1532,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); /* To which host and port were we actually connecting? */ if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS) @@ -2302,6 +2308,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; + } + + { + char host_addr[NI_MAXHOST]; + getHostaddr(conn, host_addr); + if (strcmp(host_addr, "???") != 0) + conn->connip = strdup(host_addr); + } + conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0); if (conn->sock == PGINVALID_SOCKET) { @@ -6172,6 +6193,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) { diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 52bd5d2cd8..3f13ddf092 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -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); diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 975ab33d02..66fd317b94 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -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