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