Abhijit Menon-Sen: Postgres v3 support
authorjmayer <jmayer@f5534014-38df-0310-8fa8-9805f1628bb7>
Mon, 20 Dec 2004 23:24:13 +0000 (23:24 +0000)
committerjmayer <jmayer@f5534014-38df-0310-8fa8-9805f1628bb7>
Mon, 20 Dec 2004 23:24:13 +0000 (23:24 +0000)
git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@12795 f5534014-38df-0310-8fa8-9805f1628bb7

AUTHORS
epan/dissectors/Makefile.common
epan/dissectors/packet-pgsql.c [new file with mode: 0644]
epan/dissectors/packet-postgresql.c [deleted file]

diff --git a/AUTHORS b/AUTHORS
index d7f4ffa4cb9cfd7c8a90bd2a740492ab8a7d6b6a..8d4dea27c8ac534c1ff5995ef24d287b84241733 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -2253,6 +2253,10 @@ Stefan Metzmacher        <metze [AT] samba.org> {
        LDAP Controls support in the LDAP dissector
 }
 
+Abhijit Menon-Sen      <ams [AT] oryx.com> {
+       Postgresql v3 dissector
+}
+
 
 And assorted fixes and enhancements by the people listed above
 and by:
index 834743a3da89b7ab8462002265e8363e419ad3a6..2aa65be73c9c14aa2bf506cdddf8c13faa1129ba 100644 (file)
@@ -408,7 +408,7 @@ DISSECTOR_SRC =     \
        packet-pktc.c   \
        packet-pop.c    \
        packet-portmap.c        \
-       packet-postgresql.c     \
+       packet-pgsql.c  \
        packet-ppp.c    \
        packet-pppoe.c  \
        packet-pptp.c   \
diff --git a/epan/dissectors/packet-pgsql.c b/epan/dissectors/packet-pgsql.c
new file mode 100644 (file)
index 0000000..e7c46ac
--- /dev/null
@@ -0,0 +1,886 @@
+/* packet-pgsql.c
+ * Routines for PostgreSQL v3 protocol dissection.
+ * <http://www.postgresql.org/docs/current/static/protocol.html>
+ * Copyright 2004 Abhijit Menon-Sen <ams@oryx.com>
+ *
+ * $Id $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+#include <epan/packet.h>
+#include <epan/conversation.h>
+
+#include "packet-tcp.h"
+#include "reassemble.h"
+
+#define TCP_PORT_PGSQL 5432
+
+
+static int proto_pgsql = -1;
+static int hf_frontend = -1;
+static int hf_type = -1;
+static int hf_length = -1;
+static int hf_parameter_name = -1;
+static int hf_parameter_value = -1;
+static int hf_query = -1;
+static int hf_authtype = -1;
+static int hf_passwd = -1;
+static int hf_salt = -1;
+static int hf_statement = -1;
+static int hf_portal = -1;
+static int hf_tag = -1;
+static int hf_status = -1;
+static int hf_copydata = -1;
+static int hf_error = -1;
+static int hf_pid = -1;
+static int hf_key = -1;
+static int hf_condition = -1;
+static int hf_text = -1;
+static int hf_tableoid = -1;
+static int hf_typeoid = -1;
+static int hf_oid = -1;
+static int hf_format = -1;
+static int hf_val_name = -1;
+static int hf_val_idx = -1;
+static int hf_val_length = -1;
+static int hf_val_data = -1;
+static int hf_val_mod = -1;
+static int hf_severity = -1;
+static int hf_code = -1;
+static int hf_message = -1;
+static int hf_detail = -1;
+static int hf_hint = -1;
+static int hf_position = -1;
+static int hf_where = -1;
+static int hf_file = -1;
+static int hf_line = -1;
+static int hf_routine = -1;
+
+static gint ett_pgsql = -1;
+static gint ett_values = -1;
+
+static gboolean pgsql_desegment = TRUE;
+static gboolean first_message = TRUE;
+
+static void dissect_pgsql_fe_msg(guchar, guint32, guint32, tvbuff_t *, proto_tree *);
+static void dissect_pgsql_be_msg(guchar, guint32, guint32, tvbuff_t *, proto_tree *);
+static void dissect_pgsql_msg(tvbuff_t *, packet_info *, proto_tree *);
+static void dissect_pgsql(tvbuff_t *, packet_info *, proto_tree *);
+static char *identify(gboolean, guchar);
+static guint pgsql_length(tvbuff_t *, int);
+
+static const value_string auth_types[] = {
+    { 0, "Success" },
+    { 1, "Kerberos V4" },
+    { 2, "Kerberos V5" },
+    { 3, "Plaintext password" },
+    { 4, "crypt()ed password" },
+    { 5, "MD5 password" },
+    { 6, "SCM credentials" },
+    { 0, NULL }
+};
+
+static const value_string status_vals[] = {
+    { 'I', "Idle" },
+    { 'T', "In a transaction" },
+    { 'E', "In a failed transaction" },
+    { 0, NULL }
+};
+
+static const value_string format_vals[] = {
+    { 0, "Text" },
+    { 1, "Binary" },
+    { 0, NULL }
+};
+
+
+void
+proto_reg_handoff_pgsql(void)
+{
+    dissector_handle_t pgsql_handle;
+
+    pgsql_handle = create_dissector_handle(dissect_pgsql, proto_pgsql);
+    dissector_add("tcp.port", TCP_PORT_PGSQL, pgsql_handle);
+}
+
+
+void
+proto_register_pgsql(void)
+{
+    static hf_register_info hf[] = {
+        { &hf_frontend,
+          { "Frontend", "pgsql.frontend", FT_BOOLEAN, BASE_NONE, NULL, 0,
+            "True for messages from the frontend, false otherwise.",
+            HFILL }
+        },
+        { &hf_type,
+          { "Type", "pgsql.type", FT_STRING, BASE_NONE, NULL, 0,
+            "A one-byte message type identifier.", HFILL }
+        },
+        { &hf_length,
+          { "Length", "pgsql.length", FT_UINT32, BASE_DEC, NULL, 0,
+            "The length of the message (not including the type).",
+            HFILL }
+        },
+        { &hf_parameter_name,
+          { "Parameter name", "pgsql.parameter_name", FT_STRINGZ,
+            BASE_NONE, NULL, 0, "The name of a database parameter.",
+            HFILL }
+        },
+        { &hf_parameter_value,
+          { "Parameter value", "pgsql.parameter_value", FT_STRINGZ,
+            BASE_NONE, NULL, 0, "The value of a database parameter.",
+            HFILL }
+        },
+        { &hf_query,
+          { "Query", "pgsql.query", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "A query string.", HFILL }
+        },
+        { &hf_passwd,
+          { "Password", "pgsql.password", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "A password.", HFILL }
+        },
+        { &hf_authtype,
+          { "Authentication type", "pgsql.authtype", FT_INT32, BASE_DEC,
+            VALS(auth_types), 0,
+            "The type of authentication requested by the backend.", HFILL }
+        },
+        { &hf_salt,
+          { "Salt value", "pgsql.salt", FT_BYTES, BASE_HEX, NULL, 0,
+            "The salt to use while encrypting a password.", HFILL }
+        },
+        { &hf_statement,
+          { "Statement", "pgsql.statement", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "The name of a prepared statement.", HFILL }
+        },
+        { &hf_portal,
+          { "Portal", "pgsql.portal", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "The name of a portal.", HFILL }
+        },
+        { &hf_tag,
+          { "Tag", "pgsql.tag", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "A completion tag.", HFILL }
+        },
+        { &hf_status,
+          { "Status", "pgsql.status", FT_UINT8, BASE_DEC, VALS(status_vals),
+            0, "The transaction status of the backend.", HFILL }
+        },
+        { &hf_copydata,
+          { "Copy data", "pgsql.copydata", FT_BYTES, BASE_NONE, NULL, 0,
+            "Data sent following a Copy-in or Copy-out response.", HFILL }
+        },
+        { &hf_error,
+          { "Error", "pgsql.error", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "An error message.", HFILL }
+        },
+        { &hf_pid,
+          { "PID", "pgsql.pid", FT_UINT32, BASE_DEC, NULL, 0,
+            "The process ID of a backend.", HFILL }
+        },
+        { &hf_key,
+          { "Key", "pgsql.key", FT_UINT32, BASE_DEC, NULL, 0,
+            "The secret key used by a particular backend.", HFILL }
+        },
+        { &hf_condition,
+          { "Condition", "pgsql.condition", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "The name of a NOTIFY condition.", HFILL }
+        },
+        { &hf_text,
+          { "Text", "pgsql.text", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "Text from the backend.", HFILL }
+        },
+        { &hf_tableoid,
+          { "Table OID", "pgsql.oid.table", FT_UINT32, BASE_DEC, NULL, 0,
+            "The object identifier of a table.", HFILL }
+        },
+        { &hf_typeoid,
+          { "Type OID", "pgsql.oid.type", FT_UINT32, BASE_DEC, NULL, 0,
+            "The object identifier of a type.", HFILL }
+        },
+        { &hf_oid,
+          { "OID", "pgsql.oid", FT_UINT32, BASE_DEC, NULL, 0,
+            "An object identifier.", HFILL }
+        },
+        { &hf_format,
+          { "Format", "pgsql.format", FT_UINT16, BASE_DEC, VALS(format_vals),
+            0, "A format specifier.", HFILL }
+        },
+        { &hf_val_name,
+          { "Column name", "pgsql.col.name", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "The name of a column.", HFILL }
+        },
+        { &hf_val_idx,
+          { "Column index", "pgsql.col.index", FT_UINT32, BASE_DEC, NULL, 0,
+            "The position of a column within a row.", HFILL }
+        },
+        { &hf_val_length,
+          { "Column length", "pgsql.val.length", FT_INT32, BASE_DEC, NULL, 0,
+            "The length of a parameter value, in bytes. -1 means NULL.",
+            HFILL }
+        },
+        { &hf_val_data,
+          { "Data", "pgsql.val.data", FT_BYTES, BASE_NONE, NULL, 0,
+            "Parameter data.", HFILL }
+        },
+        { &hf_val_mod,
+          { "Type modifier", "pgsql.col.typemod", FT_INT32, BASE_DEC, NULL, 0,
+            "The type modifier for a column.", HFILL }
+        },
+        { &hf_severity,
+          { "Severity", "pgsql.severity", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "Message severity.", HFILL }
+        },
+        { &hf_code,
+          { "Code", "pgsql.code", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "SQLState code.", HFILL }
+        },
+        { &hf_message,
+          { "Message", "pgsql.message", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "Error message.", HFILL }
+        },
+        { &hf_detail,
+          { "Detail", "pgsql.detail", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "Detailed error message.", HFILL }
+        },
+        { &hf_hint,
+          { "Hint", "pgsql.hint", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "A suggestion to resolve an error.", HFILL }
+        },
+        { &hf_position,
+          { "Position", "pgsql.position", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "The index of the error within the query string.", HFILL }
+        },
+        { &hf_where,
+          { "Context", "pgsql.where", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "The context in which an error occurred.", HFILL }
+        },
+        { &hf_file,
+          { "File", "pgsql.file", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "The source-code file where an error was reported.", HFILL }
+        },
+        { &hf_line,
+          { "Line", "pgsql.line", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "The line number on which an error was reported.", HFILL }
+        },
+        { &hf_routine,
+          { "Routine", "pgsql.routine", FT_STRINGZ, BASE_NONE, NULL, 0,
+            "The routine that reported an error.", HFILL }
+        }
+    };
+
+    static gint *ett[] = {
+        &ett_pgsql,
+        &ett_values
+    };
+
+    proto_pgsql = proto_register_protocol("PostgreSQL", "PGSQL", "pgsql");
+    proto_register_field_array(proto_pgsql, hf, array_length(hf));
+    proto_register_subtree_array(ett, array_length(ett));
+}
+
+
+/* This function is called once per TCP packet. It sets COL_PROTOCOL and
+ * identifies FE/BE messages by adding a ">" or "<" to COL_INFO. Then it
+ * arranges for each message to be dissected individually. */
+
+static void
+dissect_pgsql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+    conversation_t *cv;
+
+    first_message = TRUE;
+
+    /* We don't use conversation data yet, but... */
+    cv = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
+                           pinfo->srcport, pinfo->destport, 0);
+    if (!cv) {
+        cv = conversation_new(&pinfo->src, &pinfo->dst, pinfo->ptype,
+                              pinfo->srcport, pinfo->destport, 0);
+    }
+
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+        col_set_str(pinfo->cinfo, COL_PROTOCOL, "PGSQL");
+    if (check_col(pinfo->cinfo, COL_INFO))
+        col_add_str(pinfo->cinfo, COL_INFO,
+                    (pinfo->match_port == pinfo->destport) ?
+                     ">" : "<");
+
+    tcp_dissect_pdus(tvb, pinfo, tree, pgsql_desegment, 5,
+                     pgsql_length, dissect_pgsql_msg);
+}
+
+
+/* This function is called by tcp_dissect_pdus() to find the size of the
+   message starting at tvb[offset]. */
+
+static guint
+pgsql_length(tvbuff_t *tvb, int offset)
+{
+    gint n = 0;
+    guchar type;
+    guint32 length;
+
+    /* The length is either the four bytes after the type, or, if the
+       type is 0, the first four bytes. */
+    type = tvb_get_guint8(tvb, offset);
+    if (type != '\0')
+        n = 1;
+    length = tvb_get_ntohl(tvb, offset+n);
+    return length+n;
+}
+
+
+/* This function is responsible for dissecting a single message. */
+
+static void
+dissect_pgsql_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+    proto_item *ti;
+    proto_tree *ptree;
+
+    gint n;
+    guchar type;
+    char *typestr;
+    guint32 length;
+    gboolean info = check_col(pinfo->cinfo, COL_INFO);
+    gboolean fe = (pinfo->match_port == pinfo->destport);
+
+    n = 0;
+    type = tvb_get_guint8(tvb, 0);
+    if (type != '\0')
+        n += 1;
+    length = tvb_get_ntohl(tvb, n);
+
+    /* There are a few frontend messages that have no leading type byte.
+       We identify them by the fact that the first byte of their length
+       must be zero, and that the next four bytes are a unique tag. */
+    if (fe && type == '\0') {
+        guint32 tag = tvb_get_ntohl(tvb, 4);
+
+        if (length == 16 && tag == 80877102)
+            typestr = "Cancel request";
+        else if (length == 8 && tag == 80877103)
+            typestr = "SSL request";
+        else if (tag == 196608)
+            typestr = "Startup message";
+        else
+            typestr = "Unknown";
+    }
+    else {
+        typestr = identify(fe, type);
+    }
+
+    if (info) {
+        /* This is a terrible hack. It makes the "Info" column reflect
+           the contents of every message in a TCP packet. Could it be
+           done any better? */
+        col_append_fstr(pinfo->cinfo, COL_INFO, "%s%c",
+                        ( first_message ? "" : "/" ), type);
+        first_message = FALSE;
+    }
+
+    if (tree) {
+        ti = proto_tree_add_item(tree, proto_pgsql, tvb, 0, -1, FALSE);
+        ptree = proto_item_add_subtree(ti, ett_pgsql);
+
+        n = 1;
+        if (type == '\0')
+            n = 0;
+        proto_tree_add_text(ptree, tvb, 0, n, "Type: %s", typestr);
+        proto_tree_add_item_hidden(ptree, hf_type, tvb, 0, n, FALSE);
+        proto_tree_add_item(ptree, hf_length, tvb, n, 4, FALSE);
+        proto_tree_add_boolean_hidden(ptree, hf_frontend, tvb, 0, 0, fe);
+        n += 4;
+
+        if (fe)
+            dissect_pgsql_fe_msg(type, n, length, tvb, ptree);
+        else
+            dissect_pgsql_be_msg(type, n, length, tvb, ptree);
+    }
+}
+
+
+static const value_string fe_messages[] = {
+    { 'p', "Password message" },
+    { 'Q', "Simple query" },
+    { 'P', "Parse" },
+    { 'B', "Bind" },
+    { 'E', "Execute" },
+    { 'D', "Describe" },
+    { 'C', "Close" },
+    { 'H', "Flush" },
+    { 'S', "Sync" },
+    { 'F', "Function call" },
+    { 'd', "Copy data" },
+    { 'c', "Copy completion" },
+    { 'f', "Copy failure" },
+    { 'X', "Termination" },
+    { 0, NULL }
+};
+
+
+static void dissect_pgsql_fe_msg(guchar type, guint32 n, guint32 length,
+                          tvbuff_t *tvb, proto_tree *tree)
+{
+    guchar c;
+    char *s, *t;
+    guint32 i, l;
+    proto_item *ti;
+    proto_tree *shrub;
+
+    switch (type) {
+    /* Password */
+    case 'p':
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_passwd, tvb, n, l, s);
+        g_free(s);
+        break;
+
+    /* Simple query */
+    case 'Q':
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_query, tvb, n, l, s);
+        g_free(s);
+        break;
+
+    /* Parse */
+    case 'P':
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_statement, tvb, n, l, s);
+        g_free(s);
+        n += l;
+
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_query, tvb, n, l, s);
+        g_free(s);
+        n += l;
+
+        i = tvb_get_ntohs(tvb, n);
+        ti = proto_tree_add_text(tree, tvb, n, 2, "Parameters: %d", i);
+        shrub = proto_item_add_subtree(ti, ett_values);
+        n += 2;
+        while (i-- > 0) {
+            proto_tree_add_item(shrub, hf_typeoid, tvb, n, 4, FALSE);
+            n += 4;
+        }
+        break;
+
+    /* Bind */
+    case 'B':
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_portal, tvb, n, l, s);
+        g_free(s);
+        n += l;
+
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_statement, tvb, n, l, s);
+        g_free(s);
+        n += l;
+
+        i = tvb_get_ntohs(tvb, n);
+        ti = proto_tree_add_text(tree, tvb, n, 2, "Parameter formats: %d", i);
+        shrub = proto_item_add_subtree(ti, ett_values);
+        n += 2;
+        while (i-- > 0) {
+            proto_tree_add_item(shrub, hf_format, tvb, n, 2, FALSE);
+            n += 2;
+        }
+
+        i = tvb_get_ntohs(tvb, n);
+        ti = proto_tree_add_text(tree, tvb, n, 2, "Parameter values: %d", i);
+        shrub = proto_item_add_subtree(ti, ett_values);
+        n += 2;
+        while (i-- > 0) {
+            l = tvb_get_ntohl(tvb, n);
+            proto_tree_add_int(shrub, hf_val_length, tvb, n, 4, l);
+            n += 4;
+            if (l > 0)
+                proto_tree_add_item(shrub, hf_val_data, tvb, n, l, FALSE);
+            n += l;
+        }
+
+        i = tvb_get_ntohs(tvb, n);
+        ti = proto_tree_add_text(tree, tvb, n, 2, "Result formats: %d", i);
+        shrub = proto_item_add_subtree(ti, ett_values);
+        n += 2;
+        while (i-- > 0) {
+            proto_tree_add_item(shrub, hf_format, tvb, n, 2, FALSE);
+            n += 2;
+        }
+        break;
+
+    /* Execute */
+    case 'E':
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_portal, tvb, n, l, s);
+        g_free(s);
+        n += l;
+
+        ti = proto_tree_add_text(tree, tvb, n, 4, "Returns: ");
+        i = tvb_get_ntohl(tvb, n);
+        if (i == 0)
+            proto_item_append_text(ti, "all");
+        else
+            proto_item_append_text(ti, "%d", i);
+        proto_item_append_text(ti, " rows");
+        break;
+
+    /* Describe, Close */
+    case 'D':
+    case 'C':
+        i = 0;
+        c = tvb_get_guint8(tvb, n);
+        if (c == 'P')
+            i = hf_portal;
+        else
+            i = hf_statement;
+
+        if (i != 0) {
+            n += 1;
+            s = tvb_get_stringz(tvb, n, &l);
+            proto_tree_add_string_hidden(tree, i, tvb, n, l, s);
+            proto_tree_add_text(
+                tree, tvb, n-1, l, "%s: %s",
+                (c == 'P' ? "Portal" : "Statement"), s
+            );
+            g_free(s);
+        }
+        break;
+
+    /* Messages without a type identifier */
+    case '\0':
+        i = tvb_get_ntohl(tvb, n);
+        n += 4;
+        length -= n;
+        switch (i) {
+        /* Startup message */
+        case 196608:
+            while (length > 0) {
+                s = tvb_get_stringz(tvb, n, &l);
+                n += l;
+                length -= l;
+                if (length <= 0) {
+                    g_free(s);
+                    break;
+                }
+                t = tvb_get_stringz(tvb, n, &l);
+                proto_tree_add_text(tree, tvb, n, l, "%s: %s", s, t);
+                g_free(s);
+                g_free(t);
+                n += l;
+                length -= l;
+                if (length == 1 && tvb_get_guint8(tvb, n) == 0)
+                    break;
+            }
+            break;
+
+        /* SSL request */
+        case 80877103:
+            /* There's nothing to parse here, but what do we do if the
+               SSL negotiation succeeds? */
+            break;
+
+        /* Cancellation request */
+        case 80877102:
+            proto_tree_add_item(tree, hf_pid, tvb, n, 4, FALSE);
+            proto_tree_add_item(tree, hf_key, tvb, n+4, 4, FALSE);
+            break;
+        }
+        break;
+
+    /* Copy data */
+    case 'd':
+        proto_tree_add_item(tree, hf_copydata, tvb, n, length-n+1, FALSE);
+        break;
+
+    /* Copy failure */
+    case 'f':
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_error, tvb, n, l, s);
+        g_free(s);
+        break;
+
+    /* Function call */
+    case 'F':
+        proto_tree_add_item(tree, hf_oid, tvb, n, 4, FALSE);
+        n += 4;
+
+        i = tvb_get_ntohs(tvb, n);
+        ti = proto_tree_add_text(tree, tvb, n, 2, "Parameter formats: %d", i);
+        shrub = proto_item_add_subtree(ti, ett_values);
+        n += 2;
+        while (i-- > 0) {
+            proto_tree_add_item(shrub, hf_format, tvb, n, 2, FALSE);
+            n += 2;
+        }
+
+        i = tvb_get_ntohs(tvb, n);
+        ti = proto_tree_add_text(tree, tvb, n, 2, "Parameter values: %d", i);
+        shrub = proto_item_add_subtree(ti, ett_values);
+        n += 2;
+        while (i-- > 0) {
+            l = tvb_get_ntohl(tvb, n);
+            proto_tree_add_item(shrub, hf_val_length, tvb, n, 4, FALSE);
+            n += 4;
+            if (l > 0)
+                proto_tree_add_item(shrub, hf_val_data, tvb, n, l, FALSE);
+            n += l;
+        }
+
+        proto_tree_add_item(tree, hf_format, tvb, n, 2, FALSE);
+        break;
+    }
+}
+
+
+static const value_string be_messages[] = {
+    { 'R', "Authentication request" },
+    { 'K', "Backend key data" },
+    { 'S', "Parameter status" },
+    { '1', "Parse completion" },
+    { '2', "Bind completion" },
+    { '3', "Close completion" },
+    { 'C', "Command completion" },
+    { 't', "Parameter description" },
+    { 'T', "Row description" },
+    { 'D', "Data row" },
+    { 'I', "Empty query" },
+    { 'n', "No data" },
+    { 'E', "Error" },
+    { 'N', "Notice" },
+    { 's', "Portal suspended" },
+    { 'Z', "Ready for query" },
+    { 'A', "Notification" },
+    { 'V', "Function call response" },
+    { 'G', "CopyIn response" },
+    { 'H', "CopyOut response" },
+    { 'd', "Copy data" },
+    { 'c', "Copy completion" },
+    { 0, NULL }
+};
+
+
+static void dissect_pgsql_be_msg(guchar type, guint32 n, guint32 length,
+                          tvbuff_t *tvb, proto_tree *tree)
+{
+    guchar c;
+    char *s, *t;
+    guint32 i, l;
+    proto_item *ti;
+    proto_tree *shrub;
+
+    switch (type) {
+    /* Authentication request */
+    case 'R':
+        proto_tree_add_item(tree, hf_authtype, tvb, n, 4, FALSE);
+        i = tvb_get_ntohl(tvb, n);
+        if (i == 4 || i == 5) {
+            /* i -= (6-i); :-) */
+            n += 4;
+            l = (i == 4 ? 2 : 4);
+            proto_tree_add_item(tree, hf_salt, tvb, n, l, FALSE);
+        }
+        break;
+
+    /* Key data */
+    case 'K':
+        proto_tree_add_item(tree, hf_pid, tvb, n, 4, FALSE);
+        proto_tree_add_item(tree, hf_key, tvb, n+4, 4, FALSE);
+        break;
+
+    /* Parameter status */
+    case 'S':
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string_hidden(tree, hf_parameter_name, tvb, n, l, s);
+        n += l;
+        t = tvb_get_stringz(tvb, n, &i);
+        proto_tree_add_string_hidden(tree, hf_parameter_value, tvb, n, i, t);
+        proto_tree_add_text(tree, tvb, n-l, l+i, "%s: %s", s, t);
+        g_free(s);
+        g_free(t);
+        break;
+
+    /* Parameter description */
+    case 't':
+        i = tvb_get_ntohs(tvb, n);
+        proto_tree_add_text(tree, tvb, n, 2, "Parameters: %d", i);
+        n += 2;
+        while (i-- > 0) {
+            proto_tree_add_item(tree, hf_typeoid, tvb, n, 4, FALSE);
+            n += 4;
+        }
+        break;
+
+    /* Row description */
+    case 'T':
+        i = tvb_get_ntohs(tvb, n);
+        ti = proto_tree_add_text(tree, tvb, n, 2, "Columns: %d", i);
+        shrub = proto_item_add_subtree(ti, ett_values);
+        n += 2;
+        while (i-- > 0) {
+            proto_tree *twig;
+            s = tvb_get_stringz(tvb, n, &l);
+            ti = proto_tree_add_string(shrub, hf_val_name, tvb, n, l, s);
+            twig = proto_item_add_subtree(ti, ett_values);
+            g_free(s);
+            n += l;
+            proto_tree_add_item(twig, hf_tableoid, tvb, n, 4, FALSE);
+            n += 4;
+            proto_tree_add_item(twig, hf_val_idx, tvb, n, 2, FALSE);
+            n += 2;
+            proto_tree_add_item(twig, hf_typeoid, tvb, n, 4, FALSE);
+            n += 4;
+            proto_tree_add_item(twig, hf_val_length, tvb, n, 2, FALSE);
+            n += 2;
+            proto_tree_add_item(twig, hf_val_mod, tvb, n, 4, FALSE);
+            n += 4;
+            proto_tree_add_item(twig, hf_format, tvb, n, 2, FALSE);
+            n += 2;
+        }
+        break;
+
+    /* Data row */
+    case 'D':
+        i = tvb_get_ntohs(tvb, n);
+        ti = proto_tree_add_text(tree, tvb, n, 2, "Columns: %d", i);
+        shrub = proto_item_add_subtree(ti, ett_values);
+        n += 2;
+        while (i-- > 0) {
+            l = tvb_get_ntohl(tvb, n);
+            proto_tree_add_int(shrub, hf_val_length, tvb, n, 4, l);
+            n += 4;
+            if (l > 0)
+                proto_tree_add_item(shrub, hf_val_data, tvb, n, l, FALSE);
+            n += l;
+        }
+        break;
+
+    /* Command completion */
+    case 'C':
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_tag, tvb, n, l, s);
+        g_free(s);
+        break;
+
+    /* Ready */
+    case 'Z':
+        proto_tree_add_item(tree, hf_status, tvb, n, 1, FALSE);
+        break;
+
+    /* Error, Notice */
+    case 'E':
+    case 'N':
+        length -= 4;
+        while (length > 0) {
+            c = tvb_get_guint8(tvb, n);
+            if (c == '\0')
+                break;
+            s = tvb_get_stringz(tvb, n+1, &l);
+            i = hf_text;
+            switch (c) {
+            case 'S': i = hf_severity; break;
+            case 'C': i = hf_code; break;
+            case 'M': i = hf_message; break;
+            case 'D': i = hf_detail; break;
+            case 'H': i = hf_hint; break;
+            case 'P': i = hf_position; break;
+            case 'W': i = hf_where; break;
+            case 'F': i = hf_file; break;
+            case 'L': i = hf_line; break;
+            case 'R': i = hf_routine; break;
+            }
+            proto_tree_add_string(tree, i, tvb, n, l+1, s);
+            g_free(s);
+            n += l+1;
+        }
+        break;
+
+    /* NOTICE response */
+    case 'A':
+        proto_tree_add_item(tree, hf_pid, tvb, n, 4, FALSE);
+        n += 4;
+        s = tvb_get_stringz(tvb, n, &l);
+        proto_tree_add_string(tree, hf_condition, tvb, n, l, s);
+        g_free(s);
+        n += l;
+        s = tvb_get_stringz(tvb, n, &l);
+        if (l > 1)
+            proto_tree_add_string(tree, hf_text, tvb, n, l, s);
+        g_free(s);
+        break;
+
+    /* Copy in/out */
+    case 'G':
+    case 'H':
+        proto_tree_add_item(tree, hf_format, tvb, n, 1, FALSE);
+        n += 1;
+        i = tvb_get_ntohs(tvb, n);
+        ti = proto_tree_add_text(tree, tvb, n, 2, "Columns: %d", i);
+        shrub = proto_item_add_subtree(ti, ett_values);
+        n += 2;
+        while (i-- > 2) {
+            proto_tree_add_item(shrub, hf_format, tvb, n, 2, FALSE);
+            n += 2;
+        }
+        break;
+
+    /* Copy data */
+    case 'd':
+        proto_tree_add_item(tree, hf_copydata, tvb, n, length-n+1, FALSE);
+        break;
+
+    /* Function call response */
+    case 'V':
+        l = tvb_get_ntohl(tvb, n);
+        proto_tree_add_int(tree, hf_val_length, tvb, n, 4, l);
+        if (l > 0)
+            proto_tree_add_item(tree, hf_val_data, tvb, n+4, l, FALSE);
+        break;
+    }
+}
+
+
+/* This is like specifying VALS(messages) for hf_type, which we can't do
+   directly because of messages without type bytes, and because the type
+   interpretation depends on fe. */
+
+static char *identify(gboolean fe, guchar type)
+{
+    int i = 0;
+    const value_string *messages;
+
+    if (fe)
+        messages = fe_messages;
+    else
+        messages = be_messages;
+
+    while (messages[i].strptr) {
+        if (messages[i].value == type)
+            return messages[i].strptr;
+        i++;
+    }
+
+    return "Unknown";
+}
diff --git a/epan/dissectors/packet-postgresql.c b/epan/dissectors/packet-postgresql.c
deleted file mode 100644 (file)
index 651ba1f..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/* packet-postgresql.c
- * Routines for postgresql packet disassembly
- *
- * Copyright 2004, Edwin Calo <calo@fusemail.com>
- *
- * $Id$
- *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
- * Copyright 1998 Gerald Combs
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <glib.h>
-#include <epan/packet.h>
-
-static int proto_postgresql = -1;
-static int hf_postgresql_response = -1;
-static int hf_postgresql_request = -1;
-static int hf_postgresql_length = -1;
-static int hf_postgresql_string_size = -1;
-static int hf_postgresql_string = -1;
-static int hf_postgresql_total_length = -1;
-static int hf_postgresql_bitone = -1;
-static int hf_postgresql_buff_remaining = -1;
-static int hf_postgresql_opcode = -1;
-static int hf_postgresql_idone = -1;
-static gint ett_postgresql = -1;
-
-#define TCP_PORT_POSTGRESQL    5432
-
-/*
- * For a protocol description, see:
- *
- *     http://www.postgresql.org/docs/7.4/interactive/protocol.html
- *
- * for version 3.0 of the protocol and
- *
- *     http://www.postgresql.org/docs/7.3/interactive/protocol.html
- *
- * for version 2.0 of the protocol and
- *
- *     http://www.postgresql.org/docs/6.3/interactive/c50.htm
- *
- * for version 1.0 of the protocol.
- *
- * It looks like there are two types of PDUs: Old style and new style.
- * New style PDUs start with a Length guint32, where the high byte is
- * equal to 0. Old style PDUs start with a single Byte (different from 0)
- * that contains the type of the PDU.
- *
- * The specific types can be found at:
- * http://www.postgresql.org/docs/7.4/interactive/protocol-message-formats.html
- * 
- */
-
-static void
-dissect_postgresql (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
-{
-  proto_tree *postgresql_tree;
-  proto_item *ti;
-  gint offset = 0;
-  gint buff_remaining = 0;
-
-  guint8 *string;
-  guint8 bitone;
-  gint flag = 0;
-  gint counter = 0;
-
-
-
-
-  if (check_col (pinfo->cinfo, COL_PROTOCOL))
-    col_set_str (pinfo->cinfo, COL_PROTOCOL, "POSTGRESQL");
-
-  ti = proto_tree_add_item (tree, proto_postgresql, tvb, offset, -1, FALSE);
-  postgresql_tree = proto_item_add_subtree (ti, ett_postgresql);
-
-  buff_remaining = tvb_length_remaining (tvb, offset);
-
-  if (check_col (pinfo->cinfo, COL_INFO))
-    {
-      col_add_str (pinfo->cinfo, COL_INFO,
-                  (pinfo->match_port ==
-                   pinfo->destport) ? " Request" : " Response");
-    }
-
-    counter=0;
-    flag=0;
-    while ( buff_remaining >= 1 )
-    {
-         bitone = tvb_get_guint8 (tvb, offset);
-         offset += 1;
-
-         if(bitone > 0x7f || (bitone > 0x0 && bitone < 0x20) ) 
-         {
-           if(counter > 3)
-           {
-             if(offset > counter)
-             {
-                   offset -= counter+1;
-
-                    /* Reading the string from the packet */
-                    string = tvb_get_string(tvb,offset,counter);
-                    /* Printing the data */
-                    proto_tree_add_string (postgresql_tree,hf_postgresql_string,tvb, offset,counter, string );
-                    if (check_col (pinfo->cinfo, COL_INFO)) { col_append_fstr (pinfo->cinfo, COL_INFO, " %s", string ); }
-                   g_free(string);  /* Freeing up string */
-                    string=NULL;
-
-                   offset += counter+1;
-                  counter=0;
-             }
-             else
-             {
-              counter=0;
-              offset+=1;
-             }
-           }
-           else
-           {
-            counter=0;
-            offset+=1;
-           }
-         }
-
-         if( bitone == 0 )
-         {
-                if(counter != 0)
-                { 
-                  if(offset > counter)
-                  {
-                   offset -= counter+1;
-                  if( counter > 1)
-                  {
-                    /* Reading the string from the packet */
-                   string = tvb_get_string(tvb,offset,counter);
-                    /* Printing the data */
-                    proto_tree_add_string (postgresql_tree,hf_postgresql_string,tvb, offset,counter, string );
-                    if (check_col (pinfo->cinfo, COL_INFO)) { col_append_fstr (pinfo->cinfo, COL_INFO, " %s", string ); }
-                   g_free(string);  /* Freeing up string */
-                    string=NULL;
-
-                   }
-                  offset += counter+1;
-                  }
-                  counter = 0;
-                }
-                counter=0;
-         }
-         else
-         {
-             counter += 1;
-         }
-
-         buff_remaining = tvb_length_remaining (tvb, offset);
-     }
-} 
-
-
-void proto_register_postgresql (void)
-{
-
-  static hf_register_info hf[] = {
-    {&hf_postgresql_response,
-     {"Response", "postgresql.response",
-      FT_BOOLEAN, BASE_NONE, NULL, 0x0,
-      "TRUE if postgresql response", HFILL}},
-    {&hf_postgresql_request,
-     {"Request", "postgresql.request",
-      FT_BOOLEAN, BASE_NONE, NULL, 0x0,
-      "TRUE if postgresql request", HFILL}},
-    {&hf_postgresql_string, {"String", "hf_postgresql_string", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL}},
-    {&hf_postgresql_length, {"Length", "hf_postgresql_length", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
-    {&hf_postgresql_string_size, {"Size", "hf_postgresql_string_size", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}},
-    {&hf_postgresql_total_length, {"TotalLength", "hf_postgresql_total_length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
-    {&hf_postgresql_buff_remaining, {"Buffer Remaining", "hf_postgresql_buff_remaining", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}},
-    {&hf_postgresql_opcode, {"Op Code", "hf_postgresql_opcode", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL}},
-    {&hf_postgresql_bitone, {"Bitone", "hf_postgresql_bitone", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL}},
-    {&hf_postgresql_idone, {"idone", "hf_postgresql_idone", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL}},
-
-
-  };
-
-  static gint *ett[] = {
-    &ett_postgresql,
-  };
-
-  proto_postgresql =
-    proto_register_protocol ("POSTGRESQL", "POSTGRESQL", "postgresql");
-  proto_register_field_array (proto_postgresql, hf, array_length (hf));
-  proto_register_subtree_array (ett, array_length (ett));
-}
-
-void
-proto_reg_handoff_postgresql (void)
-{
-  dissector_handle_t postgresql_handle;
-
-  postgresql_handle =
-    create_dissector_handle (dissect_postgresql, proto_postgresql);
-  dissector_add ("tcp.port", TCP_PORT_POSTGRESQL, postgresql_handle);
-}