waste a couple of bytes per tcp conversation and make the tree for acked_packets...
[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  * $Id$
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@ethereal.com>
10  * Copyright 1998 Gerald Combs
11  *
12  * Copied from packet-tftp.c
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  *
28  *
29  * the protocol spec at 
30  *      http://public.logicacmg.com/~redferni/mysql/MySQL-Protocol.html
31  * and MySQL source code  
32  */
33
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37
38 #include <glib.h>
39 #include <epan/packet.h>
40 #include <epan/conversation.h>
41 #include <epan/emem.h>
42
43 #include "packet-tcp.h"
44 #include <epan/reassemble.h>
45 #include <epan/prefs.h>
46
47 /* Capabilities */
48 #define MYSQL_CAPS_LP 0x0001
49 #define MYSQL_CAPS_FR 0x0002
50 #define MYSQL_CAPS_LF 0x0004
51 #define MYSQL_CAPS_CD 0x0008
52 #define MYSQL_CAPS_NS 0x0010
53 #define MYSQL_CAPS_CP 0x0020
54 #define MYSQL_CAPS_OB 0x0040
55 #define MYSQL_CAPS_LI 0x0080
56 #define MYSQL_CAPS_IS 0x0100
57 #define MYSQL_CAPS_CU 0x0200
58 #define MYSQL_CAPS_IA 0x0400
59 #define MYSQL_CAPS_SL 0x0800
60 #define MYSQL_CAPS_II 0x1000
61 #define MYSQL_CAPS_TA 0x2000
62
63 static int proto_mysql = -1;
64 static int hf_mysql_packet_length= -1;
65 static int hf_mysql_packet_number= -1;
66 static int hf_mysql_opcode= -1;
67 static int hf_mysql_response_code= -1;
68 static int hf_mysql_error_code= -1;
69 static int hf_mysql_payload= -1;
70 static int hf_mysql_protocol= -1;
71 static int hf_mysql_caps= -1;
72 static int hf_mysql_cap_long_password= -1;
73 static int hf_mysql_cap_found_rows= -1;
74 static int hf_mysql_cap_long_flag= -1;
75 static int hf_mysql_cap_connect_with_db= -1;
76 static int hf_mysql_cap_no_schema= -1;
77 static int hf_mysql_cap_compress= -1;
78 static int hf_mysql_cap_odbc= -1;
79 static int hf_mysql_cap_local_files= -1;
80 static int hf_mysql_cap_ignore_space= -1;
81 static int hf_mysql_cap_change_user= -1;
82 static int hf_mysql_cap_interactive= -1;
83 static int hf_mysql_cap_ssl= -1;
84 static int hf_mysql_cap_ignore_sigpipe= -1;
85 static int hf_mysql_cap_transactions= -1;
86 static int hf_mysql_version = -1;
87 static int hf_mysql_max_packet= -1;
88 static int hf_mysql_user= -1;
89 static int hf_mysql_password= -1;
90 static int hf_mysql_thread_id = -1;
91 static int hf_mysql_salt= -1;
92 static int hf_mysql_charset= -1;
93 static int hf_mysql_status= -1;
94 static int hf_mysql_unused= -1;
95 static int hf_mysql_parameter= -1;
96
97 static gint ett_mysql = -1;
98 static gint ett_server_greeting = -1;
99 static gint ett_caps = -1;
100 static gint ett_request = -1;
101
102 static gboolean mysql_desegment = TRUE;
103
104 #define TCP_PORT_MySQL   3306 
105
106 #define MySQL_SLEEP             0
107 #define MySQL_QUIT              1
108 #define MySQL_INIT_DB           2
109 #define MySQL_QUERY             3
110 #define MySQL_FIELD_LIST        4
111 #define MySQL_CREATE_DB         5
112 #define MySQL_DROP_DB           6
113 #define MySQL_REFRESH           7
114 #define MySQL_SHUTDOWN          8
115 #define MySQL_STATISTICS        9
116 #define MySQL_PROCESS_INFO      10
117 #define MySQL_CONNECT           11
118 #define MySQL_PROCESS_KILL      12
119 #define MySQL_DEBUG             13
120 #define MySQL_PING              14
121 #define MySQL_TIME              15
122 #define MySQL_DELAY_INSERT      16
123 #define MySQL_CHANGE_USER       17
124 #define MySQL_BINLOG_DUMP       18
125 #define MySQL_TABLE_DUMP        19
126 #define MySQL_CONNECT_OUT       20
127
128
129 static const value_string mysql_opcode_vals[] = {
130   { MySQL_SLEEP,   "SLEEP" },
131   { MySQL_QUIT,   "Quit" },
132   { MySQL_INIT_DB,  "Init Database" },
133   { MySQL_QUERY,   "Query" },
134   { MySQL_FIELD_LIST, "Field List" },
135   { MySQL_CREATE_DB,  "Create Database" },
136   { MySQL_DROP_DB , "Drop Database" },
137   { MySQL_REFRESH , "Refresh" },
138   { MySQL_SHUTDOWN , "Shutdown" },
139   { MySQL_STATISTICS , "Statistics" },
140   { MySQL_PROCESS_INFO , "Process Info" },
141   { MySQL_CONNECT , "Connect" },
142   { MySQL_PROCESS_KILL , "Process Kill" },
143   { MySQL_DEBUG , "Debug" },
144   { MySQL_PING , "Ping" },
145   { MySQL_TIME , "Time" },
146   { MySQL_DELAY_INSERT , "Delay Insert" },
147   { MySQL_CHANGE_USER , "Change User" },
148   { MySQL_BINLOG_DUMP , "Binlog Dump" },
149   { MySQL_TABLE_DUMP, "Table Dump" },
150   { MySQL_CONNECT_OUT, "Table Connect Out" },
151   { 0,          NULL }
152 };
153
154 static const value_string mysql_status_vals[] = {
155         {1, "IN_TRANS" },
156         {2, "AUTOCOMMIT"},
157         { 0, NULL }
158 };
159 static const value_string mysql_charset_vals[] = {
160         {1, "big5"}, 
161         {2, "czech"},  
162         {3,"dec8"},
163         {4, "dos" },  
164         {5,"german1"}, 
165         {6,"hp8"},  
166         {7,"koi8_ru"},
167         {8,"latin1"},  
168         {9,"latin2"}, 
169         {9,"swe7 "},
170         {10,"usa7"},
171         {11,"ujis"},
172         {12,"sjis"},
173         {13,"cp1251"},
174         {14,"danish"},
175         {15,"hebrew"},
176         {16,"win1251"},
177         {17,"tis620"},
178         {18,"euc_kr"},
179         {19,"estonia"},
180         {20,"hungarian"},
181         {21,"koi8_ukr"},
182         {22,"win1251ukr"},
183         {23,"gb2312"},
184         {24,"greek"},
185         {25,"win1250"},
186         {26,"croat"},
187         {27,"gbk"},
188         {28,"cp1257"},
189         {29,"latin5"},
190         {0,NULL}
191 };
192 #if 0
193 static const value_string mysql_error_code_vals[] = {
194   { 0, "Not defined" },
195   { 1, "File not found" },
196   { 2, "Access violation" },
197   { 3, "Disk full or allocation exceeded" },
198   { 4, "Illegal MySQL Operation" },
199   { 5, "Unknown transfer ID" },
200   { 6, "File already exists" },
201   { 7, "No such user" },
202   { 8, "Option negotiation failed" },
203   { 0, NULL }
204 };
205 #endif
206
207 static guint get_mysql_pdu_len(tvbuff_t *tvb, int offset);
208 static void dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo,
209                 proto_tree *tree);
210 static int mysql_dissect_server_greeting(tvbuff_t *tvb, packet_info *pinfo, 
211                 int offset, proto_tree *tree);
212 static int mysql_dissect_authentication(tvbuff_t *tvb, packet_info *pinfo,
213                 int offset, proto_tree *tree);
214 static int mysql_dissect_request(tvbuff_t *tvb, packet_info *pinfo,
215                 int offset, proto_tree *tree);
216 static int mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo,
217                 int offset, proto_tree *tree);
218
219 static void
220 dissect_mysql(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
221 {
222         tcp_dissect_pdus(tvb, pinfo, tree, mysql_desegment, 3,
223             get_mysql_pdu_len, dissect_mysql_pdu);
224 }
225
226 static guint
227 get_mysql_pdu_len(tvbuff_t *tvb, int offset)
228 {
229         guint plen;
230
231         /*
232          * Get the length of the MySQL packet.
233          */
234         plen = tvb_get_letoh24(tvb, offset);
235
236         /*
237          * That length doesn't include the length field or the packet
238          * number itself; add them in.
239          */
240         return plen + 4;
241 }
242
243 static void
244 dissect_mysql_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
245 {
246         proto_tree      *mysql_tree = NULL;
247         proto_item      *ti;
248         conversation_t  *conversation;
249
250         int             offset = 0;
251         guint           packet_number;
252
253         gboolean        is_response;
254
255         conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
256                 pinfo->srcport, pinfo->destport, 0);
257
258         if (!conversation) {
259                 /* create a new conversation */
260                 conversation = conversation_new(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
261                                 pinfo->srcport, pinfo->destport, 0);
262         }
263
264
265         if (check_col(pinfo->cinfo, COL_PROTOCOL))
266                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "MySQL");
267
268         if (pinfo->destport == pinfo->match_port) {
269                 is_response=FALSE;
270         }else {
271                 is_response=TRUE;
272         }
273
274         if (tree) {
275                   ti = proto_tree_add_item(tree, proto_mysql, tvb, offset, -1, FALSE);
276                   mysql_tree = proto_item_add_subtree(ti, ett_mysql);
277
278                   proto_tree_add_item(mysql_tree, hf_mysql_packet_length, tvb,
279                             offset, 3, TRUE);
280         }
281         offset += 3;
282 /* packet number */
283         packet_number= tvb_get_guint8(tvb, offset);
284         if (tree) {
285                   proto_tree_add_uint(mysql_tree, hf_mysql_packet_number, tvb,
286                     offset, 1, packet_number);
287         }
288         offset += 1;
289
290         /*      
291          *      packet == 0 && response --> server greeting
292          *      packet == 1 && request --> login request
293          */ 
294         if(is_response ) {
295                 if( packet_number == 0 ) {
296                         if (check_col(pinfo->cinfo, COL_INFO)) {
297                                 col_add_str(pinfo->cinfo, COL_INFO, "Server Greeting" ) ; 
298                         }
299                         offset = mysql_dissect_server_greeting(tvb,pinfo,offset,mysql_tree);
300                 }else {
301                         if (check_col(pinfo->cinfo, COL_INFO)) {
302                                 col_add_str(pinfo->cinfo, COL_INFO, "Response" ) ; 
303                         }
304                         offset = mysql_dissect_response(tvb,pinfo,offset,mysql_tree);
305                 }
306         } else {
307                 if( packet_number == 1 ) {
308                         if (check_col(pinfo->cinfo, COL_INFO)) {
309                                 col_add_str(pinfo->cinfo, COL_INFO, "Login Request") ; 
310                         }
311                         offset = mysql_dissect_authentication(tvb,pinfo,offset,mysql_tree);
312                 }else {
313                         if (check_col(pinfo->cinfo, COL_INFO)) {
314                                 col_add_str(pinfo->cinfo, COL_INFO, "Request") ; 
315                         }
316                         offset = mysql_dissect_request(tvb,pinfo,offset,mysql_tree);
317                 }
318         }
319
320 /* payload */
321         if (tree && tvb_reported_length_remaining(tvb, offset) > 0) {
322                 proto_tree_add_item(mysql_tree, hf_mysql_payload,
323                     tvb, offset, -1, FALSE);
324         }
325 }
326 static int 
327 mysql_dissect_response(tvbuff_t *tvb, packet_info *pinfo,
328                 int offset, proto_tree *tree)
329 {
330         gint response_code;
331         gint error_code;
332
333         /* response code */
334         response_code= tvb_get_guint8(tvb, offset);
335         if (tree) {
336                 proto_tree_add_uint(tree, hf_mysql_response_code, tvb,
337                 offset, 1, response_code);
338         }
339         offset +=1;
340                 
341         if(response_code== 0xff ) {
342                         /* error code */
343                 error_code = tvb_get_letohs(tvb, offset);
344                 if (check_col(pinfo->cinfo, COL_INFO)) {
345                         col_append_fstr(pinfo->cinfo, COL_INFO, " Error Code: %x", error_code ); 
346                 }
347                 if (tree) {
348                         proto_tree_add_uint(tree, hf_mysql_error_code, tvb,
349                         offset, 2, error_code);
350                 }
351                 offset +=2;
352                         
353         } else {
354                 if (check_col(pinfo->cinfo, COL_INFO)) {
355                         col_append_str(pinfo->cinfo, COL_INFO, " OK" ); 
356                 }
357         }
358         return offset;
359 }
360
361 static int
362 mysql_dissect_request(tvbuff_t *tvb,packet_info *pinfo,
363                 int offset, proto_tree *tree)
364 {
365         gint opcode;
366         gint strlen;
367         proto_item *tf;
368         proto_item *req_tree=NULL;
369
370         if(tree) {
371                 tf=proto_tree_add_text(tree,tvb,offset,-1,"Command");
372                 req_tree = proto_item_add_subtree(tf ,ett_request);
373         }
374
375         opcode = tvb_get_guint8(tvb, offset);
376         
377         if (check_col(pinfo->cinfo, COL_INFO)) {
378                 col_append_fstr(pinfo->cinfo, COL_INFO, " Command: %s", 
379                         val_to_str(opcode, mysql_opcode_vals, "Unknown (%u)"));
380         }
381
382         if (req_tree) {
383                 proto_tree_add_uint_format(req_tree, hf_mysql_opcode, tvb, 
384                                 offset , 1, opcode, "Command: %s (%u)", 
385                                 val_to_str(opcode, mysql_opcode_vals, "Unknown (%u)"),opcode);
386         }
387         /* command parameter */
388
389         offset += 1;
390         if ( (strlen = tvb_length_remaining(tvb,offset)) > 0 ) {
391                 
392                 if (check_col(pinfo->cinfo, COL_INFO)) {
393                         col_append_fstr(pinfo->cinfo, COL_INFO, " : %s",
394                                         tvb_format_text(tvb,offset,strlen));
395                 }
396                 
397                 if (tree) {
398                         proto_tree_add_item(req_tree, hf_mysql_parameter, tvb,
399                                 offset, strlen, FALSE );
400                 }
401                 offset +=strlen;
402         }
403
404         return offset;
405 }
406
407
408 static int
409 mysql_dissect_authentication(tvbuff_t *tvb, packet_info *pinfo,
410                 int offset, proto_tree *tree)
411 {
412         gint16  client_caps;
413         gint32  max_packet;
414         gint    strlen;
415
416         proto_item *tf;
417         proto_item *cap_tree;
418         proto_item *login_tree=NULL;
419
420         if(tree) {
421                 tf=proto_tree_add_text(tree,tvb,offset,-1,"Login Packet");
422                 login_tree = proto_item_add_subtree(tf ,ett_server_greeting);
423         }
424
425         client_caps= tvb_get_letohs(tvb, offset);
426
427         if (check_col(pinfo->cinfo, COL_INFO)) {
428                 col_append_fstr(pinfo->cinfo, COL_INFO, " Caps: 0x%x",client_caps) ; 
429         }
430         if(tree) {
431                 tf = proto_tree_add_uint_format(login_tree, hf_mysql_caps, tvb, offset , 1, client_caps, "Caps: 0x%04x ", client_caps );
432                 cap_tree = proto_item_add_subtree(tf, ett_caps);
433                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_password, tvb, offset, 2, client_caps);
434                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_found_rows, tvb, offset, 2, client_caps);
435                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_flag, tvb, offset, 2, client_caps);
436                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_connect_with_db, tvb, offset, 2, client_caps);
437                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_no_schema, tvb, offset, 2, client_caps);
438                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_compress, tvb, offset, 2, client_caps);
439                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_odbc, tvb, offset, 2, client_caps);
440                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_local_files, tvb, offset, 2, client_caps);
441                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_space, tvb, offset, 2, client_caps);
442                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_change_user, tvb, offset, 2, client_caps);
443                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_interactive, tvb, offset, 2, client_caps);
444                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_ssl, tvb, offset, 2, client_caps);
445                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_sigpipe, tvb, offset, 2, client_caps);
446                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_transactions, tvb, offset, 2, client_caps);
447         }
448
449 /*      proto_tree_add_uint(tree, hf_mysql_client_caps, tvb,
450                 offset, 2, client_caps);
451 */
452         offset +=2;
453         /* 3 bytes max packet, 16777216 - x */
454         max_packet = 0xffffff - tvb_get_letoh24(tvb, offset);
455         if(tree) {
456                 proto_tree_add_uint(login_tree, hf_mysql_max_packet, tvb,
457                         offset, 3, max_packet);
458         }
459         offset +=3;
460         /* User name */
461         strlen = tvb_strsize(tvb,offset);
462
463         if (check_col(pinfo->cinfo, COL_INFO)) {
464                 col_append_fstr(pinfo->cinfo, COL_INFO, " ,user:  %s",
465                                 tvb_get_ptr(tvb,offset,strlen));
466         }
467         if (tree) {
468                 proto_tree_add_item(login_tree, hf_mysql_user, tvb,
469                         offset, strlen, FALSE );
470         }
471         offset +=strlen;
472         
473         /* Password */
474         strlen = tvb_length_remaining(tvb,offset);
475
476         if (check_col(pinfo->cinfo, COL_INFO)) {
477                 col_append_fstr(pinfo->cinfo, COL_INFO, " ,password:  %s",
478                                 tvb_get_ptr(tvb,offset,strlen));
479         }
480         if (tree) {
481                 proto_tree_add_item(login_tree, hf_mysql_password, tvb,
482                         offset, strlen, FALSE );
483         }
484         offset +=strlen;
485         
486                 
487         return offset;
488 }
489
490
491 static int 
492 mysql_dissect_server_greeting(tvbuff_t *tvb, packet_info *pinfo,
493                 int offset, proto_tree *tree)
494 {
495         gint protocol;
496         gint strlen;
497         gint32 thread_id;
498         gint16 server_caps;
499         gint charset;
500         gint16 status;
501         
502         proto_item *tf;
503         proto_item *greeting_tree=NULL;
504         proto_item *cap_tree;
505
506         protocol= tvb_get_guint8(tvb, offset);
507
508         if(tree) {
509                 tf = proto_tree_add_text(tree,tvb,offset,-1,"Server Greeting");
510                 greeting_tree = proto_item_add_subtree(tf ,ett_server_greeting);
511         }
512         
513         if (check_col(pinfo->cinfo, COL_INFO)) {
514                 col_append_fstr(pinfo->cinfo, COL_INFO, " Protocol : %d",protocol) ; 
515         }
516         if (tree) {
517                 proto_tree_add_uint(greeting_tree, hf_mysql_protocol, tvb,
518                         offset, 1, protocol);
519         }
520         offset +=1;
521         /* version string */
522
523         strlen = tvb_strsize(tvb,offset);
524
525         if (check_col(pinfo->cinfo, COL_INFO)) {
526                 col_append_fstr(pinfo->cinfo, COL_INFO, " ,version:  %s",
527                                 tvb_get_ptr(tvb,offset,strlen));
528         }
529         if (tree) {
530                 proto_tree_add_item(greeting_tree, hf_mysql_version, tvb,
531                         offset, strlen, FALSE );
532         }
533         offset +=strlen;
534                 
535         /* 4 bytes little endian thread_id */
536         thread_id = tvb_get_letohl(tvb, offset);
537         if(tree) {
538                 proto_tree_add_uint(greeting_tree, hf_mysql_thread_id, tvb,
539                         offset, 4, thread_id);
540         }
541         offset +=4;
542         /* salt string */
543         strlen = tvb_strsize(tvb,offset);
544         if (tree) {
545                 proto_tree_add_item(greeting_tree, hf_mysql_salt, tvb,
546                         offset, strlen, FALSE );
547         }
548         offset +=strlen;
549         /* 2 bytes CAPS */
550         server_caps= tvb_get_letohs(tvb, offset);
551
552         if (check_col(pinfo->cinfo, COL_INFO)) {
553                 col_append_fstr(pinfo->cinfo, COL_INFO, " Caps: 0x%x",server_caps) ; 
554         }
555         if(tree) {
556                 tf = proto_tree_add_uint_format(greeting_tree, hf_mysql_caps, tvb, offset , 1, server_caps, "Caps: 0x%04x ", server_caps );
557                 cap_tree = proto_item_add_subtree(tf, ett_caps);
558                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_password, tvb, offset, 2, server_caps);
559                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_found_rows, tvb, offset, 2, server_caps);
560                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_long_flag, tvb, offset, 2, server_caps);
561                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_connect_with_db, tvb, offset, 2, server_caps);
562                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_no_schema, tvb, offset, 2, server_caps);
563                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_compress, tvb, offset, 2, server_caps);
564                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_odbc, tvb, offset, 2, server_caps);
565                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_local_files, tvb, offset, 2, server_caps);
566                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_space, tvb, offset, 2, server_caps);
567                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_change_user, tvb, offset, 2, server_caps);
568                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_interactive, tvb, offset, 2, server_caps);
569                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_ssl, tvb, offset, 2, server_caps);
570                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_ignore_sigpipe, tvb, offset, 2, server_caps);
571                 proto_tree_add_boolean(cap_tree, hf_mysql_cap_transactions, tvb, offset, 2, server_caps);
572         }
573         offset+=2;
574         /* 1 byte charset */
575         charset = tvb_get_guint8(tvb, offset);
576         if (tree) {
577                 proto_tree_add_uint_format(greeting_tree, hf_mysql_charset, tvb,
578                         offset, 1, charset, "Charset: %s (%u)", 
579                         val_to_str(charset, mysql_charset_vals, "Unknown (%u)"), charset);
580         }
581         offset +=1;
582         /* 2 byte status */
583         status = tvb_get_letohs(tvb, offset);
584         if (tree) {
585                 proto_tree_add_uint_format(greeting_tree, hf_mysql_status, tvb,
586                         offset, 2, status, "Status: %s (%u)",
587                         val_to_str(status, mysql_status_vals, "Unknown (%u)"), status); 
588         }
589         offset +=2;
590         /* other unused */
591         strlen = tvb_length_remaining(tvb,offset);
592
593         if (tree) {
594                 proto_tree_add_item(greeting_tree, hf_mysql_unused, tvb,
595                         offset, strlen, FALSE );
596         }
597         offset +=strlen;
598         
599         return offset;
600 }
601
602 void
603 proto_register_mysql(void)
604 {
605   static hf_register_info hf[] = {
606     { &hf_mysql_packet_length,
607       { "Packet Length",              "mysql.packet_length",
608         FT_UINT24, BASE_DEC, NULL,  0x0,
609         "MySQL packet length", HFILL }},
610
611     { &hf_mysql_packet_number,
612       { "Packet Number",          "mysql.packet_number",
613         FT_UINT8, BASE_DEC, NULL, 0x0,
614         "MySQL Packet Number", HFILL }},
615
616     { &hf_mysql_opcode,
617       { "Command",        "mysql.opcode",
618         FT_UINT8, BASE_DEC, NULL, 0x0,
619         "MySQL OPCODE", HFILL }},
620
621     { &hf_mysql_response_code,
622       { "Response Code",          "mysql.response_code",
623         FT_UINT8, BASE_DEC, NULL, 0x0,
624         "MySQL Respone Code", HFILL }},
625
626     { &hf_mysql_error_code,
627       { "Error Code",     "mysql.error_code",
628         FT_UINT16, BASE_DEC, NULL, 0x0,
629         "MySQL Error CODE", HFILL }},
630
631     { &hf_mysql_protocol,
632       { "Protocol",       "mysql.protocol",
633         FT_UINT8, BASE_DEC, NULL, 0x0,
634         "MySQL Protocol", HFILL }},
635
636     { &hf_mysql_version,
637       { "Version",   "mysql.version",
638         FT_STRINGZ, BASE_DEC, NULL, 0x0,
639         "MySQL Version", HFILL }},
640
641     { &hf_mysql_caps,
642       { "Caps",   "mysql.caps",
643         FT_UINT16, BASE_DEC, NULL, 0x0,
644         "MySQL Capabilities", HFILL }},
645
646      { &hf_mysql_cap_long_password,
647         { "Long Password","mysql.caps.lp", 
648                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LP,
649                 "", HFILL }},
650                 
651      { &hf_mysql_cap_found_rows,
652         { "Found Rows","mysql.caps.fr", 
653                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_FR,
654                 "", HFILL }},
655
656                 
657      { &hf_mysql_cap_long_flag,
658         { "Long Flag","mysql.caps.lf", 
659                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LF,
660                 "", HFILL }},
661                 
662      { &hf_mysql_cap_connect_with_db,
663         { "Connect With Database","mysql.caps.cd", 
664                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CD,
665                 "", HFILL }},
666
667                 
668      { &hf_mysql_cap_no_schema,
669         { "Dont Allow database.table.column","mysql.caps.ns", 
670                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_NS,
671                 "", HFILL }},
672
673      { &hf_mysql_cap_compress,
674         { "Can use compression protocol","mysql.caps.CP", 
675                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CP,
676                 "", HFILL }},
677                 
678      { &hf_mysql_cap_odbc,
679         { "ODBC Client","mysql.caps.ob", 
680                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_OB,
681                 "", HFILL }},
682
683                 
684      { &hf_mysql_cap_local_files,
685         { "Can Use LOAD DATA LOCAL","mysql.caps.li", 
686                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_LI,
687                 "", HFILL }},
688                 
689      { &hf_mysql_cap_ignore_space,
690         { "Ignore Spaces before (","mysql.caps.is", 
691                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_IS,
692                 "", HFILL }},
693
694                 
695      { &hf_mysql_cap_change_user,
696         { "Support the mysql_change_user()","mysql.caps.cu", 
697                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_CU,
698                 "", HFILL }},
699
700                 
701      { &hf_mysql_cap_interactive,
702         { "an Interactive Client","mysql.caps.ia", 
703                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_IA,
704                 "", HFILL }},
705
706                 
707      { &hf_mysql_cap_ssl,
708         { "Switch to SSL after handshake","mysql.caps.sl", 
709                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_SL,
710                 "", HFILL }},
711
712      { &hf_mysql_cap_ignore_sigpipe,
713         { "Ignore sigpipes","mysql.caps.ii", 
714                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_II,
715                 "", HFILL }},
716                 
717      { &hf_mysql_cap_transactions,
718         { "Client knows about transactions","mysql.caps.ta", 
719                 FT_BOOLEAN, 16, TFS(&flags_set_truth), MYSQL_CAPS_TA,
720                 "", HFILL }},
721
722     { &hf_mysql_max_packet,
723       { "MAX Packet",         "mysql.max_packet",
724         FT_UINT24, BASE_DEC, NULL,  0x0,
725         "MySQL Max packet", HFILL }},
726                 
727     { &hf_mysql_user,
728       { "Username",           "mysql.user",
729         FT_STRINGZ, BASE_DEC, NULL, 0x0,
730         "Login Username", HFILL }}, 
731
732     { &hf_mysql_password,
733       { "Password",           "mysql.password",
734         FT_STRING, BASE_DEC, NULL, 0x0,
735         "Login Password", HFILL }}, 
736
737     { &hf_mysql_salt,
738       { "Salt",       "mysql.salt",
739         FT_STRINGZ, BASE_DEC, NULL, 0x0,
740         "Salt", HFILL }}, 
741
742     { &hf_mysql_thread_id,
743       { "Thread ID",          "mysql.thread_id",
744         FT_UINT32, BASE_DEC, NULL,  0x0,
745         "MySQL Thread ID", HFILL }},
746         
747     { &hf_mysql_charset,
748       { "Charset",            "mysql.charset",
749         FT_UINT8, BASE_DEC, NULL,  0x0,
750         "MySQL Charset", HFILL }},
751         
752     { &hf_mysql_status,
753       { "Status",             "mysql.status",
754         FT_UINT16, BASE_DEC, NULL,  0x0,
755         "MySQL Status", HFILL }},
756
757     { &hf_mysql_unused,
758       { "Unused",             "mysql.unused",
759         FT_STRING, BASE_DEC, NULL, 0x0,
760         "Unused", HFILL }},
761
762     { &hf_mysql_parameter,
763       { "Parameter",          "mysql.parameter",
764         FT_STRING, BASE_DEC, NULL, 0x0,
765         "Parameter", HFILL }},
766     { &hf_mysql_payload,
767       { "Payload",            "mysql.payload",
768         FT_STRING, BASE_DEC, NULL, 0x0,
769         "MySQL Payload", HFILL }},
770 #if 0
771     { &hf_mysql_destination_file,
772       { "DESTINATION File",   "mysql.destination_file",
773         FT_STRINGZ, BASE_DEC, NULL, 0x0,
774         "MySQL source file name", HFILL }},
775
776     { &hf_mysql_blocknum,
777       { "Block",              "mysql.block",
778         FT_UINT16, BASE_DEC, NULL, 0x0,
779         "Block number", HFILL }},
780
781     { &hf_mysql_error_code,
782       { "Error code",         "mysql.error.code",
783         FT_UINT16, BASE_DEC, VALS(mysql_error_code_vals), 0x0,
784         "Error code in case of MySQL error message", HFILL }},
785
786     { &hf_mysql_error_string,
787       { "Error message",      "mysql.error.message",
788         FT_STRINGZ, BASE_DEC, NULL, 0x0,
789         "Error string in case of MySQL error message", HFILL }},
790 #endif
791         };
792         static gint *ett[] = {
793                 &ett_mysql,
794                 &ett_server_greeting,
795                 &ett_caps,
796                 &ett_request,
797         };
798         module_t *mysql_module;
799
800         proto_mysql = proto_register_protocol("MySQL Protocol",
801                                        "MySQL", "mysql");
802         proto_register_field_array(proto_mysql, hf, array_length(hf));
803         proto_register_subtree_array(ett, array_length(ett));
804
805         mysql_module = prefs_register_protocol(proto_mysql, NULL);
806         prefs_register_bool_preference(mysql_module, "desegment_buffers",
807                 "Reassemble MySQL buffers spanning multiple TCP segments",
808                 "Whether the MySQL dissector should reassemble MySQL buffers spanning multiple TCP segments."
809                 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
810                 &mysql_desegment);
811 }
812
813 void
814 proto_reg_handoff_mysql(void)
815 {
816         dissector_handle_t mysql_handle;
817
818         mysql_handle = create_dissector_handle(dissect_mysql, proto_mysql);
819   
820         dissector_add("tcp.port", TCP_PORT_MySQL, mysql_handle);
821 }