Add HP Switch Protocol SAP value
[obnox/wireshark/wip.git] / epan / dissectors / packet-mysql.c
1 /* packet-mysql.c
2  * Routines for mysql packet dissection
3  *
4  * Huagang XIE <huagang@intruvert.com>
5  *
6  * MySQL 4.1+ protocol by Axel Schwenke <axel@mysql.com>
7  *
8  * $Id$
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1998 Gerald Combs
13  *
14  * Copied from packet-tftp.c
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
29  *
30  *
31  * the protocol spec at
32  *  http://public.logicacmg.com/~redferni/mysql/MySQL-Protocol.html
33  * and MySQL source code
34  */
35
36 /* create extra output for conversation tracking */
37 /* #define CTDEBUG 1 */
38
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42
43 #include <glib.h>
44 #include <epan/packet.h>
45 #include <epan/conversation.h>
46 #include <epan/emem.h>
47
48 #include <epan/dissectors/packet-tcp.h>
49 #include <epan/reassemble.h>
50 #include <epan/prefs.h>
51
52 /* port for protocol registration */
53 #define TCP_PORT_MySQL   3306
54
55 /* client/server capabilities */
56 #define MYSQL_CAPS_LP 0x0001
57 #define MYSQL_CAPS_FR 0x0002
58 #define MYSQL_CAPS_LF 0x0004
59 #define MYSQL_CAPS_CD 0x0008
60 #define MYSQL_CAPS_NS 0x0010
61 #define MYSQL_CAPS_CP 0x0020
62 #define MYSQL_CAPS_OB 0x0040
63 #define MYSQL_CAPS_LI 0x0080
64 #define MYSQL_CAPS_IS 0x0100
65 #define MYSQL_CAPS_CU 0x0200
66 #define MYSQL_CAPS_IA 0x0400
67 #define MYSQL_CAPS_SL 0x0800
68 #define MYSQL_CAPS_II 0x1000
69 #define MYSQL_CAPS_TA 0x2000
70 #define MYSQL_CAPS_RS 0x4000
71 #define MYSQL_CAPS_SC 0x8000
72
73 /* extended capabilities: 4.1+ client only */
74 #define MYSQL_CAPS_MS 0x0001
75 #define MYSQL_CAPS_MR 0x0002
76
77 /* status bitfield */
78 #define MYSQL_STAT_IT 0x0001
79 #define MYSQL_STAT_AC 0x0002
80 #define MYSQL_STAT_MR 0x0004
81 #define MYSQL_STAT_MU 0x0008
82 #define MYSQL_STAT_BI 0x0010
83 #define MYSQL_STAT_NI 0x0020
84 #define MYSQL_STAT_CR 0x0040
85 #define MYSQL_STAT_LR 0x0080
86 #define MYSQL_STAT_DR 0x0100
87 #define MYSQL_STAT_BS 0x0200
88
89 /* bitfield for MYSQL_REFRESH */
90 #define MYSQL_RFSH_GRANT   1   /* Refresh grant tables */
91 #define MYSQL_RFSH_LOG     2   /* Start on new log file */
92 #define MYSQL_RFSH_TABLES  4   /* close all tables */
93 #define MYSQL_RFSH_HOSTS   8   /* Flush host cache */
94 #define MYSQL_RFSH_STATUS  16  /* Flush status variables */
95 #define MYSQL_RFSH_THREADS 32  /* Flush thread cache */
96 #define MYSQL_RFSH_SLAVE   64  /* Reset master info and restart slave thread */
97 #define MYSQL_RFSH_MASTER  128 /* Remove all bin logs in the index and truncate the index */
98
99 /* MySQL operation codes */
100 #define MYSQL_SLEEP               0  /* not from client */
101 #define MYSQL_QUIT                1
102 #define MYSQL_INIT_DB             2
103 #define MYSQL_QUERY               3
104 #define MYSQL_FIELD_LIST          4
105 #define MYSQL_CREATE_DB           5
106 #define MYSQL_DROP_DB             6
107 #define MYSQL_REFRESH             7
108 #define MYSQL_SHUTDOWN            8
109 #define MYSQL_STATISTICS          9
110 #define MYSQL_PROCESS_INFO        10
111 #define MYSQL_CONNECT             11 /* not from client */
112 #define MYSQL_PROCESS_KILL        12
113 #define MYSQL_DEBUG               13
114 #define MYSQL_PING                14
115 #define MYSQL_TIME                15 /* not from client */
116 #define MYSQL_DELAY_INSERT        16 /* not from client */
117 #define MYSQL_CHANGE_USER         17
118 #define MYSQL_BINLOG_DUMP         18 /* replication */
119 #define MYSQL_TABLE_DUMP          19 /* replication */
120 #define MYSQL_CONNECT_OUT         20 /* replication */
121 #define MYSQL_REGISTER_SLAVE      21 /* replication */
122 #define MYSQL_STMT_PREPARE        22
123 #define MYSQL_STMT_EXECUTE        23
124 #define MYSQL_STMT_SEND_LONG_DATA 24
125 #define MYSQL_STMT_CLOSE          25
126 #define MYSQL_STMT_RESET          26
127 #define MYSQL_SET_OPTION          27
128 #define MYSQL_STMT_FETCH          28
129
130
131 /* decoding table: opcodes */
132 static const value_string mysql_opcode_vals[] = {
133         {MYSQL_SLEEP,   "SLEEP"},
134         {MYSQL_QUIT,   "Quit"},
135         {MYSQL_INIT_DB,  "Use Database"},
136         {MYSQL_QUERY,   "Query"},
137         {MYSQL_FIELD_LIST, "Show Fields"},
138         {MYSQL_CREATE_DB,  "Create Database"},
139         {MYSQL_DROP_DB , "Drop Database"},
140         {MYSQL_REFRESH , "Refresh"},
141         {MYSQL_SHUTDOWN , "Shutdown"},
142         {MYSQL_STATISTICS , "Statistics"},
143         {MYSQL_PROCESS_INFO , "Process List"},
144         {MYSQL_CONNECT , "Connect"},
145         {MYSQL_PROCESS_KILL , "Kill Server Thread"},
146         {MYSQL_DEBUG , "Dump Debuginfo"},
147         {MYSQL_PING , "Ping"},
148         {MYSQL_TIME , "Time"},
149         {MYSQL_DELAY_INSERT , "Insert Delayed"},
150         {MYSQL_CHANGE_USER , "Change User"},
151         {MYSQL_BINLOG_DUMP , "Send Binlog"},
152         {MYSQL_TABLE_DUMP, "Send Table"},
153         {MYSQL_CONNECT_OUT, "Slave Connect"},
154         {MYSQL_REGISTER_SLAVE, "Register Slave"},
155         {MYSQL_STMT_PREPARE, "Prepare Statement"},
156         {MYSQL_STMT_EXECUTE, "Execute Statement"},
157         {MYSQL_STMT_SEND_LONG_DATA, "Send BLOB"},
158         {MYSQL_STMT_CLOSE, "Close Statement"},
159         {MYSQL_STMT_RESET, "Reset Statement"},
160         {MYSQL_SET_OPTION, "Set Option"},
161         {MYSQL_STMT_FETCH, "Fetch Data"},
162         {0, NULL}
163 };
164
165
166 /* charset: pre-4.1 used ther term 'charset', later changed to 'collation' */
167 static const value_string mysql_charset_vals[] = {
168         {1,  "big5"},
169         {2,  "czech"},
170         {3,  "dec8"},
171         {4,  "dos" },
172         {5,  "german1"},
173         {6,  "hp8"},
174         {7,  "koi8_ru"},
175         {8,  "latin1"},
176         {9,  "latin2"},
177         {9,  "swe7 "},
178         {10, "usa7"},
179         {11, "ujis"},
180         {12, "sjis"},
181         {13, "cp1251"},
182         {14, "danish"},
183         {15, "hebrew"},
184         {16, "win1251"},
185         {17, "tis620"},
186         {18, "euc_kr"},
187         {19, "estonia"},
188         {20, "hungarian"},
189         {21, "koi8_ukr"},
190         {22, "win1251ukr"},
191         {23, "gb2312"},
192         {24, "greek"},
193         {25, "win1250"},
194         {26, "croat"},
195         {27, "gbk"},
196         {28, "cp1257"},
197         {29, "latin5"},
198         {0, NULL}
199 };
200
201
202 /* collation codes may change over time, recreate with the following SQL
203
204 SELECT CONCAT('  {', ID, ',"', CHARACTER_SET_NAME,
205               ' COLLATE ', COLLATION_NAME, '"},')
206 FROM INFORMATION_SCHEMA.COLLATIONS
207 ORDER BY ID
208 INTO OUTFILE '/tmp/mysql-collations';
209
210 */
211 static const value_string mysql_collation_vals[] = {
212         {3,   "dec8 COLLATE dec8_swedish_ci"},
213         {4,   "cp850 COLLATE cp850_general_ci"},
214         {5,   "latin1 COLLATE latin1_german1_ci"},
215         {6,   "hp8 COLLATE hp8_english_ci"},
216         {7,   "koi8r COLLATE koi8r_general_ci"},
217         {8,   "latin1 COLLATE latin1_swedish_ci"},
218         {9,   "latin2 COLLATE latin2_general_ci"},
219         {10,  "swe7 COLLATE swe7_swedish_ci"},
220         {11,  "ascii COLLATE ascii_general_ci"},
221         {14,  "cp1251 COLLATE cp1251_bulgarian_ci"},
222         {15,  "latin1 COLLATE latin1_danish_ci"},
223         {16,  "hebrew COLLATE hebrew_general_ci"},
224         {20,  "latin7 COLLATE latin7_estonian_cs"},
225         {21,  "latin2 COLLATE latin2_hungarian_ci"},
226         {22,  "koi8u COLLATE koi8u_general_ci"},
227         {23,  "cp1251 COLLATE cp1251_ukrainian_ci"},
228         {25,  "greek COLLATE greek_general_ci"},
229         {26,  "cp1250 COLLATE cp1250_general_ci"},
230         {27,  "latin2 COLLATE latin2_croatian_ci"},
231         {29,  "cp1257 COLLATE cp1257_lithuanian_ci"},
232         {30,  "latin5 COLLATE latin5_turkish_ci"},
233         {31,  "latin1 COLLATE latin1_german2_ci"},
234         {32,  "armscii8 COLLATE armscii8_general_ci"},
235         {33,  "utf8 COLLATE utf8_general_ci"},
236         {36,  "cp866 COLLATE cp866_general_ci"},
237         {37,  "keybcs2 COLLATE keybcs2_general_ci"},
238         {38,  "macce COLLATE macce_general_ci"},
239         {39,  "macroman COLLATE macroman_general_ci"},
240         {40,  "cp852 COLLATE cp852_general_ci"},
241         {41,  "latin7 COLLATE latin7_general_ci"},
242         {42,  "latin7 COLLATE latin7_general_cs"},
243         {43,  "macce COLLATE macce_bin"},
244         {44,  "cp1250 COLLATE cp1250_croatian_ci"},
245         {47,  "latin1 COLLATE latin1_bin"},
246         {48,  "latin1 COLLATE latin1_general_ci"},
247         {49,  "latin1 COLLATE latin1_general_cs"},
248         {50,  "cp1251 COLLATE cp1251_bin"},
249         {51,  "cp1251 COLLATE cp1251_general_ci"},
250         {52,  "cp1251 COLLATE cp1251_general_cs"},
251         {53,  "macroman COLLATE macroman_bin"},
252         {57,  "cp1256 COLLATE cp1256_general_ci"},
253         {58,  "cp1257 COLLATE cp1257_bin"},
254         {59,  "cp1257 COLLATE cp1257_general_ci"},
255         {63,  "binary COLLATE binary"},
256         {64,  "armscii8 COLLATE armscii8_bin"},
257         {65,  "ascii COLLATE ascii_bin"},
258         {66,  "cp1250 COLLATE cp1250_bin"},
259         {67,  "cp1256 COLLATE cp1256_bin"},
260         {68,  "cp866 COLLATE cp866_bin"},
261         {69,  "dec8 COLLATE dec8_bin"},
262         {70,  "greek COLLATE greek_bin"},
263         {71,  "hebrew COLLATE hebrew_bin"},
264         {72,  "hp8 COLLATE hp8_bin"},
265         {73,  "keybcs2 COLLATE keybcs2_bin"},
266         {74,  "koi8r COLLATE koi8r_bin"},
267         {75,  "koi8u COLLATE koi8u_bin"},
268         {77,  "latin2 COLLATE latin2_bin"},
269         {78,  "latin5 COLLATE latin5_bin"},
270         {79,  "latin7 COLLATE latin7_bin"},
271         {80,  "cp850 COLLATE cp850_bin"},
272         {81,  "cp852 COLLATE cp852_bin"},
273         {82,  "swe7 COLLATE swe7_bin"},
274         {83,  "utf8 COLLATE utf8_bin"},
275         {92,  "geostd8 COLLATE geostd8_general_ci"},
276         {93,  "geostd8 COLLATE geostd8_bin"},
277         {94,  "latin1 COLLATE latin1_spanish_ci"},
278         {99,  "cp1250 COLLATE cp1250_polish_ci"},
279         {192, "utf8 COLLATE utf8_unicode_ci"},
280         {193, "utf8 COLLATE utf8_icelandic_ci"},
281         {194, "utf8 COLLATE utf8_latvian_ci"},
282         {195, "utf8 COLLATE utf8_romanian_ci"},
283         {196, "utf8 COLLATE utf8_slovenian_ci"},
284         {197, "utf8 COLLATE utf8_polish_ci"},
285         {198, "utf8 COLLATE utf8_estonian_ci"},
286         {199, "utf8 COLLATE utf8_spanish_ci"},
287         {200, "utf8 COLLATE utf8_swedish_ci"},
288         {201, "utf8 COLLATE utf8_turkish_ci"},
289         {202, "utf8 COLLATE utf8_czech_ci"},
290         {203, "utf8 COLLATE utf8_danish_ci"},
291         {204, "utf8 COLLATE utf8_lithuanian_ci"},
292         {205, "utf8 COLLATE utf8_slovak_ci"},
293         {206, "utf8 COLLATE utf8_spanish2_ci"},
294         {207, "utf8 COLLATE utf8_roman_ci"},
295         {208, "utf8 COLLATE utf8_persian_ci"},
296         {209, "utf8 COLLATE utf8_esperanto_ci"},
297         {210, "utf8 COLLATE utf8_hungarian_ci"},
298         {0, NULL}
299 };
300
301
302 /* allowed MYSQL_SHUTDOWN levels */
303 static const value_string mysql_shutdown_vals[] = {
304         {0,   "default"},
305         {1,   "wait for connections to finish"},
306         {2,   "wait for transactions to finish"},
307         {8,   "wait for updates to finish"},
308         {16,  "wait flush all buffers"},
309         {17,  "wait flush critical buffers"},
310         {254, "kill running queries"},
311         {255, "kill connections"},
312         {0, NULL}
313 };
314
315
316 /* allowed MYSQL_SET_OPTION values */
317 static const value_string mysql_option_vals[] = {
318         {0, "multi statements on"},
319         {1, "multi statements off"},
320         {0, NULL}
321 };
322
323
324
325 /* protocol id */
326 static int proto_mysql = -1;
327
328 /* dissector configuration */
329 static gboolean mysql_desegment = TRUE;
330
331 /* expand-the-tree flags */
332 static gint ett_mysql = -1;
333 static gint ett_server_greeting = -1;
334 static gint ett_caps = -1;
335 static gint ett_extcaps = -1;
336 static gint ett_stat = -1;
337 static gint ett_request = -1;
338 static gint ett_refresh = -1;
339
340 /* protocol fields */
341 static int hf_mysql_caps= -1;
342 static int hf_mysql_cap_long_password= -1;
343 static int hf_mysql_cap_found_rows= -1;
344 static int hf_mysql_cap_long_flag= -1;
345 static int hf_mysql_cap_connect_with_db= -1;
346 static int hf_mysql_cap_no_schema= -1;
347 static int hf_mysql_cap_compress= -1;
348 static int hf_mysql_cap_odbc= -1;
349 static int hf_mysql_cap_local_files= -1;
350 static int hf_mysql_cap_ignore_space= -1;
351 static int hf_mysql_cap_change_user= -1;
352 static int hf_mysql_cap_interactive= -1;
353 static int hf_mysql_cap_ssl= -1;
354 static int hf_mysql_cap_ignore_sigpipe= -1;
355 static int hf_mysql_cap_transactions= -1;
356 static int hf_mysql_cap_reserved= -1;
357 static int hf_mysql_cap_secure_connect= -1;
358 static int hf_mysql_extcaps= -1;
359 static int hf_mysql_cap_multi_statements= -1;
360 static int hf_mysql_cap_multi_results= -1;
361 static int hf_mysql_status= -1;
362 static int hf_mysql_stat_it= -1;
363 static int hf_mysql_stat_ac= -1;
364 static int hf_mysql_stat_mr= -1;
365 static int hf_mysql_stat_mu= -1;
366 static int hf_mysql_stat_bi= -1;
367 static int hf_mysql_stat_ni= -1;
368 static int hf_mysql_stat_cr= -1;
369 static int hf_mysql_stat_lr= -1;
370 static int hf_mysql_stat_dr= -1;
371 static int hf_mysql_stat_bs= -1;
372 static int hf_mysql_refresh= -1;
373 static int hf_mysql_rfsh_grants= -1;
374 static int hf_mysql_rfsh_log= -1;
375 static int hf_mysql_rfsh_tables= -1;
376 static int hf_mysql_rfsh_hosts= -1;
377 static int hf_mysql_rfsh_status= -1;
378 static int hf_mysql_rfsh_threads= -1;
379 static int hf_mysql_rfsh_slave= -1;
380 static int hf_mysql_rfsh_master= -1;
381 static int hf_mysql_packet_length= -1;
382 static int hf_mysql_packet_number= -1;
383 static int hf_mysql_opcode= -1;
384 static int hf_mysql_response_code= -1;
385 static int hf_mysql_error_code= -1;
386 static int hf_mysql_error_string= -1;
387 static int hf_mysql_sqlstate= -1;
388 static int hf_mysql_message= -1;
389 static int hf_mysql_payload= -1;
390 static int hf_mysql_protocol= -1;
391 static int hf_mysql_version = -1;
392 static int hf_mysql_max_packet= -1;
393 static int hf_mysql_user= -1;
394 static int hf_mysql_schema= -1;
395 static int hf_mysql_thread_id = -1;
396 static int hf_mysql_salt= -1;
397 static int hf_mysql_salt2= -1;
398 static int hf_mysql_charset= -1;
399 static int hf_mysql_passwd= -1;
400 static int hf_mysql_unused= -1;
401 static int hf_mysql_parameter= -1;
402 static int hf_mysql_affected_rows= -1;
403 static int hf_mysql_insert_id= -1;
404 static int hf_mysql_num_warn= -1;
405 static int hf_mysql_thd_id= -1;
406 static int hf_mysql_stmt_id= -1;
407 static int hf_mysql_query= -1;
408 static int hf_mysql_option= -1;
409 static int hf_mysql_num_rows= -1;
410 static int hf_mysql_param= -1;
411 static int hf_mysql_exec_flags= -1;
412 static int hf_mysql_exec_iter= -1;
413 static int hf_mysql_eof= -1;
414 static int hf_mysql_num_fields= -1;
415 static int hf_mysql_extra= -1;
416
417 typedef enum my_proto_state
418 {
419         UNDEFINED,
420         LOGIN,
421         REQUEST,
422         RESPONSE_OK,
423         RESPONSE_MESSAGE,
424         RESPONSE_TABULAR,
425         FIELD_PACKET,
426         ROW_PACKET,
427         RESPONSE_PREPARE,
428         PARAM_PACKET
429 } my_proto_state_t;
430
431 #ifdef CTDEBUG
432 static const value_string state_vals[] = {
433         {UNDEFINED,        "undefined"},
434         {LOGIN,            "login"},
435         {REQUEST,          "request"},
436         {RESPONSE_OK,      "response OK"},
437         {RESPONSE_MESSAGE, "response message"},
438         {RESPONSE_TABULAR, "tabular response"},
439         {FIELD_PACKET,     "field packet"},
440         {ROW_PACKET,       "row packet"},
441         {RESPONSE_PREPARE, "response to PREPARE"},
442         {PARAM_PACKET,     "parameter packet"},
443         {0, NULL}
444 };
445 #endif
446
447 typedef struct my_conn_data
448 {
449         guint16 srv_caps;
450         guint16 clnt_caps;
451         guint16 clnt_caps_ext;
452         my_proto_state_t state;
453         GHashTable* stmts;
454 #ifdef CTDEBUG
455         guint32 generation;
456 #endif
457 } my_conn_data_t;
458
459
460 typedef struct my_stmt_data
461 {
462         guint16 nparam;
463 } my_stmt_data_t;
464
465
466 /* function prototypes */
467 void proto_reg_handoff_mysql(void);
468 void proto_register_mysql(void);
469 static void dissect_mysql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
470 static guint get_mysql_pdu_len(packet_info *pinfo, tvbuff_t *tvb, int offset);
471 static void dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
472 static int mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
473 static int mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
474 static int mysql_dissect_request(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
475 static int mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
476 static int mysql_dissect_error_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree);
477 static int mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
478 static int mysql_dissect_server_status(tvbuff_t *tvb, int offset, proto_tree *tree);
479 static int mysql_dissect_collation(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 caps);
480 static int mysql_dissect_caps(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *caps, const char* whom);
481 static int mysql_dissect_ext_caps(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 *caps, const char* whom);
482 static int mysql_dissect_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_tree *tree, my_conn_data_t *conn_data);
483 static int mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree);
484 static int mysql_dissect_row_packet(tvbuff_t *tvb, int offset, proto_tree *tree);
485 static int mysql_dissect_response_prepare(tvbuff_t *tvb, int offset, proto_tree *tree);
486 static int mysql_dissect_param_packet(tvbuff_t *tvb, int offset, proto_tree *tree);
487 static gint my_tvb_strsize(tvbuff_t *tvb, int offset);
488 static int tvb_get_fle(tvbuff_t *tvb, int offset, guint64 *res, guint8 *is_null);
489
490
491 /* dissector registration */
492 void proto_reg_handoff_mysql(void)
493 {
494         dissector_handle_t mysql_handle;
495         mysql_handle = create_dissector_handle(dissect_mysql, proto_mysql);
496         dissector_add("tcp.port", TCP_PORT_MySQL, mysql_handle);
497 }
498
499
500 /* protocol registration */
501 void proto_register_mysql(void)
502 {
503         static hf_register_info hf[]=
504         {
505                 { &hf_mysql_packet_length,
506                 { "Packet Length", "mysql.packet_length",
507                 FT_UINT24, BASE_DEC, NULL,  0x0,
508                 "Packet Length", HFILL }},
509
510                 { &hf_mysql_packet_number,
511                 { "Packet Number", "mysql.packet_number",
512                 FT_UINT8, BASE_DEC, NULL, 0x0,
513                 "Packet Number", HFILL }},
514
515                 { &hf_mysql_opcode,
516                 { "Command", "mysql.opcode",
517                 FT_UINT8, BASE_DEC, NULL, 0x0,
518                 "Command", HFILL }},
519
520                 { &hf_mysql_response_code,
521                 { "Response Code", "mysql.response_code",
522                 FT_UINT8, BASE_DEC, NULL, 0x0,
523                 "Response Code", HFILL }},
524
525                 { &hf_mysql_error_code,
526                 { "Error Code", "mysql.error_code",
527                 FT_UINT16, BASE_DEC, NULL, 0x0,
528                 "Error Code", HFILL }},
529
530                 { &hf_mysql_error_string,
531                 { "Error message", "mysql.error.message",
532                 FT_STRING, BASE_DEC, NULL, 0x0,
533                 "Error string in case of MySQL error message", HFILL }},
534
535                 { &hf_mysql_sqlstate,
536                 { "SQL state", "mysql.sqlstate",
537                 FT_STRING, BASE_NONE, NULL, 0x0,
538                 "", HFILL }},
539
540                 { &hf_mysql_message,
541                 { "Message", "mysql.message",
542                 FT_STRINGZ, BASE_DEC, NULL, 0x0,
543                 "Message", HFILL }},
544
545                 { &hf_mysql_protocol,
546                 { "Protocol", "mysql.protocol",
547                 FT_UINT8, BASE_DEC, NULL, 0x0,
548                 "Protocol Version", HFILL }},
549
550                 { &hf_mysql_version,
551                 { "Version", "mysql.version",
552                 FT_STRINGZ, BASE_DEC, NULL, 0x0,
553                 "MySQL Version", HFILL }},
554
555                 { &hf_mysql_caps,
556                 { "Caps", "mysql.caps",
557                 FT_UINT16, BASE_DEC, NULL, 0x0,
558                 "MySQL Capabilities", HFILL }},
559
560                 { &hf_mysql_cap_long_password,
561                 { "Long Password","mysql.caps.lp",
562                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LP,
563                 "", HFILL }},
564
565                 { &hf_mysql_cap_found_rows,
566                 { "Found Rows","mysql.caps.fr",
567                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_FR,
568                 "", HFILL }},
569
570                 { &hf_mysql_cap_long_flag,
571                 { "Long Column Flags","mysql.caps.lf",
572                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LF,
573                 "", HFILL }},
574
575                 { &hf_mysql_cap_connect_with_db,
576                 { "Connect With Database","mysql.caps.cd",
577                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CD,
578                 "", HFILL }},
579
580                 { &hf_mysql_cap_no_schema,
581                 { "Dont Allow database.table.column","mysql.caps.ns",
582                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_NS,
583                 "", HFILL }},
584
585                 { &hf_mysql_cap_compress,
586                 { "Can use compression protocol","mysql.caps.cp",
587                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CP,
588                 "", HFILL }},
589
590                 { &hf_mysql_cap_odbc,
591                 { "ODBC Client","mysql.caps.ob",
592                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_OB,
593                 "", HFILL }},
594
595                 { &hf_mysql_cap_local_files,
596                 { "Can Use LOAD DATA LOCAL","mysql.caps.li",
597                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LI,
598                 "", HFILL }},
599
600                 { &hf_mysql_cap_ignore_space,
601                 { "Ignore Spaces before '('","mysql.caps.is",
602                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_IS,
603                 "", HFILL }},
604
605                 { &hf_mysql_cap_change_user,
606                 { "Speaks 4.1 protocol (new flag)","mysql.caps.cu",
607                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CU,
608                 "", HFILL }},
609
610                 { &hf_mysql_cap_interactive,
611                 { "Interactive Client","mysql.caps.ia",
612                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_IA,
613                 "", HFILL }},
614
615                 { &hf_mysql_cap_ssl,
616                 { "Switch to SSL after handshake","mysql.caps.sl",
617                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_SL,
618                 "", HFILL }},
619
620                 { &hf_mysql_cap_ignore_sigpipe,
621                 { "Ignore sigpipes","mysql.caps.ii",
622                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_II,
623                 "", HFILL }},
624
625                 { &hf_mysql_cap_transactions,
626                 { "Knows about transactions","mysql.caps.ta",
627                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_TA,
628                 "", HFILL }},
629
630                 { &hf_mysql_cap_reserved,
631                 { "Speaks 4.1 protocol (old flag)","mysql.caps.rs",
632                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_RS,
633                 "", HFILL }},
634
635                 { &hf_mysql_cap_secure_connect,
636                 { "Can do 4.1 authentication","mysql.caps.sc",
637                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_SC,
638                 "", HFILL }},
639
640                 { &hf_mysql_extcaps,
641                 { "Ext. Caps", "mysql.extcaps",
642                 FT_UINT16, BASE_DEC, NULL, 0x0,
643                 "MySQL Extended Capabilities", HFILL }},
644
645                 { &hf_mysql_cap_multi_statements,
646                 { "Supports multiple statements","mysql.caps.ms",
647                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_MS,
648                 "", HFILL }},
649
650                 { &hf_mysql_cap_multi_results,
651                 { "Supports multiple results","mysql.caps.mr",
652                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_MR,
653                 "", HFILL }},
654
655                 { &hf_mysql_max_packet,
656                 { "MAX Packet", "mysql.max_packet",
657                 FT_UINT24, BASE_DEC, NULL,  0x0,
658                 "MySQL Max packet", HFILL }},
659
660                 { &hf_mysql_user,
661                 { "Username", "mysql.user",
662                 FT_STRINGZ, BASE_DEC, NULL, 0x0,
663                 "Login Username", HFILL }},
664
665                 { &hf_mysql_schema,
666                 { "Schema", "mysql.schema",
667                 FT_STRING, BASE_DEC, NULL, 0x0,
668                 "Login Schema", HFILL }},
669
670                 { &hf_mysql_salt,
671                 { "Salt", "mysql.salt",
672                 FT_STRINGZ, BASE_DEC, NULL, 0x0,
673                 "Salt", HFILL }},
674
675                 { &hf_mysql_salt2,
676                 { "Salt", "mysql.salt2",
677                 FT_STRINGZ, BASE_DEC, NULL, 0x0,
678                 "Salt", HFILL }},
679
680                 { &hf_mysql_thread_id,
681                 { "Thread ID", "mysql.thread_id",
682                 FT_UINT32, BASE_DEC, NULL,  0x0,
683                 "MySQL Thread ID", HFILL }},
684
685                 { &hf_mysql_charset,
686                 { "Charset", "mysql.charset",
687                 FT_UINT8, BASE_DEC, NULL,  0x0,
688                 "MySQL Charset", HFILL }},
689
690                 { &hf_mysql_status,
691                 { "Status", "mysql.status",
692                 FT_UINT16, BASE_DEC, NULL,  0x0,
693                 "MySQL Status", HFILL }},
694
695                 { &hf_mysql_stat_it,
696                 { "In transaction", "mysql.stat.it",
697                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_IT,
698                 "", HFILL }},
699
700                 { &hf_mysql_stat_ac,
701                 { "AUTO_COMMIT", "mysql.stat.ac",
702                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_AC,
703                 "", HFILL }},
704
705                 { &hf_mysql_stat_mr,
706                 { "More results", "mysql.stat.mr",
707                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_MR,
708                 "", HFILL }},
709
710                 { &hf_mysql_stat_mu,
711                 { "Multi query - more resultsets", "mysql.stat.mu",
712                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_MU,
713                 "", HFILL }},
714
715                 { &hf_mysql_stat_bi,
716                 { "Bad index used", "mysql.stat.bi",
717                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_BI,
718                 "", HFILL }},
719
720                 { &hf_mysql_stat_ni,
721                 { "No index used", "mysql.stat.ni",
722                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_NI,
723                 "", HFILL }},
724
725                 { &hf_mysql_stat_cr,
726                 { "Cursor exists", "mysql.stat.cr",
727                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_CR,
728                 "", HFILL }},
729
730                 { &hf_mysql_stat_lr,
731                 { "Last row sebd", "mysql.stat.lr",
732                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_LR,
733                 "", HFILL }},
734
735                 { &hf_mysql_stat_dr,
736                 { "database dropped", "mysql.stat.dr",
737                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_DR,
738                 "", HFILL }},
739
740                 { &hf_mysql_stat_bs,
741                 { "No backslash escapes", "mysql.stat.bs",
742                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_STAT_BS,
743                 "", HFILL }},
744
745                 { &hf_mysql_refresh,
746                 { "Refresh Option", "mysql.refresh",
747                 FT_UINT8, BASE_DEC, NULL,  0x0,
748                 "Refresh Option", HFILL }},
749
750                 { &hf_mysql_rfsh_grants,
751                 { "reload permissions", "mysql.rfsh.grants",
752                 FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_GRANT,
753                 "", HFILL }},
754
755                 { &hf_mysql_rfsh_log,
756                 { "flush logfiles", "mysql.rfsh.log",
757                 FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_LOG,
758                 "", HFILL }},
759
760                 { &hf_mysql_rfsh_tables,
761                 { "flush tables", "mysql.rfsh.tables",
762                 FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_TABLES,
763                 "", HFILL }},
764
765                 { &hf_mysql_rfsh_hosts,
766                 { "flush hosts", "mysql.rfsh.hosts",
767                 FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_HOSTS,
768                 "", HFILL }},
769
770                 { &hf_mysql_rfsh_status,
771                 { "reset statistics", "mysql.rfsh.status",
772                 FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_STATUS,
773                 "", HFILL }},
774
775                 { &hf_mysql_rfsh_threads,
776                 { "empty thread cache", "mysql.rfsh.threads",
777                 FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_THREADS,
778                 "", HFILL }},
779
780                 { &hf_mysql_rfsh_slave,
781                 { "flush slave status", "mysql.rfsh.slave",
782                 FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_SLAVE,
783                 "", HFILL }},
784
785                 { &hf_mysql_rfsh_master,
786                 { "flush master status", "mysql.rfsh.master",
787                 FT_BOOLEAN, 8, TFS(&flags_set_truth), MYSQL_RFSH_MASTER,
788                 "", HFILL }},
789
790                 { &hf_mysql_unused,
791                 { "Unused", "mysql.unused",
792                 FT_STRING, BASE_DEC, NULL, 0x0,
793                 "Unused", HFILL }},
794
795                 { &hf_mysql_passwd,
796                 { "Password", "mysql.passwd",
797                 FT_STRING, BASE_DEC, NULL, 0x0,
798                 "Password", HFILL }},
799
800                 { &hf_mysql_parameter,
801                 { "Parameter", "mysql.parameter",
802                 FT_STRING, BASE_DEC, NULL, 0x0,
803                 "Parameter", HFILL }},
804
805                 { &hf_mysql_payload,
806                 { "Payload", "mysql.payload",
807                 FT_STRING, BASE_DEC, NULL, 0x0,
808                 "Additional Payload", HFILL }},
809
810                 { &hf_mysql_affected_rows,
811                 { "Affected Rows", "mysql.affected_rows",
812                 FT_UINT64, BASE_DEC, NULL, 0x0,
813                 "Affected Rows", HFILL }},
814
815                 { &hf_mysql_insert_id,
816                 { "Last INSERT ID", "mysql.insert_id",
817                 FT_UINT64, BASE_DEC, NULL, 0x0,
818                 "Last INSERT ID", HFILL }},
819
820                 { &hf_mysql_num_warn,
821                 { "Warnings", "mysql.warnings",
822                 FT_UINT16, BASE_DEC, NULL, 0x0,
823                 "Warnings", HFILL }},
824
825                 { &hf_mysql_thd_id,
826                 { "Thread ID", "mysql.thd_id",
827                 FT_UINT32, BASE_DEC, NULL, 0x0,
828                 "Thread ID", HFILL }},
829
830                 { &hf_mysql_stmt_id,
831                 { "Statement ID", "mysql.stmt_id",
832                 FT_UINT32, BASE_DEC, NULL, 0x0,
833                 "Statement ID", HFILL }},
834
835                 { &hf_mysql_query,
836                 { "Statement", "mysql.query",
837                 FT_STRING, BASE_DEC, NULL, 0x0,
838                 "Statement", HFILL }},
839
840                 { &hf_mysql_option,
841                 { "Option", "mysql.option",
842                 FT_UINT16, BASE_DEC, NULL, 0x0,
843                 "Option", HFILL }},
844
845                 { &hf_mysql_param,
846                 { "Parameter", "mysql.param",
847                 FT_UINT16, BASE_DEC, NULL, 0x0,
848                 "Parameter", HFILL }},
849
850                 { &hf_mysql_num_rows,
851                 { "Rows to fetch", "mysql.num_rows",
852                 FT_UINT32, BASE_DEC, NULL, 0x0,
853                 "Rows to fetch", HFILL }},
854
855                 { &hf_mysql_exec_flags,
856                 { "Flags (unused)", "mysql.exec_flags",
857                 FT_UINT8, BASE_DEC, NULL, 0x0,
858                 "Flags (unused)", HFILL }},
859
860                 { &hf_mysql_exec_iter,
861                 { "Iterations (unused)", "mysql.exec_iter",
862                 FT_UINT32, BASE_DEC, NULL, 0x0,
863                 "Iterations (unused)", HFILL }},
864
865                 { &hf_mysql_eof,
866                 { "EOF", "mysql.eof",
867                 FT_UINT8, BASE_DEC, NULL,  0x0,
868                 "EOF", HFILL }},
869
870                 { &hf_mysql_num_fields,
871                 { "Number of fields", "mysql.num_fields",
872                 FT_UINT64, BASE_DEC, NULL,  0x0,
873                 "Number of fields", HFILL }},
874
875                 { &hf_mysql_extra,
876                 { "Extra data", "mysql.extra",
877                 FT_UINT64, BASE_DEC, NULL,  0x0,
878                 "Extra data", HFILL }}
879         };
880
881         static gint *ett[]=
882         {
883                 &ett_mysql,
884                 &ett_server_greeting,
885                 &ett_caps,
886                 &ett_extcaps,
887                 &ett_stat,
888                 &ett_request,
889                 &ett_refresh,
890         };
891
892         module_t *mysql_module;
893
894         proto_mysql= proto_register_protocol("MySQL Protocol", "MySQL", "mysql");
895         proto_register_field_array(proto_mysql, hf, array_length(hf));
896         proto_register_subtree_array(ett, array_length(ett));
897
898         mysql_module= prefs_register_protocol(proto_mysql, NULL);
899         prefs_register_bool_preference(mysql_module, "desegment_buffers",
900                                        "Reassemble MySQL buffers spanning multiple TCP segments",
901                                        "Whether the MySQL dissector should reassemble MySQL buffers spanning multiple TCP segments."
902                                        " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
903                                        &mysql_desegment);
904 }
905
906
907 /* dissector entrypoint, handles TCP-desegmentation */
908 static void dissect_mysql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
909 {
910         tcp_dissect_pdus(tvb, pinfo, tree, mysql_desegment, 3,
911                          get_mysql_pdu_len, dissect_mysql_pdu);
912 }
913
914
915 /* dissector helper: length of PDU */
916 static guint get_mysql_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
917 {
918         guint plen= tvb_get_letoh24(tvb, offset);
919         return plen + 4; /* add length field + packet number */
920 }
921
922
923 /* dissector main function: handle one PDU */
924 static void dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
925 {
926         proto_tree      *mysql_tree= NULL;
927         proto_item      *ti;
928         conversation_t  *conversation;
929         int             offset = 0;
930         guint           packet_number;
931         gboolean        is_response;
932         my_conn_data_t  *conn_data;
933 #ifdef CTDEBUG
934         my_proto_state_t state_in, state_out;
935         guint64         generation;
936 #endif
937
938         /* get conversation, create if neccessary*/
939         conversation= find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
940                                         pinfo->ptype, pinfo->srcport,
941                                         pinfo->destport, 0);
942
943         if (!conversation) {
944                 conversation= conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst,
945                                                pinfo->ptype, pinfo->srcport,
946                                                pinfo->destport, 0);
947         }
948
949         /* get associated state information, create if neccessary */
950         conn_data= conversation_get_proto_data(conversation, proto_mysql);
951         if (!conn_data) {
952                 conn_data= se_alloc(sizeof(my_conn_data_t));
953                 conn_data->srv_caps= 0;
954                 conn_data->clnt_caps= 0;
955                 conn_data->clnt_caps_ext= 0;
956                 conn_data->state= UNDEFINED;
957                 conn_data->stmts= g_hash_table_new(g_int_hash, g_int_equal);
958 #ifdef CTDEBUG
959                 conn_data->generation= 0;
960 #endif
961                 conversation_add_proto_data(conversation, proto_mysql, conn_data);
962         }
963
964         if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
965                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "MySQL");
966         }
967
968         if (pinfo->destport == pinfo->match_port) {
969                 is_response= FALSE;
970         } else {
971                 is_response= TRUE;
972         }
973
974         if (tree) {
975                 ti= proto_tree_add_item(tree, proto_mysql, tvb, offset, -1, FALSE);
976                 mysql_tree= proto_item_add_subtree(ti, ett_mysql);
977                 proto_tree_add_item(mysql_tree, hf_mysql_packet_length, tvb,
978                                     offset, 3, TRUE);
979         }
980         offset+= 3;
981
982         packet_number= tvb_get_guint8(tvb, offset);
983         if (tree) {
984                 proto_tree_add_uint(mysql_tree, hf_mysql_packet_number, tvb,
985                                     offset, 1, packet_number);
986         }
987         offset+= 1;
988
989 #ifdef CTDEBUG
990         state_in= conn_data->state;
991         generation= conn_data->generation;
992         if (tree) {
993                 proto_tree_add_text(mysql_tree, tvb, offset, 0, "conversation: %p", conversation);
994                 proto_tree_add_text(mysql_tree, tvb, offset, 0, "generation: %lld", generation);
995                 proto_tree_add_text(mysql_tree, tvb, offset, 0, "proto state: %s (%u)",
996                                     val_to_str(state_in, state_vals, "Unknown (%u)"),
997                                     state_in);
998         }
999 #endif
1000
1001         if (is_response ) {
1002                 if (packet_number == 0) {
1003                         if (check_col(pinfo->cinfo, COL_INFO)) {
1004                                 col_add_str(pinfo->cinfo, COL_INFO, "Server Greeting" );
1005                         }
1006                         offset= mysql_dissect_greeting(tvb, pinfo, offset,
1007                                                        mysql_tree, conn_data);
1008                 } else {
1009                         if (check_col(pinfo->cinfo, COL_INFO)) {
1010                                 col_add_str(pinfo->cinfo, COL_INFO, "Response" );
1011                         }
1012                         offset= mysql_dissect_response(tvb, pinfo, offset,
1013                                                        mysql_tree, conn_data);
1014                 }
1015         } else {
1016                 if (packet_number == 1) {
1017                         if (check_col(pinfo->cinfo, COL_INFO)) {
1018                                 col_add_str(pinfo->cinfo, COL_INFO, "Login Request");
1019                         }
1020                         offset= mysql_dissect_login(tvb, pinfo, offset,
1021                                                     mysql_tree, conn_data);
1022                 } else {
1023                         if (check_col(pinfo->cinfo, COL_INFO)) {
1024                                 col_add_str(pinfo->cinfo, COL_INFO, "Request");
1025                         }
1026                         offset= mysql_dissect_request(tvb, pinfo, offset,
1027                                                       mysql_tree, conn_data);
1028                 }
1029         }
1030
1031 #ifdef CTDEBUG
1032         state_out= conn_data->state;
1033         ++(conn_data->generation);
1034         if (tree) {
1035                 proto_tree_add_text(mysql_tree, tvb, offset, 0, "next proto state: %s (%u)",
1036                                     val_to_str(state_out, state_vals, "Unknown (%u)"),
1037                                     state_out);
1038         }
1039 #endif
1040
1041         /* remaining payload indicates an error */
1042         if (tree && tvb_reported_length_remaining(tvb, offset) > 0) {
1043                 proto_tree_add_string(mysql_tree, hf_mysql_payload, tvb, offset, -1,
1044                                       "FIXME - dissector is incomplete");
1045         }
1046 }
1047
1048
1049 static int mysql_dissect_greeting(tvbuff_t *tvb, packet_info *pinfo, int offset,
1050                                   proto_tree *tree, my_conn_data_t *conn_data)
1051 {
1052         gint protocol;
1053         gint strlen;
1054         gint32 thread_id;
1055         guint16 server_caps;
1056
1057         proto_item *tf;
1058         proto_item *greeting_tree= NULL;
1059
1060         protocol= tvb_get_guint8(tvb, offset);
1061
1062         if (protocol == 0xff) {
1063                 return mysql_dissect_error_packet(tvb, pinfo, offset+1, tree);
1064         }
1065
1066         conn_data->state= LOGIN;
1067
1068         if (tree) {
1069                 tf= proto_tree_add_text(tree, tvb, offset, -1, "Server Greeting");
1070                 greeting_tree= proto_item_add_subtree(tf, ett_server_greeting);
1071         }
1072
1073         if (check_col(pinfo->cinfo, COL_INFO)) {
1074                 col_append_fstr(pinfo->cinfo, COL_INFO, " proto=%d", protocol) ;
1075         }
1076         if (tree) {
1077                 proto_tree_add_uint(greeting_tree, hf_mysql_protocol, tvb,
1078                                     offset, 1, protocol);
1079         }
1080         offset+= 1;
1081
1082         /* version string */
1083         strlen= tvb_strsize(tvb,offset);
1084
1085         if (check_col(pinfo->cinfo, COL_INFO)) {
1086                 col_append_fstr(pinfo->cinfo, COL_INFO, " version=%s",
1087                                 tvb_get_ptr(tvb, offset, strlen));
1088         }
1089         if (tree) {
1090                 proto_tree_add_item(greeting_tree, hf_mysql_version, tvb,
1091                                     offset, strlen, FALSE );
1092         }
1093         offset+= strlen;
1094
1095         /* 4 bytes little endian thread_id */
1096         thread_id= tvb_get_letohl(tvb, offset);
1097         if (tree) {
1098                 proto_tree_add_uint(greeting_tree, hf_mysql_thread_id, tvb,
1099                                     offset, 4, thread_id);
1100         }
1101         offset+= 4;
1102
1103         /* salt string */
1104         strlen= tvb_strsize(tvb,offset);
1105         if (tree) {
1106                 proto_tree_add_item(greeting_tree, hf_mysql_salt, tvb,
1107                                     offset, strlen, FALSE );
1108         }
1109         offset+=strlen;
1110
1111         /* rest is optional */
1112         if (!tvb_length_remaining(tvb, offset)) return offset;
1113
1114         /* 2 bytes CAPS */
1115         offset= mysql_dissect_caps(tvb, offset, greeting_tree, &server_caps, "Server");
1116         conn_data->srv_caps= server_caps;
1117
1118         /* rest is optional */
1119         if (!tvb_length_remaining(tvb, offset)) return offset;
1120
1121         offset= mysql_dissect_collation(tvb, offset, greeting_tree, server_caps);
1122         offset= mysql_dissect_server_status(tvb, offset, greeting_tree);
1123
1124         /* 13 bytes unused */
1125         if (tree) {
1126                 proto_tree_add_item(greeting_tree, hf_mysql_unused, tvb,
1127                                     offset, 13, FALSE );
1128         }
1129         offset+= 13;
1130
1131         /* 4.1+ server: rest of salt */
1132         if (tvb_length_remaining(tvb, offset)) {
1133                 strlen= tvb_strsize(tvb,offset);
1134                 if (tree) {
1135                         proto_tree_add_item(greeting_tree, hf_mysql_salt2, tvb,
1136                                             offset, strlen, FALSE );
1137                 }
1138                 offset+= strlen;
1139         }
1140
1141         return offset;
1142 }
1143
1144
1145 static int mysql_dissect_login(tvbuff_t *tvb, packet_info *pinfo, int offset,
1146                                proto_tree *tree, my_conn_data_t *conn_data)
1147 {
1148         guint16  client_caps;
1149         guint16  ext_caps;
1150         guint32  max_packet;
1151         gint     strlen;
1152
1153         proto_item *tf;
1154         proto_item *login_tree= NULL;
1155
1156         /* after login there can be OK or DENIED */
1157         conn_data->state= RESPONSE_OK;
1158
1159         if (tree) {
1160                 tf= proto_tree_add_text(tree, tvb, offset, -1, "Login Request");
1161                 login_tree= proto_item_add_subtree(tf, ett_server_greeting);
1162         }
1163
1164         offset= mysql_dissect_caps(tvb, offset, login_tree, &client_caps, "Client");
1165         conn_data->clnt_caps= client_caps;
1166
1167         if (client_caps & MYSQL_CAPS_CU) /* 4.1 protocol */
1168         {
1169                 offset= mysql_dissect_ext_caps(tvb, offset, login_tree, &ext_caps, "Client");
1170                 conn_data->clnt_caps_ext= ext_caps;
1171
1172                 max_packet= tvb_get_letohl(tvb, offset);
1173                 if(tree) {
1174                         proto_tree_add_uint(login_tree, hf_mysql_max_packet, tvb,
1175                                             offset, 4, max_packet);
1176                 }
1177                 offset+= 4;
1178
1179                 offset= mysql_dissect_collation(tvb, offset, login_tree, client_caps);
1180
1181                 offset+= 23; /* filler bytes */
1182
1183         } else { /* pre-4.1 */
1184                 max_packet= 0xffffff - tvb_get_letoh24(tvb, offset);
1185                 if (tree) {
1186                         proto_tree_add_uint(login_tree, hf_mysql_max_packet, tvb,
1187                                             offset, 3, max_packet);
1188                 }
1189                 offset+= 3;
1190         }
1191
1192         /* User name */
1193         strlen= my_tvb_strsize(tvb, offset);
1194         if (check_col(pinfo->cinfo, COL_INFO)) {
1195                 col_append_fstr(pinfo->cinfo, COL_INFO, " user=%s",
1196                                 tvb_get_ptr(tvb,offset,strlen));
1197         }
1198         if (tree) {
1199                 proto_tree_add_item(login_tree, hf_mysql_user, tvb,
1200                                     offset, strlen, FALSE );
1201         }
1202         offset+= strlen;
1203
1204         /* rest is optional */
1205         if (!tvb_length_remaining(tvb, offset)) return offset;
1206
1207         /* password: asciiz or length+ascii */
1208         if (client_caps & MYSQL_CAPS_SC) {
1209                 strlen= tvb_get_guint8(tvb, offset);
1210                 offset+= 1;
1211         } else {
1212                 strlen= my_tvb_strsize(tvb, offset);
1213         }
1214         if (tree && strlen > 1) {
1215                 proto_tree_add_item(login_tree, hf_mysql_passwd,
1216                                     tvb, offset, strlen, FALSE);
1217         }
1218         offset+= strlen;
1219
1220         /* optional: initial schema */
1221         if (client_caps & MYSQL_CAPS_CD)
1222         {
1223                 strlen= my_tvb_strsize(tvb,offset);
1224                 if(strlen<0){
1225                         return offset;
1226                 }
1227
1228                 if (check_col(pinfo->cinfo, COL_INFO)) {
1229                         /* ugly hack: copy database to new buffer*/
1230                         guint8 buf[65];
1231                         if (strlen > 64)
1232                                 strlen= 64;
1233                         tvb_memcpy(tvb, buf, offset, strlen);
1234                         buf[strlen]= '\0';
1235                         col_append_fstr(pinfo->cinfo, COL_INFO, " db=%s", buf);
1236                 }
1237                 if (tree) {
1238                         proto_tree_add_item(login_tree, hf_mysql_schema, tvb,
1239                                             offset, strlen, FALSE );
1240                 }
1241                 offset+= strlen;
1242         }
1243
1244         return offset;
1245 }
1246
1247
1248 static int mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo, int offset,
1249                                  proto_tree *tree, my_conn_data_t *conn_data)
1250 {
1251         gint opcode;
1252         gint strlen;
1253         proto_item *tf;
1254         proto_item *req_tree= NULL;
1255         guint16 option;
1256
1257         if (tree) {
1258                 tf= proto_tree_add_text(tree, tvb, offset, -1, "Command");
1259                 req_tree= proto_item_add_subtree(tf, ett_request);
1260         }
1261
1262         opcode= tvb_get_guint8(tvb, offset);
1263         if (check_col(pinfo->cinfo, COL_INFO)) {
1264                 col_append_fstr(pinfo->cinfo, COL_INFO, " %s",
1265                                 val_to_str(opcode, mysql_opcode_vals, "Unknown (%u)"));
1266         }
1267         if (req_tree) {
1268                 proto_tree_add_uint_format(req_tree, hf_mysql_opcode, tvb,
1269                                            offset, 1, opcode, "Command: %s (%u)",
1270                                            val_to_str(opcode, mysql_opcode_vals, "Unknown (%u)"),
1271                                            opcode);
1272         }
1273         offset+= 1;
1274
1275
1276         switch (opcode) {
1277
1278         case MYSQL_QUIT:
1279                 if (conn_data->stmts) {
1280                         g_hash_table_destroy(conn_data->stmts);
1281                         conn_data->stmts= NULL;
1282                 }
1283                 break;
1284
1285         case MYSQL_PROCESS_INFO:
1286                 conn_data->state= RESPONSE_TABULAR;
1287                 break;
1288
1289         case MYSQL_DEBUG:
1290         case MYSQL_PING:
1291                 conn_data->state= RESPONSE_OK;
1292                 break;
1293
1294         case MYSQL_STATISTICS:
1295                 conn_data->state= RESPONSE_MESSAGE;
1296                 break;
1297
1298         case MYSQL_INIT_DB:
1299         case MYSQL_CREATE_DB:
1300         case MYSQL_DROP_DB:
1301                 strlen= my_tvb_strsize(tvb, offset);
1302                 if (req_tree) {
1303                         proto_tree_add_item(req_tree, hf_mysql_schema, tvb,
1304                                             offset, strlen, FALSE);
1305                 }
1306                 offset+= strlen;
1307                 conn_data->state= RESPONSE_OK;
1308                 break;
1309
1310         case MYSQL_QUERY:
1311                 strlen= my_tvb_strsize(tvb, offset);
1312                 if (req_tree) {
1313                         proto_tree_add_item(req_tree, hf_mysql_query, tvb,
1314                                             offset, strlen, FALSE);
1315                 }
1316                 offset+= strlen;
1317                 conn_data->state= RESPONSE_TABULAR;
1318                 break;
1319
1320         case MYSQL_STMT_PREPARE:
1321                 strlen= my_tvb_strsize(tvb, offset);
1322                 if (req_tree) {
1323                         proto_tree_add_item(req_tree, hf_mysql_query, tvb,
1324                                             offset, strlen, FALSE);
1325                 }
1326                 offset+= strlen;
1327                 conn_data->state= RESPONSE_PREPARE;
1328                 break;
1329
1330         case MYSQL_STMT_CLOSE:
1331                 if (conn_data->stmts) {
1332                         gint stmt= tvb_get_letohl(tvb, offset);
1333                         g_hash_table_remove(conn_data->stmts, &stmt);
1334                 }
1335                 if (req_tree) {
1336                         proto_tree_add_item(req_tree, hf_mysql_stmt_id,
1337                                             tvb, offset, 4, TRUE);
1338                 }
1339                 offset+= 4;
1340                 conn_data->state= REQUEST;
1341                 break;
1342
1343         case MYSQL_STMT_RESET:
1344                 if (req_tree) {
1345                         proto_tree_add_item(req_tree, hf_mysql_stmt_id,
1346                                             tvb, offset, 4, TRUE);
1347                 }
1348                 offset+= 4;
1349                 conn_data->state= RESPONSE_OK;
1350                 break;
1351
1352         case MYSQL_FIELD_LIST:
1353                 strlen= my_tvb_strsize(tvb, offset);
1354                 if (req_tree) {
1355                         proto_tree_add_text(req_tree, tvb, offset, strlen, "Table name: %s",
1356                                             tvb_get_string(tvb, offset, strlen));
1357                 }
1358                 offset+= strlen;
1359                 conn_data->state= RESPONSE_TABULAR;
1360                 break;
1361
1362         case MYSQL_PROCESS_KILL:
1363                 if (req_tree) {
1364                         proto_tree_add_item(req_tree, hf_mysql_thd_id,
1365                                             tvb, offset, 4, TRUE);
1366                 }
1367                 offset+= 4;
1368                 conn_data->state= RESPONSE_OK;
1369                 break;
1370
1371         case MYSQL_CHANGE_USER:
1372                 strlen= tvb_strsize(tvb, offset);
1373                 if (req_tree) {
1374                         proto_tree_add_item(req_tree, hf_mysql_user, tvb,
1375                                             offset, strlen, FALSE);
1376                 }
1377                 offset+= strlen;
1378
1379                 strlen= tvb_strsize(tvb, offset);
1380                 if (req_tree) {
1381                         proto_tree_add_item(req_tree, hf_mysql_passwd, tvb,
1382                                             offset, strlen, FALSE);
1383                 }
1384                 offset+= strlen;
1385
1386                 strlen= my_tvb_strsize(tvb, offset);
1387                 if (req_tree) {
1388                         proto_tree_add_item(req_tree, hf_mysql_schema, tvb,
1389                                             offset, strlen, FALSE);
1390                 }
1391                 offset+= strlen;
1392                 conn_data->state= RESPONSE_OK;
1393                 break;
1394
1395         case MYSQL_REFRESH:
1396                 {
1397                         proto_item *tf;
1398                         proto_item *rfsh_tree;
1399                         gint refresh= tvb_get_guint8(tvb, offset);
1400
1401                         if (req_tree) {
1402                                 tf= proto_tree_add_uint_format(req_tree, hf_mysql_refresh, tvb, offset, 1, refresh, "Refresh Bitmap: 0x%02X ", refresh);
1403                                 rfsh_tree= proto_item_add_subtree(tf, ett_refresh);
1404                                 proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_grants, tvb, offset, 1, refresh);
1405                                 proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_log, tvb, offset, 1, refresh);
1406                                 proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_tables, tvb, offset, 1, refresh);
1407                                 proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_hosts, tvb, offset, 1, refresh);
1408                                 proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_status, tvb, offset, 1, refresh);
1409                                 proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_threads, tvb, offset, 1, refresh);
1410                                 proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_slave, tvb, offset, 1, refresh);
1411                                 proto_tree_add_boolean(rfsh_tree, hf_mysql_rfsh_master, tvb, offset, 1, refresh);
1412                         }
1413                 }
1414                 offset+= 1;
1415                 conn_data->state= RESPONSE_OK;
1416                 break;
1417
1418         case MYSQL_SHUTDOWN:
1419                 opcode= tvb_get_guint8(tvb, offset);
1420                 if (req_tree) {
1421                         proto_tree_add_uint_format(req_tree, hf_mysql_opcode, tvb, offset,
1422                                                    1, opcode, "Shutdown-Level: %s",
1423                                                    val_to_str(opcode, mysql_shutdown_vals, "Unknown (%u)"));
1424                 }
1425                 offset+= 1;
1426                 conn_data->state= RESPONSE_OK;
1427                 break;
1428
1429         case MYSQL_SET_OPTION:
1430                 option= tvb_get_letohs(tvb, offset);
1431                 if (req_tree) {
1432                         proto_tree_add_uint_format(req_tree, hf_mysql_option, tvb, offset,
1433                                                    2, option, "Option: %s",
1434                                                    val_to_str(option, mysql_option_vals, "Unknown (%u)"));
1435                 }
1436                 offset+= 2;
1437                 conn_data->state= RESPONSE_OK;
1438                 break;
1439
1440         case MYSQL_STMT_FETCH:
1441                 if (req_tree) {
1442                         proto_tree_add_item(req_tree, hf_mysql_stmt_id,
1443                                             tvb, offset, 4, TRUE);
1444                 }
1445                 offset+= 4;
1446
1447                 if (req_tree) {
1448                         proto_tree_add_item(req_tree, hf_mysql_num_rows,
1449                                             tvb, offset, 4, TRUE);
1450                 }
1451                 offset+= 4;
1452                 conn_data->state= RESPONSE_TABULAR;
1453                 break;
1454
1455         case MYSQL_STMT_SEND_LONG_DATA:
1456                 if (req_tree) {
1457                         proto_tree_add_item(req_tree, hf_mysql_stmt_id,
1458                                             tvb, offset, 4, TRUE);
1459                 }
1460                 offset+= 4;
1461
1462                 if (req_tree) {
1463                         proto_tree_add_item(req_tree, hf_mysql_param,
1464                                             tvb, offset, 2, TRUE);
1465                 }
1466                 offset+= 2;
1467
1468                 /* rest is data */
1469                 strlen= tvb_reported_length_remaining(tvb, offset);
1470                 if (tree &&  strlen > 0) {
1471                         proto_tree_add_item(req_tree, hf_mysql_payload,
1472                                             tvb, offset, strlen, FALSE);
1473                 }
1474                 offset+= strlen;
1475                 conn_data->state= REQUEST;
1476                 break;
1477
1478         case MYSQL_STMT_EXECUTE:
1479                 if (req_tree) {
1480                         proto_tree_add_item(req_tree, hf_mysql_stmt_id,
1481                                             tvb, offset, 4, TRUE);
1482                 }
1483                 offset+= 4;
1484
1485                 if (req_tree) {
1486                         proto_tree_add_item(req_tree, hf_mysql_exec_flags,
1487                                             tvb, offset, 1, TRUE);
1488                 }
1489                 offset+= 1;
1490
1491                 if (req_tree) {
1492                         proto_tree_add_item(req_tree, hf_mysql_exec_iter,
1493                                             tvb, offset, 4, TRUE);
1494                 }
1495                 offset+= 4;
1496
1497 #if 0
1498 /* FIXME: rest needs metadata about statement */
1499 #else
1500                 strlen= tvb_reported_length_remaining(tvb, offset);
1501                 if (tree &&  strlen > 0) {
1502                         proto_tree_add_string(req_tree, hf_mysql_payload, tvb, offset,
1503                                               strlen, "FIXME: execute dissector incomplete");
1504                 }
1505                 offset+= strlen;
1506 #endif
1507                 conn_data->state= RESPONSE_TABULAR;
1508                 break;
1509
1510 /* FIXME: implement replication packets */
1511         case MYSQL_BINLOG_DUMP:
1512         case MYSQL_TABLE_DUMP:
1513         case MYSQL_CONNECT_OUT:
1514         case MYSQL_REGISTER_SLAVE:
1515                 if (req_tree) {
1516                         proto_tree_add_string(req_tree, hf_mysql_payload, tvb, offset, -1,
1517                                               "FIXME: implement replication packets");
1518                 }
1519                 offset+= tvb_length_remaining(tvb, offset);
1520                 conn_data->state= REQUEST;
1521                 break;
1522
1523         default:
1524                 if (req_tree) {
1525                         proto_tree_add_string(req_tree, hf_mysql_payload, tvb, offset, -1,
1526                                               "unknown/invalid command code");
1527                 }
1528                 offset+= tvb_length_remaining(tvb, offset);
1529                 conn_data->state= UNDEFINED;
1530         }
1531
1532         return offset;
1533 }
1534
1535
1536 static int mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo, int offset,
1537                                   proto_tree *tree, my_conn_data_t *conn_data)
1538 {
1539         gint response_code;
1540         gint strlen;
1541
1542         response_code= tvb_get_guint8(tvb, offset);
1543
1544         if (response_code == 0xff ) {
1545                 offset= mysql_dissect_error_packet(tvb, pinfo, offset+1, tree);
1546                 conn_data->state= REQUEST;
1547         }
1548
1549         else if (response_code == 0xfe && tvb_length_remaining(tvb, offset) < 9) {
1550
1551                 if (tree) {
1552                         proto_tree_add_uint_format(tree, hf_mysql_eof, tvb, offset, 1,
1553                                                    response_code, "EOF marker (%u)",
1554                                                    response_code);
1555                 }
1556                 offset+= 1;
1557
1558                 /* pre-4.1 packet ends here */
1559                 if (tvb_length_remaining(tvb, offset)) {
1560                         if (tree) {
1561                                 proto_tree_add_item(tree, hf_mysql_num_warn,
1562                                                     tvb, offset, 2, FALSE);
1563                         }
1564                         offset= mysql_dissect_server_status(tvb, offset+2, tree);
1565                 }
1566
1567                 if (conn_data->state == FIELD_PACKET) {
1568                         conn_data->state= ROW_PACKET;
1569                 } else {
1570                         conn_data->state= REQUEST;
1571                 }
1572         }
1573
1574         else if (response_code == 0) {
1575                 if (tvb_length_remaining(tvb, offset+1)
1576                     > tvb_get_fle(tvb, offset+1, NULL, NULL)) {
1577                         offset= mysql_dissect_ok_packet(tvb, pinfo, offset+1,
1578                                                         tree, conn_data);
1579                 } else {
1580                         offset= mysql_dissect_result_header(tvb, pinfo, offset,
1581                                                             tree, conn_data);
1582                 }
1583         }
1584
1585         else {
1586                 switch (conn_data->state) {
1587                 case RESPONSE_MESSAGE:
1588                         if ((strlen= tvb_length_remaining(tvb, offset))) {
1589                                 if (tree) {
1590                                         proto_tree_add_item(tree, hf_mysql_message, tvb,
1591                                                             offset, strlen, FALSE);
1592                                 }
1593                                 offset+= strlen;
1594                         }
1595                         conn_data->state= REQUEST;
1596                         break;
1597
1598                 case RESPONSE_TABULAR:
1599                         offset= mysql_dissect_result_header(tvb, pinfo, offset,
1600                                                             tree, conn_data);
1601                         break;
1602
1603                 case FIELD_PACKET:
1604                         offset= mysql_dissect_field_packet(tvb, offset, tree);
1605                         break;
1606
1607                 case ROW_PACKET:
1608                         offset= mysql_dissect_row_packet(tvb, offset, tree);
1609                         break;
1610
1611                 case RESPONSE_PREPARE:
1612                         offset= mysql_dissect_response_prepare(tvb, offset, tree);
1613                         break;
1614
1615                 case PARAM_PACKET:
1616                         offset= mysql_dissect_param_packet(tvb, offset, tree);
1617                         break;
1618
1619                 default:
1620                         if (tree) {
1621                                 proto_tree_add_string(tree, hf_mysql_payload, tvb, offset, -1,
1622                                                       "unknown/invalid response");
1623                         }
1624                         offset+= tvb_length_remaining(tvb, offset);
1625                         conn_data->state= UNDEFINED;
1626                 }
1627         }
1628
1629         return offset;
1630 }
1631
1632
1633 static int mysql_dissect_error_packet(tvbuff_t *tvb, packet_info *pinfo,
1634                                       int offset, proto_tree *tree)
1635 {
1636         gint error_code;
1637         error_code= tvb_get_letohs(tvb, offset);
1638
1639         if (check_col(pinfo->cinfo, COL_INFO)) {
1640                 col_append_fstr(pinfo->cinfo, COL_INFO, " Error %d", error_code);
1641         }
1642         if (tree) {
1643                 proto_tree_add_uint(tree, hf_mysql_error_code, tvb,
1644                                     offset, 2, error_code);
1645         }
1646         offset+= 2;
1647
1648         if (tvb_get_guint8(tvb, offset) == '#')
1649         {
1650                 offset+= 1;
1651                 proto_tree_add_item(tree, hf_mysql_sqlstate, tvb, offset, 5, FALSE);
1652                 offset+= 5;
1653         }
1654
1655         proto_tree_add_item(tree, hf_mysql_error_string, tvb, offset, -1, FALSE);
1656         offset+= tvb_reported_length_remaining(tvb, offset);
1657
1658         return offset;
1659 }
1660
1661
1662 static int mysql_dissect_ok_packet(tvbuff_t *tvb, packet_info *pinfo, int offset,
1663                                    proto_tree *tree, my_conn_data_t *conn_data)
1664 {
1665         gint strlen;
1666         guint64 affected_rows;
1667         guint64 insert_id;
1668         int fle;
1669
1670         if (check_col(pinfo->cinfo, COL_INFO)) {
1671                 col_append_str(pinfo->cinfo, COL_INFO, " OK" );
1672         }
1673
1674         fle= tvb_get_fle(tvb, offset, &affected_rows, NULL);
1675         if (tree) {
1676                 proto_tree_add_uint64(tree, hf_mysql_affected_rows,
1677                                       tvb, offset, fle, affected_rows);
1678         }
1679         offset+= fle;
1680
1681         fle= tvb_get_fle(tvb, offset, &insert_id, NULL);
1682         if (tree && insert_id) {
1683                 proto_tree_add_uint64(tree, hf_mysql_insert_id,
1684                                       tvb, offset, fle, insert_id);
1685         }
1686         offset+= fle;
1687
1688         offset= mysql_dissect_server_status(tvb, offset, tree);
1689
1690         /* 4.1+ protocol only: 2 bytes number of warnings */
1691         if (conn_data->clnt_caps & conn_data->srv_caps & MYSQL_CAPS_CU) {
1692                 if (tree) {
1693                         proto_tree_add_item(tree, hf_mysql_num_warn, tvb,
1694                                             offset, 2, FALSE);
1695                 }
1696                 offset+= 2;
1697         }
1698
1699         /* optional: message string */
1700         if ((strlen= tvb_length_remaining(tvb, offset))) {
1701                 if (tree) {
1702                         proto_tree_add_item(tree, hf_mysql_message, tvb,
1703                                             offset, strlen, FALSE);
1704                 }
1705                 offset+= strlen;
1706         }
1707
1708         conn_data->state= REQUEST;
1709         return offset;
1710 }
1711
1712
1713 static int mysql_dissect_server_status(tvbuff_t *tvb, int offset, proto_tree *tree)
1714 {
1715         guint16 status;
1716         proto_item *tf;
1717         proto_item *stat_tree;
1718
1719         status= tvb_get_letohs(tvb, offset);
1720         if (tree) {
1721                 tf= proto_tree_add_uint_format(tree, hf_mysql_status, tvb, offset, 2, status, "Server Status: 0x%04X ", status);
1722                 stat_tree= proto_item_add_subtree(tf, ett_stat);
1723                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_it, tvb, offset, 2, status);
1724                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_ac, tvb, offset, 2, status);
1725                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_mr, tvb, offset, 2, status);
1726                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_mu, tvb, offset, 2, status);
1727                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_bi, tvb, offset, 2, status);
1728                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_ni, tvb, offset, 2, status);
1729                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_cr, tvb, offset, 2, status);
1730                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_lr, tvb, offset, 2, status);
1731                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_dr, tvb, offset, 2, status);
1732                 proto_tree_add_boolean(stat_tree, hf_mysql_stat_bs, tvb, offset, 2, status);
1733         }
1734         offset+= 2;
1735
1736         return offset;
1737 }
1738
1739
1740 static int mysql_dissect_collation(tvbuff_t *tvb, int offset, proto_tree *tree, guint16 caps)
1741 {
1742         gint charset= tvb_get_guint8(tvb, offset);
1743         if (tree) {
1744                 proto_tree_add_uint_format(tree, hf_mysql_charset, tvb, offset, 1,
1745                                            charset, "Charset: %s (%u)",
1746                                            val_to_str(charset,
1747                                                       caps & MYSQL_CAPS_CU
1748                                                       ? mysql_collation_vals
1749                                                       : mysql_charset_vals,
1750                                                       "Unknown (%u)"), charset);
1751         }
1752         offset+= 1;
1753         return offset;
1754 }
1755
1756
1757 static int mysql_dissect_caps(tvbuff_t *tvb, int offset, proto_tree *tree,
1758                               guint16 *caps, const char* whom)
1759 {
1760         *caps= tvb_get_letohs(tvb, offset);
1761         if (tree) {
1762                 proto_item *tf= proto_tree_add_uint_format(tree, hf_mysql_caps, tvb, offset, 2, *caps,
1763                                                            "%s Capabilities: 0x%04X ", whom, *caps);
1764                 proto_item *cap_tree= proto_item_add_subtree(tf, ett_caps);
1765                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_password, tvb, offset, 2, *caps);
1766                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_found_rows, tvb, offset, 2, *caps);
1767                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_flag, tvb, offset, 2, *caps);
1768                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_connect_with_db, tvb, offset, 2, *caps);
1769                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_no_schema, tvb, offset, 2, *caps);
1770                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_compress, tvb, offset, 2, *caps);
1771                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_odbc, tvb, offset, 2, *caps);
1772                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_local_files, tvb, offset, 2, *caps);
1773                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_space, tvb, offset, 2, *caps);
1774                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_change_user, tvb, offset, 2, *caps);
1775                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_interactive, tvb, offset, 2, *caps);
1776                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_ssl, tvb, offset, 2, *caps);
1777                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_sigpipe, tvb, offset, 2, *caps);
1778                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_transactions, tvb, offset, 2, *caps);
1779                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_reserved, tvb, offset, 2, *caps);
1780                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_secure_connect, tvb, offset, 2, *caps);
1781         }
1782
1783         offset+= 2;
1784         return offset;
1785 }
1786
1787
1788 static int mysql_dissect_ext_caps(tvbuff_t *tvb, int offset, proto_tree *tree,
1789                                   guint16 *caps, const char* whom)
1790 {
1791         proto_item *extcap_tree;
1792         *caps= tvb_get_letohs(tvb, offset);
1793         if (tree) {
1794                 proto_item *tf= proto_tree_add_uint_format(tree, hf_mysql_extcaps, tvb, offset, 2, *caps,
1795                                                            "Extended %s Capabilities: 0x%04X ", whom, *caps);
1796                 extcap_tree= proto_item_add_subtree(tf, ett_extcaps);
1797                 proto_tree_add_boolean(extcap_tree, hf_mysql_cap_multi_statements, tvb, offset, 2, *caps);
1798                 proto_tree_add_boolean(extcap_tree, hf_mysql_cap_multi_results, tvb, offset, 2, *caps);
1799         }
1800
1801         offset+= 2;
1802         return offset;
1803 }
1804
1805
1806 static int mysql_dissect_result_header(tvbuff_t *tvb, packet_info *pinfo, int offset,
1807                                        proto_tree *tree, my_conn_data_t *conn_data)
1808 {
1809         gint fle;
1810         guint64 num_fields, extra;
1811
1812         if (check_col(pinfo->cinfo, COL_INFO)) {
1813                 col_append_str(pinfo->cinfo, COL_INFO, " TABULAR" );
1814         }
1815
1816         fle= tvb_get_fle(tvb, offset, &num_fields, NULL);
1817         if (tree) {
1818                 proto_tree_add_uint64(tree, hf_mysql_num_fields,
1819                                       tvb, offset, fle, num_fields);
1820         }
1821         offset+= fle;
1822
1823         if (tvb_length_remaining(tvb, offset)) {
1824                 fle= tvb_get_fle(tvb, offset, &extra, NULL);
1825                 if (tree) {
1826                         proto_tree_add_uint64(tree, hf_mysql_extra,
1827                                               tvb, offset, fle, extra);
1828                 }
1829                 offset+= fle;
1830         }
1831
1832         if (num_fields) {
1833                 conn_data->state= FIELD_PACKET;
1834         } else {
1835                 conn_data->state= ROW_PACKET;
1836         }
1837
1838         return offset;
1839 }
1840
1841
1842 static int mysql_dissect_field_packet(tvbuff_t *tvb, int offset, proto_tree *tree)
1843 {
1844         proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_field_packet()");
1845         return offset + tvb_length_remaining(tvb, offset);
1846 }
1847
1848
1849 static int mysql_dissect_row_packet(tvbuff_t *tvb, int offset, proto_tree *tree)
1850 {
1851         proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_row_packet()");
1852         return offset + tvb_length_remaining(tvb, offset);
1853 }
1854
1855
1856 static int mysql_dissect_response_prepare(tvbuff_t *tvb, int offset, proto_tree *tree)
1857 {
1858         proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_response_prepare()");
1859         return offset + tvb_length_remaining(tvb, offset);
1860 }
1861
1862
1863 static int mysql_dissect_param_packet(tvbuff_t *tvb, int offset, proto_tree *tree)
1864 {
1865         proto_tree_add_text(tree, tvb, offset, -1, "FIXME: write mysql_dissect_param_packet()");
1866         return offset + tvb_length_remaining(tvb, offset);
1867 }
1868
1869
1870
1871 /*
1872  get length of string in packet buffer
1873
1874  SYNOPSIS
1875    my_tvb_strsize()
1876      tvb      packet buffer
1877      offset   current offset
1878
1879  DESCRIPTION
1880    deliver length of string, delimited by either \0 or end of buffer
1881
1882  RETURN VALUE
1883    length of string found, including \0 (if present)
1884
1885 */
1886 static gint my_tvb_strsize(tvbuff_t *tvb, int offset)
1887 {
1888         gint len = tvb_strnlen(tvb, offset, -1);
1889         if (len == -1) {
1890                 len = tvb_length_remaining(tvb, offset);
1891         } else {
1892                 len++; /* the trailing \0 */
1893         }
1894         return len;
1895 }
1896
1897
1898 /*
1899  read "field length encoded" value from packet buffer
1900
1901  SYNOPSIS
1902    tvb_get_fle()
1903      tvb     in    packet buffer
1904      offset  in    offset in buffer
1905      res     out   where to store FLE value, may be NULL
1906      is_null out   where to store ISNULL flag, may be NULL
1907
1908  DESCRIPTION
1909    read FLE from packet buffer and store its value and ISNULL flag
1910    in caller provided variables
1911
1912  RETURN VALUE
1913    length of FLE
1914 */
1915 static int tvb_get_fle(tvbuff_t *tvb, int offset, guint64 *res, guint8 *is_null)
1916 {
1917         guint8 prefix= tvb_get_guint8(tvb, offset);
1918
1919         if (is_null)
1920                 *is_null= 0;
1921
1922         switch (prefix) {
1923         case 251:
1924                 if (res)
1925                         *res= 0;
1926                 if (is_null)
1927                         *is_null= 1;
1928                 break;
1929         case 252:
1930                 if (res)
1931                         *res= tvb_get_letohs(tvb, offset+1);
1932                 return 3;
1933         case 253:
1934                 if (res)
1935                         *res= tvb_get_letohl(tvb, offset+1);
1936                 return 5;
1937         case 254:
1938                 if (res)
1939                         *res= tvb_get_letoh64(tvb, offset+1);
1940                 return 9;
1941         default:
1942                 if (res)
1943                         *res= prefix;
1944         }
1945
1946         return 1;
1947 }
1948
1949