Apply the small performance enhancment patches for:
[obnox/wireshark/wip.git] / epan / dissectors / packet-pgsql.c
1 /* packet-pgsql.c
2  * Routines for PostgreSQL v3 protocol dissection.
3  * <http://www.postgresql.org/docs/current/static/protocol.html>
4  * Copyright 2004 Abhijit Menon-Sen <ams@oryx.com>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <glib.h>
32 #include <epan/packet.h>
33 #include <epan/conversation.h>
34 #include <epan/prefs.h>
35 #include <epan/emem.h>
36
37 #include "packet-tcp.h"
38 #include <epan/reassemble.h>
39
40
41 static int proto_pgsql = -1;
42 static int hf_frontend = -1;
43 static int hf_type = -1;
44 static int hf_length = -1;
45 static int hf_parameter_name = -1;
46 static int hf_parameter_value = -1;
47 static int hf_query = -1;
48 static int hf_authtype = -1;
49 static int hf_passwd = -1;
50 static int hf_salt = -1;
51 static int hf_statement = -1;
52 static int hf_portal = -1;
53 static int hf_tag = -1;
54 static int hf_status = -1;
55 static int hf_copydata = -1;
56 static int hf_error = -1;
57 static int hf_pid = -1;
58 static int hf_key = -1;
59 static int hf_condition = -1;
60 static int hf_text = -1;
61 static int hf_tableoid = -1;
62 static int hf_typeoid = -1;
63 static int hf_oid = -1;
64 static int hf_format = -1;
65 static int hf_val_name = -1;
66 static int hf_val_idx = -1;
67 static int hf_val_length = -1;
68 static int hf_val_data = -1;
69 static int hf_val_mod = -1;
70 static int hf_severity = -1;
71 static int hf_code = -1;
72 static int hf_message = -1;
73 static int hf_detail = -1;
74 static int hf_hint = -1;
75 static int hf_position = -1;
76 static int hf_where = -1;
77 static int hf_file = -1;
78 static int hf_line = -1;
79 static int hf_routine = -1;
80
81 static gint ett_pgsql = -1;
82 static gint ett_values = -1;
83
84 static guint pgsql_port = 5432;
85 static gboolean pgsql_desegment = TRUE;
86 static gboolean first_message = TRUE;
87
88 static void dissect_pgsql_fe_msg(guchar, guint, tvbuff_t *, gint, proto_tree *);
89 static void dissect_pgsql_be_msg(guchar, guint, tvbuff_t *, gint, proto_tree *);
90 static void dissect_pgsql_msg(tvbuff_t *, packet_info *, proto_tree *);
91 static void dissect_pgsql(tvbuff_t *, packet_info *, proto_tree *);
92 static guint pgsql_length(packet_info *, tvbuff_t *, int);
93
94 static const value_string fe_messages[] = {
95     { 'p', "Password message" },
96     { 'Q', "Simple query" },
97     { 'P', "Parse" },
98     { 'B', "Bind" },
99     { 'E', "Execute" },
100     { 'D', "Describe" },
101     { 'C', "Close" },
102     { 'H', "Flush" },
103     { 'S', "Sync" },
104     { 'F', "Function call" },
105     { 'd', "Copy data" },
106     { 'c', "Copy completion" },
107     { 'f', "Copy failure" },
108     { 'X', "Termination" },
109     { 0, NULL }
110 };
111
112 static const value_string be_messages[] = {
113     { 'R', "Authentication request" },
114     { 'K', "Backend key data" },
115     { 'S', "Parameter status" },
116     { '1', "Parse completion" },
117     { '2', "Bind completion" },
118     { '3', "Close completion" },
119     { 'C', "Command completion" },
120     { 't', "Parameter description" },
121     { 'T', "Row description" },
122     { 'D', "Data row" },
123     { 'I', "Empty query" },
124     { 'n', "No data" },
125     { 'E', "Error" },
126     { 'N', "Notice" },
127     { 's', "Portal suspended" },
128     { 'Z', "Ready for query" },
129     { 'A', "Notification" },
130     { 'V', "Function call response" },
131     { 'G', "CopyIn response" },
132     { 'H', "CopyOut response" },
133     { 'd', "Copy data" },
134     { 'c', "Copy completion" },
135     { 0, NULL }
136 };
137
138
139 static const value_string auth_types[] = {
140     { 0, "Success" },
141     { 1, "Kerberos V4" },
142     { 2, "Kerberos V5" },
143     { 3, "Plaintext password" },
144     { 4, "crypt()ed password" },
145     { 5, "MD5 password" },
146     { 6, "SCM credentials" },
147     { 0, NULL }
148 };
149
150 static const value_string status_vals[] = {
151     { 'I', "Idle" },
152     { 'T', "In a transaction" },
153     { 'E', "In a failed transaction" },
154     { 0, NULL }
155 };
156
157 static const value_string format_vals[] = {
158     { 0, "Text" },
159     { 1, "Binary" },
160     { 0, NULL }
161 };
162
163
164 void
165 proto_reg_handoff_pgsql(void)
166 {
167     dissector_handle_t pgsql_handle;
168
169     pgsql_handle = create_dissector_handle(dissect_pgsql, proto_pgsql);
170     dissector_add("tcp.port", pgsql_port, pgsql_handle);
171 }
172
173
174 void
175 proto_register_pgsql(void)
176 {
177     static hf_register_info hf[] = {
178         { &hf_frontend,
179           { "Frontend", "pgsql.frontend", FT_BOOLEAN, BASE_NONE, NULL, 0,
180             "True for messages from the frontend, false otherwise.",
181             HFILL }
182         },
183         { &hf_type,
184           { "Type", "pgsql.type", FT_STRING, BASE_NONE, NULL, 0,
185             "A one-byte message type identifier.", HFILL }
186         },
187         { &hf_length,
188           { "Length", "pgsql.length", FT_UINT32, BASE_DEC, NULL, 0,
189             "The length of the message (not including the type).",
190             HFILL }
191         },
192         { &hf_parameter_name,
193           { "Parameter name", "pgsql.parameter_name", FT_STRINGZ,
194             BASE_NONE, NULL, 0, "The name of a database parameter.",
195             HFILL }
196         },
197         { &hf_parameter_value,
198           { "Parameter value", "pgsql.parameter_value", FT_STRINGZ,
199             BASE_NONE, NULL, 0, "The value of a database parameter.",
200             HFILL }
201         },
202         { &hf_query,
203           { "Query", "pgsql.query", FT_STRINGZ, BASE_NONE, NULL, 0,
204             "A query string.", HFILL }
205         },
206         { &hf_passwd,
207           { "Password", "pgsql.password", FT_STRINGZ, BASE_NONE, NULL, 0,
208             "A password.", HFILL }
209         },
210         { &hf_authtype,
211           { "Authentication type", "pgsql.authtype", FT_INT32, BASE_DEC,
212             VALS(auth_types), 0,
213             "The type of authentication requested by the backend.", HFILL }
214         },
215         { &hf_salt,
216           { "Salt value", "pgsql.salt", FT_BYTES, BASE_HEX, NULL, 0,
217             "The salt to use while encrypting a password.", HFILL }
218         },
219         { &hf_statement,
220           { "Statement", "pgsql.statement", FT_STRINGZ, BASE_NONE, NULL, 0,
221             "The name of a prepared statement.", HFILL }
222         },
223         { &hf_portal,
224           { "Portal", "pgsql.portal", FT_STRINGZ, BASE_NONE, NULL, 0,
225             "The name of a portal.", HFILL }
226         },
227         { &hf_tag,
228           { "Tag", "pgsql.tag", FT_STRINGZ, BASE_NONE, NULL, 0,
229             "A completion tag.", HFILL }
230         },
231         { &hf_status,
232           { "Status", "pgsql.status", FT_UINT8, BASE_DEC, VALS(status_vals),
233             0, "The transaction status of the backend.", HFILL }
234         },
235         { &hf_copydata,
236           { "Copy data", "pgsql.copydata", FT_BYTES, BASE_NONE, NULL, 0,
237             "Data sent following a Copy-in or Copy-out response.", HFILL }
238         },
239         { &hf_error,
240           { "Error", "pgsql.error", FT_STRINGZ, BASE_NONE, NULL, 0,
241             "An error message.", HFILL }
242         },
243         { &hf_pid,
244           { "PID", "pgsql.pid", FT_UINT32, BASE_DEC, NULL, 0,
245             "The process ID of a backend.", HFILL }
246         },
247         { &hf_key,
248           { "Key", "pgsql.key", FT_UINT32, BASE_DEC, NULL, 0,
249             "The secret key used by a particular backend.", HFILL }
250         },
251         { &hf_condition,
252           { "Condition", "pgsql.condition", FT_STRINGZ, BASE_NONE, NULL, 0,
253             "The name of a NOTIFY condition.", HFILL }
254         },
255         { &hf_text,
256           { "Text", "pgsql.text", FT_STRINGZ, BASE_NONE, NULL, 0,
257             "Text from the backend.", HFILL }
258         },
259         { &hf_tableoid,
260           { "Table OID", "pgsql.oid.table", FT_UINT32, BASE_DEC, NULL, 0,
261             "The object identifier of a table.", HFILL }
262         },
263         { &hf_typeoid,
264           { "Type OID", "pgsql.oid.type", FT_UINT32, BASE_DEC, NULL, 0,
265             "The object identifier of a type.", HFILL }
266         },
267         { &hf_oid,
268           { "OID", "pgsql.oid", FT_UINT32, BASE_DEC, NULL, 0,
269             "An object identifier.", HFILL }
270         },
271         { &hf_format,
272           { "Format", "pgsql.format", FT_UINT16, BASE_DEC, VALS(format_vals),
273             0, "A format specifier.", HFILL }
274         },
275         { &hf_val_name,
276           { "Column name", "pgsql.col.name", FT_STRINGZ, BASE_NONE, NULL, 0,
277             "The name of a column.", HFILL }
278         },
279         { &hf_val_idx,
280           { "Column index", "pgsql.col.index", FT_UINT32, BASE_DEC, NULL, 0,
281             "The position of a column within a row.", HFILL }
282         },
283         { &hf_val_length,
284           { "Column length", "pgsql.val.length", FT_INT32, BASE_DEC, NULL, 0,
285             "The length of a parameter value, in bytes. -1 means NULL.",
286             HFILL }
287         },
288         { &hf_val_data,
289           { "Data", "pgsql.val.data", FT_BYTES, BASE_NONE, NULL, 0,
290             "Parameter data.", HFILL }
291         },
292         { &hf_val_mod,
293           { "Type modifier", "pgsql.col.typemod", FT_INT32, BASE_DEC, NULL, 0,
294             "The type modifier for a column.", HFILL }
295         },
296         { &hf_severity,
297           { "Severity", "pgsql.severity", FT_STRINGZ, BASE_NONE, NULL, 0,
298             "Message severity.", HFILL }
299         },
300         { &hf_code,
301           { "Code", "pgsql.code", FT_STRINGZ, BASE_NONE, NULL, 0,
302             "SQLState code.", HFILL }
303         },
304         { &hf_message,
305           { "Message", "pgsql.message", FT_STRINGZ, BASE_NONE, NULL, 0,
306             "Error message.", HFILL }
307         },
308         { &hf_detail,
309           { "Detail", "pgsql.detail", FT_STRINGZ, BASE_NONE, NULL, 0,
310             "Detailed error message.", HFILL }
311         },
312         { &hf_hint,
313           { "Hint", "pgsql.hint", FT_STRINGZ, BASE_NONE, NULL, 0,
314             "A suggestion to resolve an error.", HFILL }
315         },
316         { &hf_position,
317           { "Position", "pgsql.position", FT_STRINGZ, BASE_NONE, NULL, 0,
318             "The index of the error within the query string.", HFILL }
319         },
320         { &hf_where,
321           { "Context", "pgsql.where", FT_STRINGZ, BASE_NONE, NULL, 0,
322             "The context in which an error occurred.", HFILL }
323         },
324         { &hf_file,
325           { "File", "pgsql.file", FT_STRINGZ, BASE_NONE, NULL, 0,
326             "The source-code file where an error was reported.", HFILL }
327         },
328         { &hf_line,
329           { "Line", "pgsql.line", FT_STRINGZ, BASE_NONE, NULL, 0,
330             "The line number on which an error was reported.", HFILL }
331         },
332         { &hf_routine,
333           { "Routine", "pgsql.routine", FT_STRINGZ, BASE_NONE, NULL, 0,
334             "The routine that reported an error.", HFILL }
335         }
336     };
337
338     static gint *ett[] = {
339         &ett_pgsql,
340         &ett_values
341     };
342
343     module_t *mod_pgsql;
344
345     proto_pgsql = proto_register_protocol("PostgreSQL", "PGSQL", "pgsql");
346     proto_register_field_array(proto_pgsql, hf, array_length(hf));
347     proto_register_subtree_array(ett, array_length(ett));
348
349     mod_pgsql = prefs_register_protocol(proto_pgsql, NULL);
350     prefs_register_uint_preference(
351         mod_pgsql, "tcp.port", "PGSQL TCP port", "Set the port for PGSQL "
352         "messages (if different from the default of 5432)", 10, &pgsql_port
353     );
354 }
355
356
357 /* This function is called once per TCP packet. It sets COL_PROTOCOL and
358  * identifies FE/BE messages by adding a ">" or "<" to COL_INFO. Then it
359  * arranges for each message to be dissected individually. */
360
361 static void
362 dissect_pgsql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
363 {
364     conversation_t *cv;
365
366     first_message = TRUE;
367
368     /* We don't use conversation data yet, but... */
369     cv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
370                            pinfo->srcport, pinfo->destport, 0);
371     if (!cv) {
372         cv = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
373                               pinfo->srcport, pinfo->destport, 0);
374     }
375
376     if (check_col(pinfo->cinfo, COL_PROTOCOL))
377         col_set_str(pinfo->cinfo, COL_PROTOCOL, "PGSQL");
378     if (check_col(pinfo->cinfo, COL_INFO))
379         col_set_str(pinfo->cinfo, COL_INFO,
380                     (pinfo->match_port == pinfo->destport) ?
381                      ">" : "<");
382
383     tcp_dissect_pdus(tvb, pinfo, tree, pgsql_desegment, 5,
384                      pgsql_length, dissect_pgsql_msg);
385 }
386
387
388 /* This function is called by tcp_dissect_pdus() to find the size of the
389    message starting at tvb[offset]. */
390
391 static guint
392 pgsql_length(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
393 {
394     gint n = 0;
395     guchar type;
396     guint length;
397
398     /* The length is either the four bytes after the type, or, if the
399        type is 0, the first four bytes. */
400     type = tvb_get_guint8(tvb, offset);
401     if (type != '\0')
402         n = 1;
403     length = tvb_get_ntohl(tvb, offset+n);
404     return length+n;
405 }
406
407
408 /* This function is responsible for dissecting a single message. */
409
410 static void
411 dissect_pgsql_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
412 {
413     proto_item *ti;
414     proto_tree *ptree;
415
416     gint n;
417     guchar type;
418     const char *typestr;
419     guint length;
420     gboolean info = check_col(pinfo->cinfo, COL_INFO);
421     gboolean fe = (pinfo->match_port == pinfo->destport);
422
423     n = 0;
424     type = tvb_get_guint8(tvb, 0);
425     if (type != '\0')
426         n += 1;
427     length = tvb_get_ntohl(tvb, n);
428
429     /* This is like specifying VALS(messages) for hf_type, which we can't do
430        directly because of messages without type bytes, and because the type
431        interpretation depends on fe. */
432     if (fe) {
433         /* There are a few frontend messages that have no leading type byte.
434            We identify them by the fact that the first byte of their length
435            must be zero, and that the next four bytes are a unique tag. */
436         if (type == '\0') {
437             guint tag = tvb_get_ntohl(tvb, 4);
438
439             if (length == 16 && tag == 80877102)
440                 typestr = "Cancel request";
441             else if (length == 8 && tag == 80877103)
442                 typestr = "SSL request";
443             else if (tag == 196608)
444                 typestr = "Startup message";
445             else
446                 typestr = "Unknown";
447         } else
448             typestr = val_to_str(type, fe_messages, "Unknown");
449     }
450     else {
451         typestr = val_to_str(type, be_messages, "Unknown");
452     }
453
454     if (info) {
455         /* This is a terrible hack. It makes the "Info" column reflect
456            the contents of every message in a TCP packet. Could it be
457            done any better? */
458         col_append_fstr(pinfo->cinfo, COL_INFO, "%s%c",
459                         ( first_message ? "" : "/" ), type);
460         first_message = FALSE;
461     }
462
463     if (tree) {
464         ti = proto_tree_add_item(tree, proto_pgsql, tvb, 0, -1, FALSE);
465         ptree = proto_item_add_subtree(ti, ett_pgsql);
466
467         n = 1;
468         if (type == '\0')
469             n = 0;
470         proto_tree_add_text(ptree, tvb, 0, n, "Type: %s", typestr);
471         proto_tree_add_item_hidden(ptree, hf_type, tvb, 0, n, FALSE);
472         proto_tree_add_item(ptree, hf_length, tvb, n, 4, FALSE);
473         proto_tree_add_boolean_hidden(ptree, hf_frontend, tvb, 0, 0, fe);
474         n += 4;
475
476         if (fe)
477             dissect_pgsql_fe_msg(type, length, tvb, n, ptree);
478         else
479             dissect_pgsql_be_msg(type, length, tvb, n, ptree);
480     }
481 }
482
483
484 static void dissect_pgsql_fe_msg(guchar type, guint length, tvbuff_t *tvb,
485                                  gint n, proto_tree *tree)
486 {
487     guchar c;
488     gint i, l;
489     char *s, *t;
490     proto_item *ti;
491     proto_tree *shrub;
492
493     switch (type) {
494     /* Password */
495     case 'p':
496         s = tvb_get_ephemeral_stringz(tvb, n, &l);
497         proto_tree_add_string(tree, hf_passwd, tvb, n, l, s);
498         break;
499
500     /* Simple query */
501     case 'Q':
502         s = tvb_get_ephemeral_stringz(tvb, n, &l);
503         proto_tree_add_string(tree, hf_query, tvb, n, l, s);
504         break;
505
506     /* Parse */
507     case 'P':
508         s = tvb_get_ephemeral_stringz(tvb, n, &l);
509         proto_tree_add_string(tree, hf_statement, tvb, n, l, s);
510         n += l;
511
512         s = tvb_get_ephemeral_stringz(tvb, n, &l);
513         proto_tree_add_string(tree, hf_query, tvb, n, l, s);
514         n += l;
515
516         i = tvb_get_ntohs(tvb, n);
517         ti = proto_tree_add_text(tree, tvb, n, 2, "Parameters: %d", i);
518         shrub = proto_item_add_subtree(ti, ett_values);
519         n += 2;
520         while (i-- > 0) {
521             proto_tree_add_item(shrub, hf_typeoid, tvb, n, 4, FALSE);
522             n += 4;
523         }
524         break;
525
526     /* Bind */
527     case 'B':
528         s = tvb_get_ephemeral_stringz(tvb, n, &l);
529         proto_tree_add_string(tree, hf_portal, tvb, n, l, s);
530         n += l;
531
532         s = tvb_get_ephemeral_stringz(tvb, n, &l);
533         proto_tree_add_string(tree, hf_statement, tvb, n, l, s);
534         n += l;
535
536         i = tvb_get_ntohs(tvb, n);
537         ti = proto_tree_add_text(tree, tvb, n, 2, "Parameter formats: %d", i);
538         shrub = proto_item_add_subtree(ti, ett_values);
539         n += 2;
540         while (i-- > 0) {
541             proto_tree_add_item(shrub, hf_format, tvb, n, 2, FALSE);
542             n += 2;
543         }
544
545         i = tvb_get_ntohs(tvb, n);
546         ti = proto_tree_add_text(tree, tvb, n, 2, "Parameter values: %d", i);
547         shrub = proto_item_add_subtree(ti, ett_values);
548         n += 2;
549         while (i-- > 0) {
550             l = tvb_get_ntohl(tvb, n);
551             proto_tree_add_int(shrub, hf_val_length, tvb, n, 4, l);
552             n += 4;
553             if (l > 0) {
554                 proto_tree_add_item(shrub, hf_val_data, tvb, n, l, FALSE);
555                 n += l;
556             }
557         }
558
559         i = tvb_get_ntohs(tvb, n);
560         ti = proto_tree_add_text(tree, tvb, n, 2, "Result formats: %d", i);
561         shrub = proto_item_add_subtree(ti, ett_values);
562         n += 2;
563         while (i-- > 0) {
564             proto_tree_add_item(shrub, hf_format, tvb, n, 2, FALSE);
565             n += 2;
566         }
567         break;
568
569     /* Execute */
570     case 'E':
571         s = tvb_get_ephemeral_stringz(tvb, n, &l);
572         proto_tree_add_string(tree, hf_portal, tvb, n, l, s);
573         n += l;
574
575         ti = proto_tree_add_text(tree, tvb, n, 4, "Returns: ");
576         i = tvb_get_ntohl(tvb, n);
577         if (i == 0)
578             proto_item_append_text(ti, "all");
579         else
580             proto_item_append_text(ti, "%d", i);
581         proto_item_append_text(ti, " rows");
582         break;
583
584     /* Describe, Close */
585     case 'D':
586     case 'C':
587         i = 0;
588         c = tvb_get_guint8(tvb, n);
589         if (c == 'P')
590             i = hf_portal;
591         else
592             i = hf_statement;
593
594         if (i != 0) {
595             n += 1;
596             s = tvb_get_ephemeral_stringz(tvb, n, &l);
597             proto_tree_add_string_hidden(tree, i, tvb, n, l, s);
598             proto_tree_add_text(
599                 tree, tvb, n-1, l, "%s: %s",
600                 (c == 'P' ? "Portal" : "Statement"), s
601             );
602         }
603         break;
604
605     /* Messages without a type identifier */
606     case '\0':
607         i = tvb_get_ntohl(tvb, n);
608         n += 4;
609         length -= n;
610         switch (i) {
611         /* Startup message */
612         case 196608:
613             while (length > 0) {
614                 s = tvb_get_ephemeral_stringz(tvb, n, &l);
615                 length -= l;
616                 if (length <= 0) {
617                     break;
618                 }
619                 t = tvb_get_ephemeral_stringz(tvb, n+l, &i);
620                 proto_tree_add_text(tree, tvb, n, l+i, "%s: %s", s, t);
621                 n += l+i;
622                 length -= i;
623                 if (length == 1 && tvb_get_guint8(tvb, n) == 0)
624                     break;
625             }
626             break;
627
628         /* SSL request */
629         case 80877103:
630             /* There's nothing to parse here, but what do we do if the
631                SSL negotiation succeeds? */
632             break;
633
634         /* Cancellation request */
635         case 80877102:
636             proto_tree_add_item(tree, hf_pid, tvb, n, 4, FALSE);
637             proto_tree_add_item(tree, hf_key, tvb, n+4, 4, FALSE);
638             break;
639         }
640         break;
641
642     /* Copy data */
643     case 'd':
644         proto_tree_add_item(tree, hf_copydata, tvb, n, length-n+1, FALSE);
645         break;
646
647     /* Copy failure */
648     case 'f':
649         s = tvb_get_ephemeral_stringz(tvb, n, &l);
650         proto_tree_add_string(tree, hf_error, tvb, n, l, s);
651         break;
652
653     /* Function call */
654     case 'F':
655         proto_tree_add_item(tree, hf_oid, tvb, n, 4, FALSE);
656         n += 4;
657
658         i = tvb_get_ntohs(tvb, n);
659         ti = proto_tree_add_text(tree, tvb, n, 2, "Parameter formats: %d", i);
660         shrub = proto_item_add_subtree(ti, ett_values);
661         n += 2;
662         while (i-- > 0) {
663             proto_tree_add_item(shrub, hf_format, tvb, n, 2, FALSE);
664             n += 2;
665         }
666
667         i = tvb_get_ntohs(tvb, n);
668         ti = proto_tree_add_text(tree, tvb, n, 2, "Parameter values: %d", i);
669         shrub = proto_item_add_subtree(ti, ett_values);
670         n += 2;
671         while (i-- > 0) {
672             l = tvb_get_ntohl(tvb, n);
673             proto_tree_add_item(shrub, hf_val_length, tvb, n, 4, FALSE);
674             n += 4;
675             if (l > 0) {
676                 proto_tree_add_item(shrub, hf_val_data, tvb, n, l, FALSE);
677                 n += l;
678             }
679         }
680
681         proto_tree_add_item(tree, hf_format, tvb, n, 2, FALSE);
682         break;
683     }
684 }
685
686
687 static void dissect_pgsql_be_msg(guchar type, guint length, tvbuff_t *tvb,
688                                  gint n, proto_tree *tree)
689 {
690     guchar c;
691     gint i, l;
692     char *s, *t;
693     proto_item *ti;
694     proto_tree *shrub;
695
696     switch (type) {
697     /* Authentication request */
698     case 'R':
699         proto_tree_add_item(tree, hf_authtype, tvb, n, 4, FALSE);
700         i = tvb_get_ntohl(tvb, n);
701         if (i == 4 || i == 5) {
702             /* i -= (6-i); :-) */
703             n += 4;
704             l = (i == 4 ? 2 : 4);
705             proto_tree_add_item(tree, hf_salt, tvb, n, l, FALSE);
706         }
707         break;
708
709     /* Key data */
710     case 'K':
711         proto_tree_add_item(tree, hf_pid, tvb, n, 4, FALSE);
712         proto_tree_add_item(tree, hf_key, tvb, n+4, 4, FALSE);
713         break;
714
715     /* Parameter status */
716     case 'S':
717         s = tvb_get_ephemeral_stringz(tvb, n, &l);
718         proto_tree_add_string_hidden(tree, hf_parameter_name, tvb, n, l, s);
719         n += l;
720         t = tvb_get_ephemeral_stringz(tvb, n, &i);
721         proto_tree_add_string_hidden(tree, hf_parameter_value, tvb, n, i, t);
722         proto_tree_add_text(tree, tvb, n-l, l+i, "%s: %s", s, t);
723         break;
724
725     /* Parameter description */
726     case 't':
727         i = tvb_get_ntohs(tvb, n);
728         proto_tree_add_text(tree, tvb, n, 2, "Parameters: %d", i);
729         n += 2;
730         while (i-- > 0) {
731             proto_tree_add_item(tree, hf_typeoid, tvb, n, 4, FALSE);
732             n += 4;
733         }
734         break;
735
736     /* Row description */
737     case 'T':
738         i = tvb_get_ntohs(tvb, n);
739         ti = proto_tree_add_text(tree, tvb, n, 2, "Columns: %d", i);
740         shrub = proto_item_add_subtree(ti, ett_values);
741         n += 2;
742         while (i-- > 0) {
743             proto_tree *twig;
744             s = tvb_get_ephemeral_stringz(tvb, n, &l);
745             ti = proto_tree_add_string(shrub, hf_val_name, tvb, n, l, s);
746             twig = proto_item_add_subtree(ti, ett_values);
747             n += l;
748             proto_tree_add_item(twig, hf_tableoid, tvb, n, 4, FALSE);
749             n += 4;
750             proto_tree_add_item(twig, hf_val_idx, tvb, n, 2, FALSE);
751             n += 2;
752             proto_tree_add_item(twig, hf_typeoid, tvb, n, 4, FALSE);
753             n += 4;
754             proto_tree_add_item(twig, hf_val_length, tvb, n, 2, FALSE);
755             n += 2;
756             proto_tree_add_item(twig, hf_val_mod, tvb, n, 4, FALSE);
757             n += 4;
758             proto_tree_add_item(twig, hf_format, tvb, n, 2, FALSE);
759             n += 2;
760         }
761         break;
762
763     /* Data row */
764     case 'D':
765         i = tvb_get_ntohs(tvb, n);
766         ti = proto_tree_add_text(tree, tvb, n, 2, "Columns: %d", i);
767         shrub = proto_item_add_subtree(ti, ett_values);
768         n += 2;
769         while (i-- > 0) {
770             l = tvb_get_ntohl(tvb, n);
771             proto_tree_add_int(shrub, hf_val_length, tvb, n, 4, l);
772             n += 4;
773             if (l > 0) {
774                 proto_tree_add_item(shrub, hf_val_data, tvb, n, l, FALSE);
775                 n += l;
776             }
777         }
778         break;
779
780     /* Command completion */
781     case 'C':
782         s = tvb_get_ephemeral_stringz(tvb, n, &l);
783         proto_tree_add_string(tree, hf_tag, tvb, n, l, s);
784         break;
785
786     /* Ready */
787     case 'Z':
788         proto_tree_add_item(tree, hf_status, tvb, n, 1, FALSE);
789         break;
790
791     /* Error, Notice */
792     case 'E':
793     case 'N':
794         length -= 4;
795         while (length > 0) {
796             c = tvb_get_guint8(tvb, n);
797             if (c == '\0')
798                 break;
799             s = tvb_get_ephemeral_stringz(tvb, n+1, &l);
800             i = hf_text;
801             switch (c) {
802             case 'S': i = hf_severity; break;
803             case 'C': i = hf_code; break;
804             case 'M': i = hf_message; break;
805             case 'D': i = hf_detail; break;
806             case 'H': i = hf_hint; break;
807             case 'P': i = hf_position; break;
808             case 'W': i = hf_where; break;
809             case 'F': i = hf_file; break;
810             case 'L': i = hf_line; break;
811             case 'R': i = hf_routine; break;
812             }
813             proto_tree_add_string(tree, i, tvb, n, l+1, s);
814             n += l+1;
815         }
816         break;
817
818     /* NOTICE response */
819     case 'A':
820         proto_tree_add_item(tree, hf_pid, tvb, n, 4, FALSE);
821         n += 4;
822         s = tvb_get_ephemeral_stringz(tvb, n, &l);
823         proto_tree_add_string(tree, hf_condition, tvb, n, l, s);
824         n += l;
825         s = tvb_get_ephemeral_stringz(tvb, n, &l);
826         if (l > 1)
827             proto_tree_add_string(tree, hf_text, tvb, n, l, s);
828         break;
829
830     /* Copy in/out */
831     case 'G':
832     case 'H':
833         proto_tree_add_item(tree, hf_format, tvb, n, 1, FALSE);
834         n += 1;
835         i = tvb_get_ntohs(tvb, n);
836         ti = proto_tree_add_text(tree, tvb, n, 2, "Columns: %d", i);
837         shrub = proto_item_add_subtree(ti, ett_values);
838         n += 2;
839         while (i-- > 2) {
840             proto_tree_add_item(shrub, hf_format, tvb, n, 2, FALSE);
841             n += 2;
842         }
843         break;
844
845     /* Copy data */
846     case 'd':
847         proto_tree_add_item(tree, hf_copydata, tvb, n, length-n+1, FALSE);
848         break;
849
850     /* Function call response */
851     case 'V':
852         l = tvb_get_ntohl(tvb, n);
853         proto_tree_add_int(tree, hf_val_length, tvb, n, 4, l);
854         if (l > 0)
855             proto_tree_add_item(tree, hf_val_data, tvb, n+4, l, FALSE);
856         break;
857     }
858 }