]> granicus.if.org Git - postgresql/commitdiff
psql: Show IP address in \conninfo
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 19 Nov 2018 17:34:12 +0000 (14:34 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 19 Nov 2018 17:34:12 +0000 (14:34 -0300)
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

doc/src/sgml/libpq.sgml
src/bin/psql/command.c
src/interfaces/libpq/exports.txt
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h

index 601091c5709b295d6519c0c545a43363286e9565..d2e5b08541ecdfce8fabd0ec2e043d26f5fa3acf 100644 (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>
index 04e227b5a6478a5d0031bbd5558964207a9352a6..ee88e1ca5cff0a72185301291415025682dba4e7 100644 (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"),
index 4359fae30d43c51f72e6348bb13a321fadfd650f..cc9ee9ce6b82506ec733d66aef273176a0f9d749 100644 (file)
@@ -173,3 +173,4 @@ PQsetErrorContextVisibility 170
 PQresultVerboseErrorMessage 171
 PQencryptPasswordConn     172
 PQresultMemorySize        173
+PQhostaddr                174
index d001bc513d30ba855bec068e9a2c598c12641ba7..bc456fec0c293210a7a3cefaeb5659d680c86454 100644 (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)
 {
index 52bd5d2cd845471cefbb8d41d4f110fc06854654..3f13ddf092be81096d57a6c90a01d132c4679c71 100644 (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);
index 975ab33d025396ec77d19c7852b06a3986ea5eee..66fd317b949498f1600a8ceb9b1190f8c4e79019 100644 (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