Removed second include <epan/packet.h>.
[obnox/wireshark/wip.git] / epan / dissectors / packet-memcache.c
1 /* packet-memcache.c
2  * Routines for Memcache Binary Protocol
3  * http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol
4  *
5  * Copyright 2009, Stig Bjorlykke <stig@bjorlykke.org>
6  *
7  * Routines for Memcache Textual Protocol
8  * http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
9  *
10  * Copyright 2009, Rama Chitta <rama@gear6.com>
11  *
12  * $Id$
13  *
14  * Wireshark - Network traffic analyzer
15  * By Gerald Combs <gerald@wireshark.org>
16  * Copyright 1998 Gerald Combs
17  *
18  * This program is free software; you can redistribute it and/or
19  * modify it under the terms of the GNU General Public License
20  * as published by the Free Software Foundation; either version 2
21  * of the License, or (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program; if not, write to the Free Software
30  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <glib.h>
42
43 #include <epan/conversation.h>
44 #include <epan/packet.h>
45 #include <epan/strutil.h>
46 #include <epan/base64.h>
47 #include <epan/emem.h>
48 #include <epan/stats_tree.h>
49 #include <epan/req_resp_hdrs.h>
50 #include <epan/prefs.h>
51 #include <epan/expert.h>
52
53 #include "packet-tcp.h"
54
55 #define PNAME  "Memcache Protocol"
56 #define PSNAME "MEMCACHE"
57 #define PFNAME "memcache"
58
59 #define MEMCACHE_PORT         11211
60 #define MEMCACHE_HEADER_LEN   24
61
62 /* Magic Byte */
63 #define MAGIC_REQUEST         0x80
64 #define MAGIC_RESPONSE        0x81
65
66 /* Response Status */
67 #define RS_NO_ERROR           0x0000
68 #define RS_KEY_NOT_FOUND      0x0001
69 #define RS_KEY_EXISTS         0x0002
70 #define RS_VALUE_TOO_BIG      0x0003
71 #define RS_INVALID_ARGUMENTS  0x0004
72 #define RS_ITEM_NOT_STORED    0x0005
73 #define RS_UNKNOWN_COMMAND    0x0081
74 #define RS_OUT_OF_MEMORY      0x0082
75
76 /* Command Opcodes */
77 #define OP_GET                0x00
78 #define OP_SET                0x01
79 #define OP_ADD                0x02
80 #define OP_REPLACE            0x03
81 #define OP_DELETE             0x04
82 #define OP_INCREMENT          0x05
83 #define OP_DECREMENT          0x06
84 #define OP_QUIT               0x07
85 #define OP_FLUSH              0x08
86 #define OP_GET_Q              0x09
87 #define OP_NO_OP              0x0A
88 #define OP_VERSION            0x0B
89 #define OP_GET_K              0x0C
90 #define OP_GET_K_Q            0x0D
91 #define OP_APPEND             0x0E
92 #define OP_PREPEND            0x0F
93 #define OP_STAT               0x10
94 #define OP_SET_Q              0x11
95 #define OP_ADD_Q              0x12
96 #define OP_REPLACE_Q          0x13
97 #define OP_DELETE_Q           0x14
98 #define OP_INCREMENT_Q        0x15
99 #define OP_DECREMENT_Q        0x16
100 #define OP_QUIT_Q             0x17
101 #define OP_FLUSH_Q            0x18
102 #define OP_APPEND_Q           0x19
103 #define OP_PREPEND_Q          0x1A
104
105 /* Internally defined command opcodes used in the textual dissector only */
106 /* This values are not defined in any standard and can be redefined here */
107 #define OP_GETS               0xF0
108 #define OP_CAS                0xF1
109 #define OP_VERBOSE            0xF2
110
111 /* Data Types */
112 #define DT_RAW_BYTES          0x00
113
114 static int proto_memcache = -1;
115
116 static int hf_magic = -1;
117 static int hf_opcode = -1;
118 static int hf_extras_length = -1;
119 static int hf_key_length = -1;
120 static int hf_value_length = -1;
121 static int hf_data_type = -1;
122 static int hf_reserved = -1;
123 static int hf_status = -1;
124 static int hf_total_body_length = -1;
125 static int hf_opaque = -1;
126 static int hf_cas = -1;
127 static int hf_extras = -1;
128 static int hf_extras_flags = -1;
129 static int hf_extras_expiration = -1;
130 static int hf_extras_delta = -1;
131 static int hf_extras_initial = -1;
132 static int hf_extras_unknown = -1;
133 static int hf_extras_missing = -1;
134 static int hf_key = -1;
135 static int hf_key_missing = -1;
136 static int hf_value = -1;
137 static int hf_value_missing = -1;
138 static int hf_uint64_response = -1;
139
140 static int hf_command = -1;
141 static int hf_subcommand = -1;
142 static int hf_flags = -1;
143 static int hf_expiration = -1;
144 static int hf_noreply = -1;
145
146 static int hf_response = -1;
147
148 static int hf_version = -1;
149 static int hf_slabclass = -1;
150 static int hf_name = -1;
151 static int hf_name_value = -1;
152
153 static gint ett_memcache = -1;
154 static gint ett_extras = -1;
155
156 static const value_string magic_vals[] = {
157   { MAGIC_REQUEST,         "Request"            },
158   { MAGIC_RESPONSE,        "Response"           },
159   { 0, NULL }
160 };
161
162 static const value_string status_vals[] = {
163   { RS_NO_ERROR,           "No error"           },
164   { RS_KEY_NOT_FOUND,      "Key not found"      },
165   { RS_KEY_EXISTS,         "Key exists"         },
166   { RS_VALUE_TOO_BIG,      "Value too big"      },
167   { RS_INVALID_ARGUMENTS,  "Invalid arguments"  },
168   { RS_ITEM_NOT_STORED,    "Item not stored"    },
169   { RS_UNKNOWN_COMMAND,    "Unknown command"    },
170   { RS_OUT_OF_MEMORY,      "Out of memory"      },
171   { 0, NULL }
172 };
173
174 static const value_string opcode_vals[] = {
175   { OP_GET,                "Get"                },
176   { OP_SET,                "Set"                },
177   { OP_ADD,                "Add"                },
178   { OP_REPLACE,            "Replace"            },
179   { OP_DELETE,             "Delete"             },
180   { OP_INCREMENT,          "Increment"          },
181   { OP_DECREMENT,          "Decrement"          },
182   { OP_QUIT,               "Quit"               },
183   { OP_FLUSH,              "Flush"              },
184   { OP_GET_Q,              "Get Quietly"        },
185   { OP_NO_OP,              "No-op"              },
186   { OP_VERSION,            "Version"            },
187   { OP_GET_K,              "Get Key"            },
188   { OP_GET_K_Q,            "Get Key Quietly"    },
189   { OP_APPEND,             "Append"             },
190   { OP_PREPEND,            "Prepend"            },
191   { OP_STAT,               "Statistics"         },
192   { OP_SET_Q,              "Set Quietly"        },
193   { OP_ADD_Q,              "Add Quietly"        },
194   { OP_REPLACE_Q,          "Replace Quietly"    },
195   { OP_DELETE_Q,           "Delete Quietly"     },
196   { OP_INCREMENT_Q,        "Increment Quietly"  },
197   { OP_DECREMENT_Q,        "Decrement Quietly"  },
198   { OP_QUIT_Q,             "Quit Quietly"       },
199   { OP_FLUSH_Q,            "Flush Quietly"      },
200   { OP_APPEND_Q,           "Append Quietly"     },
201   { OP_PREPEND_Q,          "Prepend Quietly"    },
202   /* Internally defined values not valid here */
203   { 0, NULL }
204 };
205
206 static const value_string data_type_vals[] = {
207   { DT_RAW_BYTES,          "Raw bytes"          },
208   { 0, NULL }
209 };
210
211 /* memcache message types. */
212 typedef enum _memcache_type { 
213   MEMCACHE_REQUEST, 
214   MEMCACHE_RESPONSE,
215   MEMCACHE_UNKNOWN
216 } memcache_type_t;
217
218 /* desegmentation of MEMCACHE header */
219 static gboolean memcache_desegment_headers = TRUE;
220
221 /* desegmentation of MEMCACHE payload */
222 static gboolean memcache_desegment_body = TRUE;
223
224 /* should refer to either the request or the response dissector.
225  */
226 typedef int (*ReqRespDissector)(tvbuff_t*, packet_info *, proto_tree *, 
227                                 int, const guchar*, const guchar*, guint8);
228
229 /* determines if a packet contains a memcache
230  * request or reply by looking at its first token.
231  */
232 static int
233 is_memcache_request_or_reply(const gchar *data, int linelen, guint8 *opcode,
234                              memcache_type_t *type, int *expect_content_length, 
235                              ReqRespDissector *reqresp_dissector);
236
237 static guint 
238 get_memcache_pdu_len (packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
239 {
240   guint32 body_len;
241
242   /* Get the length of the memcache body */
243   body_len = tvb_get_ntohl(tvb, offset+8);
244
245   /* That length doesn't include the header; add that in */
246   return body_len + MEMCACHE_HEADER_LEN;
247 }
248
249 static void 
250 dissect_extras (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, 
251                 gint offset, guint8 extras_len, guint8 opcode, gboolean request)
252 {
253   proto_tree *extras_tree = NULL;
254   proto_item *extras_item = NULL, *ti;
255   gint        save_offset = offset;
256   gboolean    illegal = FALSE;  /* Set when extras shall not be present */
257   gboolean    missing = FALSE;  /* Set when extras is missing */
258
259   if (extras_len) {
260     extras_item = proto_tree_add_item (tree, hf_extras, tvb, offset, extras_len, FALSE);
261     extras_tree = proto_item_add_subtree (extras_item, ett_extras);
262   }
263
264   switch (opcode) {
265
266   case OP_GET:
267   case OP_GET_Q:
268   case OP_GET_K:
269   case OP_GET_K_Q:
270     if (extras_len) {
271       if (request) {
272         /* Request shall not have extras */
273         illegal = TRUE;
274       } else {
275         proto_tree_add_item (extras_tree, hf_extras_flags, tvb, offset, 4, FALSE);
276         offset += 4;
277       }
278     } else if (!request) {
279       /* Response must have extras */
280       missing = TRUE;
281     }
282     break;
283
284   case OP_SET:
285   case OP_SET_Q:
286   case OP_ADD:
287   case OP_ADD_Q:
288   case OP_REPLACE:
289   case OP_REPLACE_Q:
290     if (extras_len) {
291       if (request) {
292         proto_tree_add_item (extras_tree, hf_extras_flags, tvb, offset, 4, FALSE);
293         offset += 4;
294
295         proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, FALSE);
296         offset += 4;
297       } else {
298         /* Response shall not have extras */
299         illegal = TRUE;
300       }
301     } else if (request) {
302       /* Request must have extras */
303       missing = TRUE;
304     }
305     break;
306
307   case OP_INCREMENT:
308   case OP_INCREMENT_Q:
309   case OP_DECREMENT:
310   case OP_DECREMENT_Q:
311     if (extras_len) {
312       if (request) {
313         proto_tree_add_item (extras_tree, hf_extras_delta, tvb, offset, 8, FALSE);
314         offset += 8;
315
316         proto_tree_add_item (extras_tree, hf_extras_initial, tvb, offset, 8, FALSE);
317         offset += 8;
318
319         proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, FALSE);
320         offset += 4;
321       } else {
322         /* Response must not have extras (response is in Value) */
323         illegal = TRUE;
324       }
325     } else if (request) {
326       /* Request must have extras */
327       missing = TRUE;
328     }
329     break;
330
331   case OP_FLUSH:
332   case OP_FLUSH_Q:
333     if (extras_len) {
334       proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, FALSE);
335       offset += 4;
336     }
337     break;
338
339   case OP_DELETE:
340   case OP_DELETE_Q:
341   case OP_QUIT:
342   case OP_QUIT_Q:
343   case OP_VERSION:
344   case OP_APPEND:
345   case OP_APPEND_Q:
346   case OP_PREPEND:
347   case OP_PREPEND_Q:
348   case OP_STAT:
349     /* Must not have extras */
350     if (extras_len) {
351       illegal = TRUE;
352     }
353     break;
354
355   default:
356     if (extras_len) {
357       /* Decode as unknown extras */
358       proto_tree_add_item (extras_tree, hf_extras_unknown, tvb, offset, extras_len, FALSE);
359       offset += extras_len;
360     }
361     break;
362   }
363
364   if (illegal) {
365     ti = proto_tree_add_item (extras_tree, hf_extras_unknown, tvb, offset, extras_len, FALSE);
366     expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s shall not have Extras", 
367                             val_to_str (opcode, opcode_vals, "Opcode %d"),
368                             request ? "Request" : "Response");
369     offset += extras_len;
370   } else if (missing) {
371     ti = proto_tree_add_item (tree, hf_extras_missing, tvb, offset, 0, FALSE);
372     expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s must have Extras", 
373                             val_to_str (opcode, opcode_vals, "Opcode %d"),
374                             request ? "Request" : "Response");
375   }
376
377   if ((offset - save_offset) != extras_len) {
378     expert_add_info_format (pinfo, extras_item, PI_UNDECODED, PI_WARN, 
379                             "Illegal Extras length, should be %d", offset - save_offset);
380   }
381 }
382
383 static void 
384 dissect_key (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, 
385              gint offset, int key_len, guint8 opcode, gboolean request)
386 {
387   proto_item *ti = NULL;
388   gboolean    illegal = FALSE;  /* Set when key shall not be present */
389   gboolean    missing = FALSE;  /* Set when key is missing */
390
391   if (key_len) {
392     ti = proto_tree_add_item (tree, hf_key, tvb, offset, key_len, FALSE);
393     offset += key_len;
394   }
395
396   /* Sanity check */
397   if (key_len) {
398     if ((opcode == OP_QUIT) || (opcode == OP_QUIT_Q) || (opcode == OP_NO_OP) || (opcode == OP_VERSION)) {
399       /* Request and Response must not have key */
400       illegal = TRUE;
401     }
402     if ((opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) || (opcode == OP_DELETE) ||
403         (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) || (opcode == OP_DELETE_Q) ||
404         (opcode == OP_FLUSH) || (opcode == OP_APPEND) || (opcode == OP_PREPEND) ||
405         (opcode == OP_FLUSH_Q) || (opcode == OP_APPEND_Q) || (opcode == OP_PREPEND_Q))
406     {
407       /* Response must not have a key */
408       if (!request) {
409         illegal = TRUE;
410       }
411     }
412   } else {
413     if ((opcode == OP_GET) || (opcode == OP_GET_Q) || (opcode == OP_GET_K) || (opcode == OP_GET_K_Q) ||
414         (opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) || (opcode == OP_DELETE) ||
415         (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) || (opcode == OP_DELETE_Q) ||
416         (opcode == OP_INCREMENT) || (opcode == OP_DECREMENT) || (opcode == OP_INCREMENT_Q) || (opcode == OP_DECREMENT_Q))
417     {
418       /* Request must have key */
419       if (request) {
420         missing = TRUE;
421       }
422     }
423   }
424
425   if (illegal) {
426     expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s shall not have Key", 
427                             val_to_str (opcode, opcode_vals, "Opcode %d"),
428                             request ? "Request" : "Response");
429   } else if (missing) {
430     ti = proto_tree_add_item (tree, hf_key_missing, tvb, offset, 0, FALSE);
431     expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s must have Key",
432                             val_to_str (opcode, opcode_vals, "Opcode %d"),
433                             request ? "Request" : "Response");
434   }
435 }
436
437 static void 
438 dissect_value (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, 
439                gint offset, guint32 value_len, guint8 opcode, gboolean request)
440 {
441   proto_item *ti = NULL;
442   gboolean    illegal = FALSE;  /* Set when value shall not be present */
443   gboolean    missing = FALSE;  /* Set when value is missing */
444
445   if (value_len > 0) {
446     if (!request && ((opcode == OP_INCREMENT) || (opcode == OP_DECREMENT))) {
447       ti = proto_tree_add_item (tree, hf_uint64_response, tvb, offset, 8, FALSE);
448       if (value_len != 8) {
449         expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "Illegal Value length, should be 8");
450       }
451     } else {
452       ti = proto_tree_add_item (tree, hf_value, tvb, offset, value_len, FALSE);
453     }
454     offset += value_len;
455   }
456
457   /* Sanity check */
458   if (value_len) {
459     if ((opcode == OP_GET) || (opcode == OP_GET_Q) || (opcode == OP_GET_K) || (opcode == OP_GET_K_Q) || 
460         (opcode == OP_INCREMENT) || (opcode == OP_DECREMENT) || (opcode == OP_VERSION) ||
461         (opcode == OP_INCREMENT_Q) || (opcode == OP_DECREMENT_Q))
462     {
463       /* Request must not have value */
464       if (request) {
465         illegal = TRUE;
466       }
467     }
468     if ((opcode == OP_DELETE) ||  (opcode == OP_QUIT) || (opcode == OP_FLUSH) || (opcode == OP_NO_OP) ||
469         (opcode == OP_DELETE_Q) ||  (opcode == OP_QUIT_Q) || (opcode == OP_FLUSH_Q))
470     {
471       /* Request and Response must not have value */
472       illegal = TRUE;
473     }
474     if ((opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) ||
475         (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) ||
476         (opcode == OP_APPEND) || (opcode == OP_PREPEND) || (opcode == OP_APPEND_Q) || (opcode == OP_PREPEND_Q))
477     {
478       /* Response must not have value */
479       if (!request) {
480         illegal = TRUE;
481       }
482     }
483   } else {
484     if ((opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) ||
485         (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) ||
486         (opcode == OP_APPEND) || (opcode == OP_PREPEND) || (opcode == OP_APPEND_Q) || (opcode == OP_PREPEND_Q))
487     {
488       /* Request must have a value */
489       if (request) {
490         missing = TRUE;
491       }
492     }
493   }
494
495   if (illegal) {
496     expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s shall not have Value", 
497                             val_to_str (opcode, opcode_vals, "Opcode %d"),
498                             request ? "Request" : "Response");
499   } else if (missing) {
500     ti = proto_tree_add_item (tree, hf_value_missing, tvb, offset, 0, FALSE);
501     expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s must have Value",
502                             val_to_str (opcode, opcode_vals, "Opcode %d"),
503                             request ? "Request" : "Response"); 
504   }
505 }
506
507 static void 
508 dissect_memcache (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
509 {
510   proto_tree *memcache_tree;
511   proto_item *memcache_item, *ti;
512   gint        offset = 0;
513   guint8      magic, opcode, extras_len;
514   guint16     key_len, status = 0;
515   guint32     body_len, value_len;
516   gboolean    request;
517
518   col_set_str (pinfo->cinfo, COL_PROTOCOL, PSNAME);
519   col_clear (pinfo->cinfo, COL_INFO);
520
521   memcache_item = proto_tree_add_item (tree, proto_memcache, tvb, offset, -1, FALSE);
522   memcache_tree = proto_item_add_subtree (memcache_item, ett_memcache);
523
524   magic = tvb_get_guint8 (tvb, offset);
525   ti = proto_tree_add_item (memcache_tree, hf_magic, tvb, offset, 1, FALSE);
526   offset += 1;
527
528   if (match_strval (magic, magic_vals) == NULL) {
529     expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "Unknown magic byte: %d", magic);
530   }
531
532   opcode = tvb_get_guint8 (tvb, offset);
533   ti = proto_tree_add_item (memcache_tree, hf_opcode, tvb, offset, 1, FALSE);
534   offset += 1;
535
536   if (match_strval (opcode, opcode_vals) == NULL) {
537     expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "Unknown opcode: %d", opcode);
538   }
539
540   proto_item_append_text (memcache_item, ", %s %s", val_to_str (opcode, opcode_vals, "Unknown opcode (%d)"),
541                           val_to_str (magic, magic_vals, "Unknown magic (%d)"));
542
543   col_append_fstr (pinfo->cinfo, COL_INFO, "%s %s", 
544                    val_to_str (opcode, opcode_vals, "Unknown opcode (%d)"),
545                    val_to_str (magic, magic_vals, "Unknown magic (%d)"));
546
547   key_len = tvb_get_ntohs (tvb, offset);
548   proto_tree_add_item (memcache_tree, hf_key_length, tvb, offset, 2, FALSE);
549   offset += 2;
550
551   extras_len = tvb_get_guint8 (tvb, offset);
552   proto_tree_add_item (memcache_tree, hf_extras_length, tvb, offset, 1, FALSE);
553   offset += 1;
554
555   proto_tree_add_item (memcache_tree, hf_data_type, tvb, offset, 1, FALSE);
556   offset += 1;
557
558   status = tvb_get_ntohs (tvb, offset);
559   if (magic & 0x01) {    /* We suppose this is a response, even when unknown magic byte */
560     request = FALSE;
561     ti = proto_tree_add_item (memcache_tree, hf_status, tvb, offset, 2, FALSE);
562     if (status != 0) {
563       expert_add_info_format (pinfo, ti, PI_RESPONSE_CODE, PI_NOTE, "%s: %s", 
564                               val_to_str (opcode, opcode_vals, "Unknown opcode (%d)"),
565                               val_to_str (status, status_vals, "Status: %d"));
566     }
567   } else {
568     request = TRUE;
569     ti = proto_tree_add_item (memcache_tree, hf_reserved, tvb, offset, 2, FALSE);
570     if (status != 0) {
571       expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "Reserved value: %d", status);
572     }
573   }
574   offset += 2;
575
576   body_len = tvb_get_ntohl (tvb, offset);
577   value_len = body_len - extras_len - key_len;
578   ti = proto_tree_add_uint (memcache_tree, hf_value_length, tvb, offset, 0, value_len);
579   PROTO_ITEM_SET_GENERATED (ti);
580
581   proto_tree_add_item (memcache_tree, hf_total_body_length, tvb, offset, 4, FALSE);
582   offset += 4;
583
584   proto_tree_add_item (memcache_tree, hf_opaque, tvb, offset, 4, FALSE);
585   offset += 4;
586
587   proto_tree_add_item (memcache_tree, hf_cas, tvb, offset, 8, FALSE);
588   offset += 8;
589
590   if (status == 0) {
591     dissect_extras (tvb, pinfo, memcache_tree, offset, extras_len, opcode, request);
592     offset += extras_len;
593
594     dissect_key (tvb, pinfo, memcache_tree, offset, key_len, opcode, request);
595     offset += key_len;
596
597     dissect_value (tvb, pinfo, memcache_tree, offset, value_len, opcode, request);
598     offset += value_len;
599   } else if (body_len) {
600     proto_tree_add_item (memcache_tree, hf_value, tvb, offset, body_len, FALSE);
601     offset += body_len;
602         
603     col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)", 
604                      val_to_str (status, status_vals, "Unknown status: %d"));
605   } else {
606     ti = proto_tree_add_item (memcache_tree, hf_value_missing, tvb, offset, 0, FALSE);
607     expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s with status %s (%d) must have Value",
608                             val_to_str (opcode, opcode_vals, "Opcode %d"),
609                             val_to_str (status, status_vals, "Unknown"), status);
610   }
611 }
612
613 /* Obtain the content length by peeping into the header.
614  */
615 static gboolean 
616 get_payload_length (tvbuff_t *tvb, const int token_number, int offset, 
617                     guint32 *bytes, gboolean *content_length_found)
618 {
619   const guchar *next_token;
620   const guchar *line, *lineend;
621   guchar       *bytes_val;
622   int           tokenlen, i = 0, linelen;
623   gint          next_offset;
624
625   /* get the header line. */
626   linelen = tvb_find_line_end (tvb, offset,
627                                tvb_ensure_length_remaining (tvb, offset), &next_offset,
628                                FALSE);
629   if (linelen < 0) {
630     return FALSE;
631   }
632
633   line = tvb_get_ptr (tvb, offset, linelen);
634   lineend = line + linelen;
635
636   while (++i < token_number) {
637     tokenlen = get_token_len (line, lineend, &next_token);
638     if (tokenlen == 0) {
639       return FALSE;
640     }
641     offset += (int) (next_token - line);
642     line = next_token;
643   }
644
645   /* line or the next_token has the value we want. */
646   tokenlen = get_token_len (line, lineend, &next_token);
647   if (tokenlen == 0)  {
648     return FALSE;
649   }
650
651   bytes_val = tvb_get_ephemeral_string (tvb, offset, tokenlen);
652   if (bytes_val) {
653     if (sscanf (bytes_val, "%u", bytes) == 1) {
654       *content_length_found = TRUE;
655     } else {
656       return FALSE;
657     }
658   } else {
659     return FALSE;
660   }
661
662   /* reached this far, we got what we want. */
663   return TRUE;
664 }
665
666 /* check if a PDU needs to be desegmented. */
667 static gboolean
668 desegment_pdus (tvbuff_t *tvb, packet_info *pinfo, const int offset,
669                 const int data_offset, guint32 content_length)
670 {
671   gint length_remaining, reported_length_remaining;
672
673   /* data_offset has been set to start of the data block. */
674   if (!tvb_bytes_exist (tvb, data_offset, content_length)) { 
675
676     length_remaining = tvb_length_remaining (tvb, data_offset);
677     reported_length_remaining = tvb_reported_length_remaining (tvb, data_offset);
678
679     if (length_remaining < reported_length_remaining) { 
680       /* It's a waste of time asking for more
681        * data, because that data wasn't captured.
682        */
683       return FALSE;
684     }
685
686     if (length_remaining == -1) { 
687       length_remaining = 0;
688     }
689
690     pinfo->desegment_offset = offset; /* start of the packet. */
691     pinfo->desegment_len = (content_length + 2) - length_remaining; /* add 2 for /r/n */
692
693     return TRUE;
694   }
695   return FALSE;
696 }
697
698 /*
699  * Optionally do reassembly of the requests, responses and data.
700  */
701 static gboolean
702 memcache_req_resp_hdrs_do_reassembly (
703     tvbuff_t *tvb, const int offset, packet_info *pinfo, 
704     const gboolean desegment_headers, const gboolean desegment_body, 
705     const memcache_type_t type, const int expect_content_length)
706 {
707   int       linelen;
708   gint      next_offset;
709   gint      length_remaining;
710   gint      reported_length_remaining;
711   guint32   content_length          = 0;
712   gboolean  content_length_found    = FALSE;
713   gboolean  ret                     = FALSE;
714
715   /*
716    * If header desegmentation is activated, check the
717    * header in this tvbuff.
718    * request one more byte (we don't know how many bytes 
719    * we'll need, so we just ask for one).
720    */
721   if (desegment_headers && pinfo->can_desegment) { 
722     next_offset = offset;
723
724     reported_length_remaining = tvb_reported_length_remaining (tvb, next_offset);
725     /*
726      * Request one more byte if there're no
727      * bytes left in the reported data (if there're
728      * bytes left in the reported data, but not in
729      * the available data, requesting more bytes
730      * won't help, as those bytes weren't captured).
731      */
732     if (reported_length_remaining < 1) { 
733       pinfo->desegment_offset = offset;
734       pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
735       return FALSE;
736     }
737
738     length_remaining = tvb_length_remaining (tvb, next_offset);
739
740     /* Request one more byte if we cannot find a 
741      * header (i.e. a line end).
742      */
743     linelen = tvb_find_line_end (tvb, next_offset, -1, &next_offset, TRUE);
744     if (linelen == -1 && length_remaining >= reported_length_remaining) { 
745       /* Not enough data; ask for one more byte. */
746       pinfo->desegment_offset = offset;
747       pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
748       return FALSE; 
749     }
750
751     /* Browse through the header to find the content length.
752      *
753      * request:
754      * <command name> <key> <flags> <exptime> <bytes> [noreply]\r\n
755      * cas <key> <flags> <exptime> <bytes> <cas unqiue> [noreply]\r\n
756      *
757      * response:
758      * VALUE <key> <flags> <bytes> [<cas unique>]\r\n 
759      * <data block>\r\n
760      */ 
761     if (expect_content_length == TRUE) { 
762       switch (type) {
763
764       case MEMCACHE_REQUEST:
765         /* Get the fifth token in the header.*/
766         ret = get_payload_length (tvb, 5 , offset, &content_length, &content_length_found);
767         if (!ret) { 
768           return FALSE; 
769         }
770         break;
771
772       case MEMCACHE_RESPONSE:
773         /* Get the fourth token in the header.*/
774         ret =  get_payload_length (tvb, 4 , offset, &content_length, &content_length_found);
775         if (!ret) { 
776           return FALSE; 
777         } 
778         break;
779
780       default:
781         /* Unrecognized message type. */
782         return FALSE; 
783       }
784     }
785   }
786
787   /* We have reached the end of a header, so there 
788    * should be 'content_length' bytes after this 
789    * followed by CRLF. The next_offset points to the 
790    * start of the data bytes. 
791    */
792   if (desegment_body && content_length_found) {
793     return !desegment_pdus (tvb, pinfo, offset, next_offset, content_length);
794   }
795
796   /* No further desegmentation needed. */
797   return TRUE;
798 }
799
800 /* Dissect a memcache message. */
801 static int
802 dissect_memcache_message (tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
803 {
804   const guchar      *line;
805   const guchar      *lineend;
806   int                orig_offset;
807   int                first_linelen;
808   int                datalen;
809   int                expect_content_length = FALSE;
810   gint               next_offset;
811
812   gboolean           is_request_or_reply;
813   memcache_type_t    memcache_type;
814   ReqRespDissector   reqresp_dissector  = NULL;
815   proto_tree        *memcache_tree      = NULL;
816   proto_item        *memcache_item      = NULL;
817   guint8             opcode = 0xff; /* set to something that is not in the list. */
818
819   /* Find a line end in the packet.
820    * Note that "tvb_find_line_end ()" will return a value that
821    * is not longer than what's in the buffer, so the
822    * "tvb_get_ptr ()" call won't throw an exception.
823    */
824   first_linelen = tvb_find_line_end (tvb, offset,
825                                      tvb_ensure_length_remaining (tvb, offset), &next_offset,
826                                      FALSE);
827   if (first_linelen < 0) { 
828     return -1;
829   }
830
831   line = tvb_get_ptr (tvb, offset, first_linelen);
832   lineend = line + first_linelen;
833
834   memcache_type = MEMCACHE_UNKNOWN; /* packet type not known yet */
835
836   /* Look at the first token of the first line to 
837    * determine if it is a request or a response? 
838    */
839   is_request_or_reply = 
840     is_memcache_request_or_reply ((const gchar *)line, 
841                                   first_linelen, &opcode, &memcache_type, 
842                                   &expect_content_length, &reqresp_dissector);
843   if (is_request_or_reply) {
844
845     /* Yes, it is a request or a response.
846      * Do header and body desegmentation if we've been told to.
847      */
848     if (!memcache_req_resp_hdrs_do_reassembly (tvb, offset, pinfo, memcache_desegment_headers, 
849                                                memcache_desegment_body, memcache_type, 
850                                                expect_content_length))
851     {
852       /* More data needed for desegmentation. */
853       return -1;
854     }
855   }
856
857   /* Columns and summary display. */ 
858   col_set_str (pinfo->cinfo, COL_PROTOCOL, PSNAME);
859
860   /* If the packet is a memcache request or reply,
861    * put the first line from the buffer into the summary
862    * Otherwise, just call it a continuation.
863    */
864   if (is_request_or_reply) {
865     line = tvb_get_ptr (tvb, offset, first_linelen);
866     col_add_fstr (pinfo->cinfo, COL_INFO, "%s ", 
867                  format_text (line, first_linelen));
868   } else {
869     col_set_str (pinfo->cinfo, COL_INFO, "MEMCACHE Continuation");
870   }
871
872   orig_offset = offset;
873
874   memcache_item = proto_tree_add_item (tree, proto_memcache, tvb, offset, -1, FALSE);
875   memcache_tree = proto_item_add_subtree (memcache_item, ett_memcache);
876
877   /* Process the packet data. The first line is expected to be a 
878    * header. If its not a header then we don't dissect.
879    * At this point, we already know if it is a request or a 
880    * response.
881    */
882   if (tvb_reported_length_remaining (tvb, offset) != 0) {
883     /* Dissect a request or a response. */
884     if (is_request_or_reply && reqresp_dissector) {
885       if (tree) {
886         next_offset = reqresp_dissector (tvb, pinfo, memcache_tree, 
887                                          offset, line, lineend, opcode);
888         if (next_offset == -1) { 
889           /* Error in dissecting. */
890           return -1;
891         }
892         offset = next_offset;
893       }
894     }
895   }
896
897   /*
898    * If a 'bytes' value was supplied, the amount of data to be
899    * processed as MEMCACHE payload is the minimum of the 'bytes'
900    * value and the amount of data remaining in the frame.
901    *
902    */
903   datalen = tvb_length_remaining (tvb, offset);
904   if (datalen > 0) {
905     /*
906      * We've processed "datalen" bytes worth of data
907      * (which may be no data at all); advance the
908      * offset past whatever data we've processed.
909      */
910     offset += datalen;
911   }
912
913   return offset - orig_offset;
914 }
915
916 /* Payload dissector 
917  * <data block>\r\n
918  */ 
919 static int 
920 content_data_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, 
921                        int content_length, guint8 opcode)
922 {
923   gint          datalen;
924   int           len;
925   gboolean      short_pkt = FALSE;
926
927   /* 
928    * Expecting to read 'content_length' number of bytes from 
929    * the buffer. It is not necessary that we have all the 
930    * content_length bytes available to read.
931    */
932   if ((len = tvb_reported_length_remaining (tvb, offset) != 0)) {
933     /* bytes actually remaining in this tvbuff. */
934     datalen = tvb_length_remaining (tvb, offset);
935     if (content_length >= 0) {
936       if (datalen >= (content_length + 2)) { /* also consider \r\n*/
937         datalen = content_length;
938       } else {
939         short_pkt = TRUE;
940       }
941     }
942
943     /* dissect the data block. */
944     dissect_value (tvb, pinfo, tree, offset, datalen, opcode, TRUE);
945     if (datalen > 0) {
946       /*
947        * We've processed "datalen" bytes worth of data
948        * (which may be no data at all); advance the
949        * offset past whatever data we've processed.
950        */
951       if (!short_pkt) {
952         offset += (datalen + 2); /* go past /r/n*/
953       } else {
954         offset += datalen; /* short packet; no /r/n*/
955       }
956     }
957   }
958
959   return offset;
960 }
961
962 /* Find the occurrences of a ':' in a stat response. */
963 static guint
964 find_stat_colon (const guchar *line, const guchar *lineend, 
965                  const guchar **first_colon, const guchar **last_colon)
966 {
967   const guchar *linep, *temp;
968   guint         occurrences = 0;
969   guchar        c;
970
971   linep = line;
972   while (linep < lineend) { 
973     temp = linep;
974     c = *linep++;
975
976     switch (c) {
977     case ':':
978       occurrences++;
979       if (occurrences == 1) {
980         *first_colon = temp;
981       } else if (occurrences == 2) {
982         *last_colon = temp;
983       } else {
984         /* anything other than 1 or 2; 
985          * return immediately 
986          */
987         return occurrences;
988       }
989       break;
990     default:
991       break;
992     }
993   }
994
995   return occurrences;
996 }
997
998 /* incr/decr response dissector */
999 static int
1000 incr_dissector (tvbuff_t *tvb, proto_tree *tree, int offset)
1001 {
1002   gint           next_offset;
1003   int            linelen, len;
1004   const guchar  *line, *lineend;
1005
1006   const guchar  *next_token;
1007   int            tokenlen;
1008
1009   /* expecting to read 'bytes' number of bytes from the buffer. */
1010   if ((len = tvb_reported_length_remaining (tvb, offset)) != 0) {
1011     /* Find the end of the line. */
1012     linelen = tvb_find_line_end (tvb, offset,
1013                                  tvb_ensure_length_remaining (tvb, offset), &next_offset,
1014                                  FALSE);
1015     if (linelen < 0) {
1016       /* header is out of the packet limits. */
1017       return -1;
1018     }
1019
1020     /*
1021      * Get a buffer that refers to the line.
1022      * in other words, the unstructured portion 
1023      * of memcache.
1024      */
1025     line = tvb_get_ptr (tvb, offset, linelen);
1026     lineend = line + linelen;
1027  
1028     /* 64 bit value */
1029     tokenlen = get_token_len (line, lineend, &next_token);
1030     if (tokenlen == 0) {
1031       return -1;
1032     }
1033
1034     proto_tree_add_item (tree, hf_uint64_response, tvb, offset, tokenlen, FALSE);
1035     offset += (int) (next_token - line);
1036     line = next_token;
1037
1038     /* CRLF */
1039     tokenlen = get_token_len (line, lineend, &next_token);
1040     if (tokenlen == 0) {
1041       return next_offset;
1042     } else {
1043       return -1; /* invalid token */
1044     }
1045   }
1046
1047   return offset;
1048 }
1049
1050 /* stats response dissector */
1051 static int
1052 stat_dissector (tvbuff_t *tvb, proto_tree *tree, int offset)
1053 {
1054   guint         occurrences = 0;
1055   const guchar *first_colon = NULL, *last_colon = NULL;
1056   int           tokenlen, linelen;
1057   gint          next_offset;
1058   const guchar *next_token;
1059   const guchar *line, *lineend;
1060   int           reported_datalen = -1;
1061   guint32       slabclass;
1062   guchar        response_chars[21];
1063
1064   while ((reported_datalen = tvb_reported_length_remaining (tvb, offset)) != 0) {
1065     /* Find the end of the line. */
1066     linelen = tvb_find_line_end (tvb, offset,
1067                                  tvb_ensure_length_remaining (tvb, offset), &next_offset,
1068                                  FALSE);
1069     if (linelen < 0) {
1070       return -1;
1071     }
1072
1073     /*
1074      * Get a buffer that refers to the line.
1075      */
1076     line = tvb_get_ptr (tvb, offset, linelen);
1077     lineend = line + linelen;
1078
1079     tokenlen = get_token_len (line, lineend, &next_token);
1080     if ((tokenlen == 4) && strncmp (line, "STAT", tokenlen) == 0) {
1081       proto_tree_add_item (tree, hf_command, tvb, offset, tokenlen, FALSE);
1082       offset += (int) (next_token - line);
1083       line = next_token;
1084       occurrences = find_stat_colon (line, lineend, &first_colon, &last_colon);
1085     } else if ((tokenlen == 3) && strncmp (line, "END", tokenlen) == 0) {
1086       /* done. reached an end of response. */
1087       offset += (int) (next_token - line);
1088       return offset;
1089     } else {
1090       /* invalid token */
1091       return -1;
1092     }
1093
1094     switch (occurrences) {
1095     case 2: /* stats items: 2 colons */
1096       /* subcommand 'items' */
1097       tokenlen = (int) (first_colon - line);
1098       proto_tree_add_item (tree, hf_subcommand, tvb, offset, 
1099                           tokenlen, FALSE);
1100       offset += tokenlen + 1;
1101
1102       /* slabclass */
1103       tokenlen = (int) (last_colon - first_colon - 1);
1104       if (tokenlen > 10 || tokenlen <= 0) {
1105         return -1;
1106       }
1107       memcpy (response_chars, first_colon + 1, tokenlen);
1108       response_chars[tokenlen] = '\0';
1109
1110       slabclass = (guint32) strtoul (response_chars, NULL, 10);
1111       proto_tree_add_uint (tree, hf_slabclass, tvb, offset, tokenlen, slabclass);
1112       offset += tokenlen + 1;
1113       line = last_colon + 1;
1114       break;
1115
1116     case 1: /* stats slabs: 1 colon */
1117       tokenlen = (int) (first_colon - line);
1118       if (tokenlen > 10 || tokenlen <= 0) {
1119         return -1;
1120       }
1121       memcpy (response_chars, line, tokenlen);
1122       response_chars[tokenlen] = '\0';
1123
1124       slabclass = (guint32) strtoul (response_chars, NULL, 10);
1125       proto_tree_add_uint (tree, hf_slabclass, tvb, offset, tokenlen, slabclass);
1126
1127       offset += (int) (tokenlen + 1);
1128       line = first_colon + 1;
1129       break;
1130
1131     case 0: /* stats: 0 colons */
1132       break;
1133
1134     default:
1135       /* invalid token. */
1136       return -1;
1137     }
1138
1139     /* <hf_name> <hf_name_value>\r\n */
1140     tokenlen = get_token_len (line, lineend, &next_token);
1141     if (tokenlen == 0) {
1142       return -1; /* invalid token */
1143     }
1144
1145     proto_tree_add_item (tree, hf_name, tvb, offset, tokenlen, FALSE);
1146     offset += (int) (next_token - line);
1147     line = next_token;
1148
1149     /* value */
1150     tokenlen = get_token_len (line, lineend, &next_token);
1151     if (tokenlen == 0) {
1152       return -1; /* invalid token */
1153     }
1154     proto_tree_add_item (tree, hf_name_value, tvb, offset, tokenlen, FALSE);
1155     offset += (int) (next_token - line);
1156     line = next_token;
1157
1158     offset = next_offset;
1159     occurrences = 0;
1160   }
1161
1162   return offset;
1163 }
1164
1165 /* get/gets response dissector */
1166 static int
1167 get_response_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1168 {
1169   gint           next_offset;
1170   int            linelen, len;
1171   const guchar  *line, *lineend;
1172   const guchar  *next_token;
1173   int            tokenlen;
1174   guint16        flags;
1175   guint32        bytes;
1176   guint64        cas;
1177   guint8         opcode = 0xff;
1178   gchar          response_chars[21]; /* cover uint64 (20 + 1) bytes*/
1179
1180   /* expecting to read 'bytes' number of bytes from the buffer. */
1181   while ((len = tvb_reported_length_remaining (tvb, offset)) != 0) {
1182     /* Find the end of the line. */
1183     linelen = tvb_find_line_end (tvb, offset,
1184                                  tvb_ensure_length_remaining (tvb, offset), &next_offset,
1185                                  FALSE);
1186     if (linelen < 0) {
1187       /* header is out of the packet limits. */
1188       return -1;
1189     }
1190
1191     /*
1192      * Get a buffer that refers to the line.
1193      * in other words, the unstructured portion 
1194      * of memcache.
1195      */
1196     line = tvb_get_ptr (tvb, offset, linelen);
1197     lineend = line + linelen;
1198  
1199     /* VALUE token  */
1200     tokenlen = get_token_len (line, lineend, &next_token);
1201     if (tokenlen == 0) {
1202       /* error */
1203       return -1;
1204     }
1205
1206     if ((tokenlen == 5) && strncmp (line, "VALUE", tokenlen) == 0) {
1207       /* proceed */
1208     } else if ((tokenlen == 3) && strncmp (line, "END", tokenlen) == 0) {
1209       /* done. reached an end of response. */
1210       offset += (int) (next_token - line);
1211       return offset;
1212     } else {
1213       /* invalid token */
1214       return -1;
1215     }
1216
1217     offset += (int) (next_token - line);
1218     line = next_token;
1219
1220     /* key */
1221     tokenlen = get_token_len (line, lineend, &next_token);
1222     if (tokenlen == 0) { 
1223       return -1;
1224     }
1225     dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, TRUE);
1226     offset += (int) (next_token - line);
1227     line = next_token;
1228
1229     /* flags */
1230     tokenlen = get_token_len (line, lineend, &next_token);
1231     if (tokenlen == 0 || tokenlen > 5) {
1232       return -1;
1233     }
1234     memcpy (response_chars, line, tokenlen);
1235     response_chars[tokenlen] = '\0';
1236
1237     flags = (guint16) strtoul (response_chars, NULL, 10);
1238     proto_tree_add_uint (tree, hf_flags, tvb, offset, tokenlen, flags);
1239
1240     offset += (int) (next_token - line);
1241     line = next_token;
1242      
1243     /* bytes */
1244     tokenlen = get_token_len (line, lineend, &next_token);
1245     if (tokenlen == 0 || tokenlen > 10) {
1246       return -1;
1247     }
1248     memcpy (response_chars, line, tokenlen);
1249     response_chars[tokenlen] = '\0';
1250
1251     bytes = (guint32) strtoul (response_chars, NULL, 10);
1252     proto_tree_add_uint (tree, hf_value_length, tvb, offset, tokenlen, bytes);
1253
1254     offset += (int) (next_token - line);
1255     line = next_token;
1256
1257     /* check if cas id is present */
1258     tokenlen = get_token_len (line, lineend, &next_token);
1259     if (tokenlen > 20) {
1260       return -1;
1261     }
1262
1263     if (tokenlen != 0) {  /* reached the end of line; CRLF */
1264       memcpy (response_chars, line, tokenlen);
1265       response_chars[tokenlen] = '\0';
1266
1267       cas = (guint64) strtoul (response_chars, NULL, 10);
1268       proto_tree_add_uint64 (tree, hf_cas, tvb, offset, tokenlen, cas);
1269
1270       offset += (int) (next_token - line);
1271       line = next_token;
1272
1273       /* CRLF */
1274       tokenlen = get_token_len (line, lineend, &next_token);
1275       if (tokenlen != 0) {
1276         return -1; /* invalid token */
1277       }
1278     }
1279
1280     offset = next_offset;
1281     /* <datablock>\r\n */
1282     offset = content_data_dissector (tvb, pinfo, tree, offset, bytes, opcode); 
1283     if (offset == -1) {
1284       return offset;
1285     }
1286   }
1287
1288   return offset;
1289 }
1290
1291 /* Basic memcache response dissector. */
1292 static int 
1293 memcache_response_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
1294                              const guchar *line, const guchar *lineend, guint8 opcode)
1295 {
1296   const guchar *next_token;
1297   int           tokenlen;
1298
1299   switch (opcode) {
1300
1301   case OP_GET:
1302   case OP_GETS:
1303     return get_response_dissector (tvb, pinfo, tree, offset);
1304
1305   case OP_VERSION:
1306     /* response code.  */
1307     tokenlen = get_token_len (line, lineend, &next_token);
1308     if (tokenlen == 0) {
1309       return -1;
1310     }   
1311     if ((tokenlen == 7) && strncmp (line, "VERSION", tokenlen) == 0) {
1312       offset += (int) (next_token - line);
1313       line = next_token;
1314     } else {
1315       return -1;
1316     }
1317
1318     /* version string */
1319     tokenlen = get_token_len (line, lineend, &next_token);
1320     if (tokenlen == 0) {
1321       /* expecting version string. */
1322       return -1;
1323     }
1324
1325     proto_tree_add_item (tree, hf_version, tvb, offset, tokenlen, FALSE);
1326     offset += (int) (next_token - line);
1327     line = next_token;
1328
1329     /* CRLF */
1330     tokenlen = get_token_len (line, lineend, &next_token);
1331     if (tokenlen != 0) {
1332       /* invalid token */
1333       return -1;
1334     }
1335
1336     return offset;
1337
1338   case OP_STAT:
1339     return stat_dissector (tvb, tree, offset);
1340
1341   default:
1342     break;
1343   }
1344
1345   /* response code.  */
1346   tokenlen = get_token_len (line, lineend, &next_token);
1347   if (tokenlen == 0) {
1348     return -1;
1349   }
1350
1351   /* all the following mark an end of a response.
1352    * should take care of set, add, cas, append, replace
1353    * prepend, flush_all, verbosity, delete and to an extent 
1354    * incr, decr and stat commands.
1355    */
1356   if ((tokenlen == 6 && strncmp (line, "STORED", tokenlen) == 0) ||
1357       (tokenlen == 10 && strncmp (line, "NOT_STORED", tokenlen) == 0) ||
1358       (tokenlen == 6 && strncmp (line, "EXISTS", tokenlen) == 0) ||
1359       (tokenlen == 9 && strncmp (line, "NOT_FOUND", tokenlen) == 0) ||
1360       (tokenlen == 7 && strncmp (line, "DELETED", tokenlen) == 0) ||
1361       (tokenlen == 2 && strncmp (line, "OK", tokenlen) == 0) ||
1362       (tokenlen == 3 && strncmp (line, "END", tokenlen) == 0)) 
1363   {
1364     proto_tree_add_item (tree, hf_response, tvb, offset, tokenlen, FALSE);
1365     offset += (int) (next_token - line);
1366     line = next_token;
1367     return offset;
1368   }
1369
1370   /* if we have reached this point:
1371    * it is either an incr/decr response of the format 
1372    *  <value>\r\n. 
1373    *  or
1374    *  "stats sizes" response of the format:
1375    *  <size> <count> \r\n
1376    */
1377   if (opcode == OP_INCREMENT) {
1378     return incr_dissector (tvb, tree, offset);
1379   }
1380
1381   return offset;
1382 }
1383
1384 /* Basic memcache request dissector. */
1385 static int 
1386 memcache_request_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
1387                             const guchar *line, const guchar *lineend, guint8 opcode)
1388 {
1389   const guchar *next_token;
1390   int           tokenlen;
1391
1392   guint16       flags;
1393   guint32       expiration;
1394   guint32       bytes;
1395   guint64       cas;
1396   gchar         response_chars[21]; /* cover uint64 (20 + 1) bytes*/
1397
1398   /* command. */
1399   tokenlen = get_token_len (line, lineend, &next_token);
1400   if (tokenlen == 0) {
1401     return -1;
1402   }
1403   proto_tree_add_item (tree, hf_command, tvb, offset, tokenlen, FALSE);
1404   offset += (int) (next_token - line);
1405   line = next_token;
1406
1407   switch (opcode) {
1408
1409   case OP_SET:
1410   case OP_ADD:
1411   case OP_REPLACE:
1412   case OP_APPEND:
1413   case OP_PREPEND:
1414   case OP_CAS:
1415
1416     /* key */
1417     tokenlen = get_token_len (line, lineend, &next_token);
1418     if (tokenlen == 0) { 
1419       return -1;
1420     }
1421
1422     dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, TRUE);
1423     offset += (int) (next_token - line);
1424     line = next_token;
1425
1426     /* flags */
1427     tokenlen = get_token_len (line, lineend, &next_token);
1428     if (tokenlen == 0 || tokenlen > 5) {
1429       return -1;
1430     }
1431     memcpy (response_chars, line, tokenlen);
1432     response_chars[tokenlen] = '\0';
1433
1434     flags = (guint16) strtoul (response_chars, NULL, 10);
1435     proto_tree_add_uint (tree, hf_flags, tvb, offset, tokenlen, flags);
1436
1437     offset += (int) (next_token - line);
1438     line = next_token;
1439      
1440     /* expiration */ 
1441     tokenlen = get_token_len (line, lineend, &next_token);
1442     if (tokenlen == 0 || tokenlen > 10) {
1443       return -1;
1444     }
1445     memcpy (response_chars, line, tokenlen);
1446     response_chars[tokenlen] = '\0';
1447
1448     expiration = (guint32) strtoul (response_chars, NULL, 10);
1449     proto_tree_add_uint (tree, hf_expiration, tvb, offset, tokenlen, expiration);
1450
1451     offset += (int) (next_token - line);
1452     line = next_token;
1453
1454     /* bytes */
1455     tokenlen = get_token_len (line, lineend, &next_token);
1456     if (tokenlen == 0 || tokenlen > 10) {
1457       return -1;
1458     }
1459     memcpy (response_chars, line, tokenlen);
1460     response_chars[tokenlen] = '\0';
1461
1462     bytes = (guint32) strtoul (response_chars, NULL, 10);
1463     proto_tree_add_uint (tree, hf_value_length, tvb, offset, tokenlen, bytes);
1464
1465     offset += (int) (next_token - line);
1466     line = next_token;
1467
1468     /* cas id. */
1469     if (opcode == OP_CAS) {
1470       tokenlen = get_token_len (line, lineend, &next_token);
1471       if (tokenlen == 0 || tokenlen > 20) {
1472         return -1;
1473       }
1474       memcpy (response_chars, line, tokenlen);
1475       response_chars[tokenlen] = '\0';
1476
1477       cas = (guint64) strtoul (response_chars, NULL, 10);
1478       proto_tree_add_uint64 (tree, hf_cas, tvb, offset, tokenlen, cas);
1479
1480       offset += (int) (next_token - line);
1481       line = next_token;
1482     }
1483
1484     /* check if the following bit is "noreply" or
1485      * the actual data block. 
1486      */
1487     tokenlen = get_token_len (line, lineend, &next_token);
1488     if (tokenlen != 0) {
1489       if (tokenlen == 7 && strncmp (line, "noreply", 7) == 0) { 
1490         proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, FALSE);
1491       }
1492       offset += (int) (next_token - line);
1493       line = next_token;
1494     }
1495
1496     offset += 2 ; /* go past /r/n*/
1497     /* <datablock>\r\n */
1498     offset = content_data_dissector (tvb, pinfo, tree, offset, bytes, opcode); 
1499     if (offset == -1) {
1500       return offset;
1501     }
1502     break;
1503
1504   case OP_INCREMENT:
1505   case OP_DECREMENT:
1506     /* key */
1507     tokenlen = get_token_len (line, lineend, &next_token);
1508     if (tokenlen == 0) { 
1509       return -1;
1510     }
1511     dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, TRUE);
1512     offset += (int) (next_token - line);
1513     line = next_token;
1514
1515     /* value */
1516     tokenlen = get_token_len (line, lineend, &next_token);
1517     if (tokenlen == 0) {
1518       return -1;
1519     }
1520     proto_tree_add_item (tree, hf_value, tvb, offset, tokenlen, FALSE);
1521     offset += (int) (next_token - line);
1522     line = next_token;
1523
1524     /* check for "noreply" */
1525     tokenlen = get_token_len (line, lineend, &next_token);
1526     if (tokenlen == 0) {
1527       return offset; /* reached CRLF */
1528     }
1529     if (tokenlen == 7 && strncmp (line, "noreply", 7) == 0) {
1530       proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, FALSE);
1531       offset += (int) (next_token - line);
1532       line = next_token;
1533     } else {
1534       return -1; /* should have been noreply or CRLF. */
1535     }
1536
1537     /* CRLF */
1538     tokenlen = get_token_len (line, lineend, &next_token);
1539     if (tokenlen == 0) {
1540       return offset; /* CRLF */
1541     } else {
1542       /*something's wrong; invalid command maybe. */
1543       return -1;
1544     }
1545     break;
1546
1547   case OP_DELETE:
1548     /* key */
1549     tokenlen = get_token_len (line, lineend, &next_token);
1550     if (tokenlen == 0) { 
1551       return -1;
1552     }
1553     /* dissect key. */
1554     dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, TRUE);
1555     offset += (int) (next_token - line);
1556     line = next_token;
1557
1558     /* check if its expiration or noreply */
1559     tokenlen = get_token_len (line, lineend, &next_token);
1560     if (tokenlen == 0) {
1561       return offset; /* neither expiration nor noreply; CRLF */
1562     }
1563     if (tokenlen <= 10) {
1564       if (tokenlen == 7 && strncmp (line, "noreply", 7) == 0) {
1565         /* noreply */
1566         proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, FALSE);
1567       } else {
1568         /* expiration */
1569         memcpy (response_chars, line, tokenlen);
1570         response_chars[tokenlen] = '\0';
1571
1572         expiration = (guint32) strtoul (response_chars, NULL, 10);
1573         proto_tree_add_uint (tree, hf_expiration, tvb, offset, tokenlen, expiration);
1574       }
1575       offset += (int) (next_token - line);
1576       line = next_token;
1577     } else {
1578       return -1;
1579     }
1580
1581     /* CRLF */
1582     tokenlen = get_token_len (line, lineend, &next_token);
1583     if (tokenlen == 0) {
1584       return offset;
1585     } else {
1586       /*something's wrong; invalid command maybe. */
1587       return -1;
1588     }
1589     break;
1590
1591   case OP_GET:
1592   case OP_GETS:
1593     /* could be followed by any number of keys, add
1594      * them one by one. tokenlen cannot be 0 to begin
1595      * with.
1596      */
1597     while (tokenlen != 0) {
1598       tokenlen = get_token_len (line, lineend, &next_token);
1599       if (tokenlen == 0) {
1600         return offset; /* CRLF */
1601       }
1602       dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, TRUE);
1603       offset += (int) (next_token - line);
1604       line = next_token;
1605     }
1606     break;
1607
1608   case OP_STAT:
1609     tokenlen = get_token_len (line, lineend, &next_token);
1610     if (tokenlen == 0) { /* just the 'stats' command;*/
1611       return offset;
1612     } else { /* there is a sub command; record it*/
1613       proto_tree_add_item (tree, hf_subcommand, tvb, offset, tokenlen, FALSE);
1614       offset += (int) (next_token - line);
1615       line = next_token;
1616     }
1617
1618     /* CRLF */
1619     tokenlen = get_token_len (line, lineend, &next_token);
1620     if (tokenlen == 0) {
1621       return offset;
1622     } else {
1623       /* something's wrong; invalid command maybe. */
1624       return -1;
1625     }
1626     break;
1627
1628   case OP_FLUSH:
1629     /* check if its expiration or noreply */
1630     tokenlen = get_token_len (line, lineend, &next_token);
1631     if (tokenlen == 0) {
1632       return offset; /* neither expiration nor noreply; CRLF */
1633     }
1634     if (tokenlen <= 10) {
1635       if (tokenlen == 7 && strncmp (line, "noreply", 7) == 0) {
1636         /* noreply */
1637         proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, FALSE);
1638       } else {
1639         /* expiration */
1640         memcpy (response_chars, line, tokenlen);
1641         response_chars[tokenlen] = '\0';
1642
1643         expiration = (guint32) strtoul (response_chars, NULL, 10);
1644         proto_tree_add_uint (tree, hf_expiration, tvb, offset, tokenlen, expiration);
1645       }
1646       offset += (int) (next_token - line);
1647       line = next_token;
1648     } else {
1649       return -1;
1650     }
1651
1652     /* maybe noreply now? */
1653     tokenlen = get_token_len (line, lineend, &next_token);
1654     if (tokenlen == 0) {
1655       return offset;
1656     } 
1657     if (tokenlen == 7 && strncmp (line, "noreply", 7) == 0) { 
1658       /* noreply */
1659       proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, FALSE);
1660       offset += (int) (next_token - line);
1661       line = next_token;
1662     } else {
1663       return -1; /* expecting CRLF and if not noreply*/
1664     }
1665     break;
1666
1667   case OP_VERBOSE:
1668     /* not implemented for now.*/
1669     break;
1670
1671   case OP_VERSION:
1672   case OP_QUIT:
1673     /* CRLF */
1674     tokenlen = get_token_len (line, lineend, &next_token);
1675     if (tokenlen == 0) {
1676       return offset;
1677     } else {
1678       /*something's wrong; invalid command maybe. */
1679       return -1;
1680     }
1681
1682   default:
1683     /* invalid command maybe; break out. */
1684     break;
1685   }
1686
1687   return offset;
1688 }
1689
1690 /*
1691  * any message that is not starting with the following keywords
1692  * is a response.
1693  */
1694 static int
1695 is_memcache_request_or_reply (const gchar *data, int linelen, guint8 *opcode,
1696                              memcache_type_t *type, int *expect_content_length, 
1697                              ReqRespDissector *reqresp_dissector)
1698 {
1699   const guchar *ptr = (const guchar *)data;
1700   int           is_request_or_response = FALSE;
1701   int           indx = 0;
1702
1703   /* look for a space */
1704   while (indx < linelen) {
1705     if (*ptr == ' ')
1706       break;
1707
1708     ptr++;
1709     indx++;
1710   }
1711
1712   /* is it a response? */
1713   switch (indx) {
1714   case 2:
1715     if (strncmp (data, "OK", indx) == 0) {
1716       *type = MEMCACHE_RESPONSE;
1717       is_request_or_response = TRUE;
1718     }
1719     break;
1720
1721   case 3:
1722     if (strncmp (data, "END", indx) == 0) {
1723       *type = MEMCACHE_RESPONSE;
1724       is_request_or_response = TRUE;
1725     }
1726     break;
1727
1728   case 4:
1729     if (strncmp (data, "STAT", indx) == 0) {
1730       *opcode = OP_STAT;
1731       *type = MEMCACHE_RESPONSE;
1732       is_request_or_response = TRUE;
1733     }
1734     break;
1735
1736   case 5:
1737     if (strncmp (data, "VALUE", indx) == 0) {
1738       *opcode = OP_GET;
1739       *type = MEMCACHE_RESPONSE;
1740       *expect_content_length = TRUE;
1741       is_request_or_response = TRUE;
1742     }
1743     break;
1744
1745   case 6:
1746     if (strncmp (data, "EXISTS", indx) == 0 ||
1747         strncmp (data, "STORED", indx) == 0) {
1748       *type = MEMCACHE_RESPONSE;
1749       is_request_or_response = TRUE;
1750     }
1751     break;
1752
1753   case 7:
1754     if (strncmp (data, "VERSION", indx) == 0) { 
1755       *opcode = OP_VERSION;
1756       *type = MEMCACHE_RESPONSE;
1757       is_request_or_response = TRUE;
1758     } else if (strncmp (data, "DELETED", indx) == 0) {
1759       *opcode = OP_DELETE;
1760       *type = MEMCACHE_RESPONSE;
1761       is_request_or_response = TRUE;
1762     }
1763     break;
1764
1765   case 9:
1766     if (strncmp (data, "NOT_FOUND", indx) == 0) {
1767       *type = MEMCACHE_RESPONSE;
1768       is_request_or_response = TRUE;
1769     }
1770     break;
1771
1772   case 10:
1773     if (strncmp (data, "NOT_STORED", indx) == 0) {
1774       *type = MEMCACHE_RESPONSE;
1775       is_request_or_response = TRUE;
1776     }
1777     break;
1778
1779   default:
1780     break; /* is it a request? */
1781   }
1782
1783   if (is_request_or_response && reqresp_dissector) {
1784     *reqresp_dissector = memcache_response_dissector;
1785     return is_request_or_response;
1786   }
1787
1788   /* is it a request?  */
1789   switch (indx) { 
1790   case 3: 
1791     if (strncmp (data, "get", indx) == 0) {
1792       *opcode = OP_GET;
1793       *type = MEMCACHE_REQUEST; 
1794       is_request_or_response = TRUE;
1795     } else if (strncmp (data, "set", indx) == 0) {
1796       *opcode = OP_SET;
1797       *type = MEMCACHE_REQUEST; 
1798       *expect_content_length = TRUE;
1799       is_request_or_response = TRUE;
1800     } else if (strncmp (data, "add", indx) == 0) {
1801       *opcode = OP_ADD;
1802       *type = MEMCACHE_REQUEST; 
1803       *expect_content_length = TRUE;
1804       is_request_or_response = TRUE;
1805     } else if (strncmp (data, "cas", indx) == 0) {
1806       *opcode = OP_CAS;
1807       *type = MEMCACHE_REQUEST; 
1808       *expect_content_length = TRUE;
1809       is_request_or_response = TRUE;
1810     }
1811     break;
1812
1813   case 4: 
1814     if (strncmp (data, "gets", indx) == 0) {
1815       *opcode = OP_GETS;
1816       *type = MEMCACHE_REQUEST; 
1817       is_request_or_response = TRUE;
1818     } else if (strncmp (data, "incr", indx) == 0) {
1819       *opcode = OP_INCREMENT;
1820       *type = MEMCACHE_REQUEST; 
1821       is_request_or_response = TRUE;
1822     } else if (strncmp (data, "decr", indx) == 0) {
1823       *opcode = OP_DECREMENT;
1824       *type = MEMCACHE_REQUEST; 
1825       is_request_or_response = TRUE;
1826     } else if (strncmp (data, "quit", indx) == 0) {
1827       *opcode = OP_QUIT;
1828       *type = MEMCACHE_REQUEST; 
1829       is_request_or_response = TRUE;
1830     }
1831     break;
1832
1833   case 5:
1834     if (strncmp (data, "stats", indx) == 0) {
1835       *opcode = OP_STAT;
1836       *type = MEMCACHE_REQUEST;
1837       is_request_or_response = TRUE;
1838     }
1839     break;
1840
1841   case 6: 
1842     if (strncmp (data, "append", indx) == 0) {
1843       *opcode = OP_APPEND;
1844       *type = MEMCACHE_REQUEST; 
1845       *expect_content_length = TRUE;
1846       is_request_or_response = TRUE;
1847     } else if (strncmp (data, "delete", indx) == 0) {
1848       *opcode = OP_DELETE;
1849       *type = MEMCACHE_REQUEST; 
1850       is_request_or_response = TRUE;
1851     }
1852     break;
1853
1854   case 7: 
1855     if (strncmp (data, "replace", indx) == 0) {
1856       *opcode = OP_REPLACE;
1857       *type = MEMCACHE_REQUEST;
1858       *expect_content_length = TRUE;
1859       is_request_or_response = TRUE;
1860     } else if (strncmp (data, "prepend", indx) == 0) {
1861       *opcode = OP_PREPEND;
1862       *type = MEMCACHE_REQUEST;
1863       *expect_content_length = TRUE;
1864       is_request_or_response = TRUE;
1865     } else if (strncmp (data, "version", indx) == 0) {
1866       *opcode = OP_VERSION;
1867       *type = MEMCACHE_REQUEST;
1868       is_request_or_response = TRUE;
1869     }
1870     break;
1871
1872   case 9: 
1873     if (strncmp (data, "flush_all", indx) == 0) {
1874       *opcode = OP_FLUSH;
1875       *type = MEMCACHE_REQUEST;
1876       is_request_or_response = TRUE;
1877     }
1878     break;
1879
1880   default: 
1881     break; /* check if it is an 'incr' or 'stats sizes' response. */
1882   }
1883
1884   if (is_request_or_response && reqresp_dissector) {
1885     *reqresp_dissector = memcache_request_dissector;
1886     return is_request_or_response;
1887   }
1888
1889   /* XXX:
1890    * Recognize 'incr', 'decr' and 'stats sizes' responses.
1891    * I don't have a solution for this yet.
1892    */
1893   return is_request_or_response;
1894 }
1895
1896 /* dissect memcache textual protocol PDUs. */
1897 static void
1898 dissect_memcache_text (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1899 {
1900   int   offset = 0;
1901   int   len; 
1902
1903   while (tvb_reported_length_remaining (tvb, offset) != 0) { 
1904
1905     /* dissect the memcache packet. */
1906     len = dissect_memcache_message (tvb, offset, pinfo, tree);
1907     if (len == -1)
1908       break;
1909     offset += len;
1910
1911     /*
1912      * OK, we've set the Protocol and Info columns for the
1913      * first MEMCACHE message; set a fence so that subsequent
1914      * MEMCACHE messages don't overwrite the Info column.
1915      */
1916     col_set_fence (pinfo->cinfo, COL_INFO);
1917   }
1918 }
1919
1920 /* Dissect tcp packets based on the type of protocol (text/binary) */
1921 static void 
1922 dissect_memcache_tcp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1923
1924   gint        offset = 0;
1925   guint8      magic;
1926
1927   magic = tvb_get_guint8 (tvb, offset);
1928
1929   if (match_strval (magic, magic_vals) != NULL) { 
1930     tcp_dissect_pdus (tvb, pinfo, tree, memcache_desegment_body, 12,
1931                       get_memcache_pdu_len, dissect_memcache);
1932   } else {
1933     dissect_memcache_text (tvb, pinfo, tree);
1934   }
1935 }
1936
1937 /* Dissect udp packets based on the type of protocol (text/binary) */
1938 static void 
1939 dissect_memcache_udp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1940 {
1941   gint        offset = 0;
1942   guint8      magic;
1943
1944   magic = tvb_get_guint8 (tvb, offset);
1945
1946   if (match_strval (magic, magic_vals) != NULL) { 
1947     dissect_memcache (tvb, pinfo, tree);
1948   } else {
1949     dissect_memcache_message (tvb, 0, pinfo, tree);
1950   }
1951 }
1952
1953 /* Registration functions; register memcache protocol, 
1954  * its configuration options and also register the tcp and udp
1955  * dissectors.
1956  */
1957 void 
1958 proto_register_memcache (void)
1959
1960   static hf_register_info hf[] = { 
1961     { &hf_magic,
1962       { "Magic", "memcache.magic", 
1963         FT_UINT8, BASE_DEC, VALS (magic_vals), 0x0, 
1964         "Magic number", HFILL } },
1965
1966     { &hf_opcode,
1967       { "Opcode", "memcache.opcode", 
1968         FT_UINT8, BASE_DEC, VALS (opcode_vals), 0x0, 
1969         "Command code", HFILL } },
1970
1971     { &hf_extras_length,
1972       { "Extras length", "memcache.extras.length", 
1973         FT_UINT8, BASE_DEC, NULL, 0x0, 
1974         "Length in bytes of the command extras", HFILL } },
1975
1976     { &hf_key_length,
1977       { "Key Length", "memcache.key.length", 
1978         FT_UINT16, BASE_DEC, NULL, 0x0, 
1979         "Length in bytes of the text key that follows the command extras", HFILL } },
1980
1981     { &hf_value_length,
1982       { "Value length", "memcache.value.length", 
1983         FT_UINT32, BASE_DEC, NULL, 0x0, 
1984         "Length in bytes of the value that follows the key", HFILL } },
1985
1986     { &hf_data_type,
1987       { "Data type", "memcache.data_type", 
1988         FT_UINT8, BASE_DEC, VALS (data_type_vals), 0x0, 
1989         NULL, HFILL } },
1990
1991     { &hf_reserved,
1992       { "Reserved", "memcache.reserved", 
1993         FT_UINT16, BASE_DEC, NULL, 0x0, 
1994         "Reserved for future use", HFILL } },
1995
1996     { &hf_status,
1997       { "Status", "memcache.status", 
1998         FT_UINT16, BASE_DEC, VALS (status_vals), 0x0, 
1999         "Status of the response", HFILL } },
2000
2001     { &hf_total_body_length,
2002       { "Total body length", "memcache.total_body_length", 
2003         FT_UINT32, BASE_DEC, NULL, 0x0, 
2004         "Length in bytes of extra + key + value", HFILL } },
2005
2006     { &hf_opaque,
2007       { "Opaque", "memcache.opaque", 
2008         FT_UINT32, BASE_DEC, NULL, 0x0, 
2009         NULL, HFILL } },
2010
2011     { &hf_cas,
2012       { "CAS", "memcache.cas", 
2013         FT_UINT64, BASE_DEC, NULL, 0x0, 
2014         "Data version check", HFILL } },
2015
2016     { &hf_extras,
2017       { "Extras", "memcache.extras", 
2018         FT_NONE, BASE_NONE, NULL, 0x0, 
2019         NULL, HFILL } },
2020
2021     { &hf_extras_flags,
2022       { "Flags", "memcache.extras.flags", 
2023         FT_UINT32, BASE_HEX, NULL, 0x0, 
2024         NULL, HFILL } },
2025
2026     { &hf_extras_expiration,
2027       { "Expiration", "memcache.extras.expiration", 
2028         FT_UINT32, BASE_DEC, NULL, 0x0, 
2029         NULL, HFILL } },
2030
2031     { &hf_extras_delta,
2032       { "Amount to add", "memcache.extras.delta", 
2033         FT_UINT64, BASE_DEC, NULL, 0x0, 
2034         NULL, HFILL } },
2035
2036     { &hf_extras_initial,
2037       { "Initial value", "memcache.extras.initial", 
2038         FT_UINT64, BASE_DEC, NULL, 0x0, 
2039         NULL, HFILL } },
2040
2041     { &hf_extras_unknown,
2042       { "Unknown", "memcache.extras.unknown", 
2043         FT_BYTES, BASE_NONE, NULL, 0x0, 
2044         "Unknown Extras", HFILL } },
2045
2046     { &hf_extras_missing,
2047       { "Extras missing", "memcache.extras.missing", 
2048         FT_NONE, BASE_NONE, NULL, 0x0, 
2049         "Extras is mandatory for this command", HFILL } },
2050
2051     { &hf_key,
2052       { "Key", "memcache.key", 
2053         FT_STRING, BASE_NONE, NULL, 0x0, 
2054         NULL, HFILL } },
2055
2056     { &hf_key_missing,
2057       { "Key missing", "memcache.key.missing", 
2058         FT_NONE, BASE_NONE, NULL, 0x0, 
2059         "Key is mandatory for this command", HFILL } },
2060
2061     { &hf_value,
2062       { "Value", "memcache.value", 
2063         FT_STRING, BASE_NONE, NULL, 0x0, 
2064         NULL, HFILL } },
2065
2066     { &hf_value_missing,
2067       { "Value missing", "memcache.value.missing", 
2068         FT_NONE, BASE_NONE, NULL, 0x0, 
2069         "Value is mandatory for this command", HFILL } },
2070
2071     { &hf_uint64_response, 
2072       { "Response", "memcache.extras.response", 
2073         FT_UINT64, BASE_DEC, NULL, 0x0, 
2074         NULL, HFILL } },
2075
2076     { &hf_command,
2077       { "Command", "memcache.command", 
2078         FT_STRING, BASE_NONE , NULL, 0x0, 
2079         NULL, HFILL } },
2080
2081     { &hf_subcommand,
2082       { "Sub command", "memcache.subcommand", 
2083         FT_STRING, BASE_NONE, NULL, 0x0, 
2084         "Sub command if any", HFILL } },
2085
2086     { &hf_flags,
2087       { "Flags", "memcache.flags", 
2088         FT_UINT16, BASE_DEC, NULL, 0x0, 
2089         NULL, HFILL } },
2090
2091     { &hf_expiration,
2092       { "Expiration", "memcache.expiration", 
2093         FT_UINT32, BASE_DEC, NULL, 0x0, 
2094         NULL, HFILL } },
2095
2096     { &hf_noreply,
2097       { "Noreply", "memcache.noreply", 
2098         FT_STRING, BASE_NONE, NULL, 0x0, 
2099         "Client does not expect a reply", HFILL } },
2100
2101     { &hf_response,
2102       { "Response", "memcache.response", 
2103         FT_STRING, BASE_NONE, NULL, 0x0, 
2104         "Response command", HFILL } },
2105
2106     { &hf_version,
2107       { "Version", "memcache.version", 
2108         FT_STRING, BASE_NONE, NULL, 0x0, 
2109         "Version of running memcache", HFILL } },
2110
2111     { &hf_slabclass,
2112       { "Slab class", "memcache.slabclass", 
2113         FT_UINT32, BASE_DEC, NULL, 0x0, 
2114         "Slab class of a stat", HFILL } },
2115
2116     { &hf_name,
2117       { "Stat name", "memcache.name", 
2118         FT_STRING, BASE_NONE, NULL, 0x0, 
2119         "Name of a stat", HFILL } },
2120
2121     { &hf_name_value,
2122       { "Stat value", "memcache.name_value", 
2123         FT_STRING, BASE_NONE, NULL, 0x0, 
2124         "Value of a stat", HFILL } },
2125   };
2126
2127   static gint *ett[] = {
2128     &ett_memcache,
2129     &ett_extras
2130   };
2131
2132   module_t *memcache_module;
2133
2134   proto_memcache = proto_register_protocol (PNAME, PSNAME, PFNAME);
2135   register_dissector ("memcache.tcp", dissect_memcache_tcp, proto_memcache);
2136   register_dissector ("memcache.udp", dissect_memcache_udp, proto_memcache);
2137   
2138   proto_register_field_array (proto_memcache, hf, array_length (hf));
2139   proto_register_subtree_array (ett, array_length (ett));
2140
2141   /* Register our configuration options */
2142   memcache_module = prefs_register_protocol (proto_memcache, NULL);
2143
2144   prefs_register_bool_preference (memcache_module, "desegment_headers", 
2145                                  "Reassemble MEMCACHE headers spanning multiple TCP segments",
2146                                  "Whether the MEMCACHE dissector should reassemble headers "
2147                                  "of a request spanning multiple TCP segments. "
2148                                  "To use this option, you must also enable "
2149                                  "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.", 
2150                                  &memcache_desegment_headers);
2151
2152   prefs_register_bool_preference (memcache_module, "desegment_pdus", 
2153                                   "Reassemble PDUs spanning multiple TCP segments",
2154                                   "Whether the memcache dissector should reassemble PDUs"
2155                                   " spanning multiple TCP segments."
2156                                   " To use this option, you must also enable \"Allow subdissectors"
2157                                   " to reassemble TCP streams\" in the TCP protocol settings.",
2158                                   &memcache_desegment_body);
2159 }
2160
2161 /* Register the tcp and udp memcache dissectors. */
2162 void 
2163 proto_reg_handoff_memcache (void)
2164 {
2165   dissector_handle_t memcache_tcp_handle;
2166   dissector_handle_t memcache_udp_handle;
2167
2168   memcache_tcp_handle = find_dissector ("memcache.tcp");
2169   memcache_udp_handle = find_dissector ("memcache.udp");
2170
2171   dissector_add ("tcp.port", MEMCACHE_PORT, memcache_tcp_handle);
2172   dissector_add ("udp.port", MEMCACHE_PORT, memcache_udp_handle);
2173 }
2174
2175 /*
2176  * Editor modelines
2177  *
2178  * Local Variables:
2179  * c-basic-offset: 2
2180  * tab-width: 2
2181  * indent-tabs-mode: nil
2182  * End:
2183  *
2184  * ex: set shiftwidth=2 tabstop=2 expandtab
2185  * :indentSize=2:tabSize=2:noTabs=true:
2186  */