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