aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-memcache.c
diff options
context:
space:
mode:
authorStig Bjørlykke <stig@bjorlykke.org>2009-03-03 21:57:43 +0000
committerStig Bjørlykke <stig@bjorlykke.org>2009-03-03 21:57:43 +0000
commitdce5b2bc42f8d1517d835a3563e87d98e9d361de (patch)
tree1d0fd27020919dd82a28a36147a5b638072a9020 /epan/dissectors/packet-memcache.c
parentc84b8b1c4bc26de15e6665c14a7e53d3124e807b (diff)
Added support for Memcache Binary Protocol.
svn path=/trunk/; revision=27594
Diffstat (limited to 'epan/dissectors/packet-memcache.c')
-rw-r--r--epan/dissectors/packet-memcache.c674
1 files changed, 674 insertions, 0 deletions
diff --git a/epan/dissectors/packet-memcache.c b/epan/dissectors/packet-memcache.c
new file mode 100644
index 0000000000..be1b390b49
--- /dev/null
+++ b/epan/dissectors/packet-memcache.c
@@ -0,0 +1,674 @@
+/* packet-memcache.c
+ * Routines for Memcache Binary Protocol
+ * http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol
+ *
+ * Copyright 2009, Stig Bjørlykke <stig@bjorlykke.org>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/expert.h>
+
+#include "packet-tcp.h"
+
+#define PNAME "Memcache Binary Protocol"
+#define PSNAME "MEMCACHE"
+#define PFNAME "memcache"
+
+#define MEMCACHE_PORT 11211
+#define MEMCACHE_HEADER_LEN 24
+
+/* Magic Byte */
+#define MAGIC_REQUEST 0x80
+#define MAGIC_RESPONSE 0x81
+
+/* Response Status */
+#define RS_NO_ERROR 0x0000
+#define RS_KEY_NOT_FOUND 0x0001
+#define RS_KEY_EXISTS 0x0002
+#define RS_VALUE_TOO_BIG 0x0003
+#define RS_INVALID_ARGUMENTS 0x0004
+#define RS_ITEM_NOT_STORED 0x0005
+#define RS_UNKNOWN_COMMAND 0x0081
+#define RS_OUT_OF_MEMORY 0x0082
+
+/* Command Opcodes */
+#define OP_GET 0x00
+#define OP_SET 0x01
+#define OP_ADD 0x02
+#define OP_REPLACE 0x03
+#define OP_DELETE 0x04
+#define OP_INCREMENT 0x05
+#define OP_DECREMENT 0x06
+#define OP_QUIT 0x07
+#define OP_FLUSH 0x08
+#define OP_GET_Q 0x09
+#define OP_NO_OP 0x0A
+#define OP_VERSION 0x0B
+#define OP_GET_K 0x0C
+#define OP_GET_K_Q 0x0D
+#define OP_APPEND 0x0E
+#define OP_PREPEND 0x0F
+#define OP_STAT 0x10
+#define OP_SET_Q 0x11
+#define OP_ADD_Q 0x12
+#define OP_REPLACE_Q 0x13
+#define OP_DELETE_Q 0x14
+#define OP_INCREMENT_Q 0x15
+#define OP_DECREMENT_Q 0x16
+#define OP_QUIT_Q 0x17
+#define OP_FLUSH_Q 0x18
+#define OP_APPEND_Q 0x19
+#define OP_PREPEND_Q 0x1A
+
+/* Data Types */
+#define DT_RAW_BYTES 0x00
+
+void proto_reg_handoff_memcache (void);
+
+static int proto_memcache = -1;
+
+static int hf_magic = -1;
+static int hf_opcode = -1;
+static int hf_extras_length = -1;
+static int hf_key_length = -1;
+static int hf_value_length = -1;
+static int hf_data_type = -1;
+static int hf_reserved = -1;
+static int hf_status = -1;
+static int hf_total_body_length = -1;
+static int hf_opaque = -1;
+static int hf_cas = -1;
+static int hf_extras = -1;
+static int hf_extras_flags = -1;
+static int hf_extras_expiration = -1;
+static int hf_extras_delta = -1;
+static int hf_extras_initial = -1;
+static int hf_extras_unknown = -1;
+static int hf_extras_missing = -1;
+static int hf_key = -1;
+static int hf_key_missing = -1;
+static int hf_value = -1;
+static int hf_value_missing = -1;
+static int hf_uint64_response = -1;
+
+static gint ett_memcache = -1;
+static gint ett_extras = -1;
+
+/* User definable values */
+static gboolean memcache_desegment = TRUE;
+
+static const value_string magic_vals[] = {
+ { MAGIC_REQUEST, "Request" },
+ { MAGIC_RESPONSE, "Response" },
+ { 0, NULL }
+};
+
+static const value_string status_vals[] = {
+ { RS_NO_ERROR, "No error" },
+ { RS_KEY_NOT_FOUND, "Key not found" },
+ { RS_KEY_EXISTS, "Key exists" },
+ { RS_VALUE_TOO_BIG, "Value too big" },
+ { RS_INVALID_ARGUMENTS, "Invalid arguments" },
+ { RS_ITEM_NOT_STORED, "Item not stored" },
+ { RS_UNKNOWN_COMMAND, "Unknown command" },
+ { RS_OUT_OF_MEMORY, "Out of memory" },
+ { 0, NULL }
+};
+
+static const value_string opcode_vals[] = {
+ { OP_GET, "Get" },
+ { OP_SET, "Set" },
+ { OP_ADD, "Add" },
+ { OP_REPLACE, "Replace" },
+ { OP_DELETE, "Delete" },
+ { OP_INCREMENT, "Increment" },
+ { OP_DECREMENT, "Decrement" },
+ { OP_QUIT, "Quit" },
+ { OP_FLUSH, "Flush" },
+ { OP_GET_Q, "Get Quietly" },
+ { OP_NO_OP, "No-op" },
+ { OP_VERSION, "Version" },
+ { OP_GET_K, "Get Key" },
+ { OP_GET_K_Q, "Get Key Quietly" },
+ { OP_APPEND, "Append" },
+ { OP_PREPEND, "Prepend" },
+ { OP_STAT, "Statistics" },
+ { OP_SET_Q, "Set Quietly" },
+ { OP_ADD_Q, "Add Quietly" },
+ { OP_REPLACE_Q, "Replace Quietly" },
+ { OP_DELETE_Q, "Delete Quietly" },
+ { OP_INCREMENT_Q, "Increment Quietly" },
+ { OP_DECREMENT_Q, "Decrement Quietly" },
+ { OP_QUIT_Q, "Quit Quietly" },
+ { OP_FLUSH_Q, "Flush Quietly" },
+ { OP_APPEND_Q, "Append Quietly" },
+ { OP_PREPEND_Q, "Prepend Quietly" },
+ { 0, NULL }
+};
+
+static const value_string data_type_vals[] = {
+ { DT_RAW_BYTES, "Raw bytes" },
+ { 0, NULL }
+};
+
+static guint
+get_memcache_pdu_len (packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
+{
+ guint32 body_len;
+
+ /* Get the length of the memcache body */
+ body_len = tvb_get_ntohl(tvb, offset+8);
+
+ /* That length doesn't include the header; add that in */
+ return body_len + MEMCACHE_HEADER_LEN;
+}
+
+static void
+dissect_extras (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ gint offset, guint8 extras_len, guint8 opcode, gboolean request)
+{
+ proto_tree *extras_tree = NULL;
+ proto_item *extras_item = NULL, *ti;
+ gint save_offset = offset;
+ gboolean illegal = FALSE; /* Set when extras shall not be present */
+ gboolean missing = FALSE; /* Set when extras is missing */
+
+ if (extras_len) {
+ extras_item = proto_tree_add_item (tree, hf_extras, tvb, offset, extras_len, FALSE);
+ extras_tree = proto_item_add_subtree (extras_item, ett_extras);
+ }
+
+ switch (opcode) {
+
+ case OP_GET:
+ case OP_GET_Q:
+ case OP_GET_K:
+ case OP_GET_K_Q:
+ if (extras_len) {
+ if (request) {
+ /* Request shall not have extras */
+ illegal = TRUE;
+ } else {
+ proto_tree_add_item (extras_tree, hf_extras_flags, tvb, offset, 4, FALSE);
+ offset += 4;
+ }
+ } else if (!request) {
+ /* Response must have extras */
+ missing = TRUE;
+ }
+ break;
+
+ case OP_SET:
+ case OP_SET_Q:
+ case OP_ADD:
+ case OP_ADD_Q:
+ case OP_REPLACE:
+ case OP_REPLACE_Q:
+ if (extras_len) {
+ if (request) {
+ proto_tree_add_item (extras_tree, hf_extras_flags, tvb, offset, 4, FALSE);
+ offset += 4;
+
+ proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, FALSE);
+ offset += 4;
+ } else {
+ /* Response shall not have extras */
+ illegal = TRUE;
+ }
+ } else if (request) {
+ /* Request must have extras */
+ missing = TRUE;
+ }
+ break;
+
+ case OP_INCREMENT:
+ case OP_INCREMENT_Q:
+ case OP_DECREMENT:
+ case OP_DECREMENT_Q:
+ if (extras_len) {
+ if (request) {
+ proto_tree_add_item (extras_tree, hf_extras_delta, tvb, offset, 8, FALSE);
+ offset += 8;
+
+ proto_tree_add_item (extras_tree, hf_extras_initial, tvb, offset, 8, FALSE);
+ offset += 8;
+
+ proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, FALSE);
+ offset += 4;
+ } else {
+ /* Response must not have extras (response is in Value) */
+ illegal = TRUE;
+ }
+ } else if (request) {
+ /* Request must have extras */
+ missing = TRUE;
+ }
+ break;
+
+ case OP_FLUSH:
+ case OP_FLUSH_Q:
+ if (extras_len) {
+ proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, FALSE);
+ offset += 4;
+ }
+ break;
+
+ case OP_DELETE:
+ case OP_DELETE_Q:
+ case OP_QUIT:
+ case OP_QUIT_Q:
+ case OP_VERSION:
+ case OP_APPEND:
+ case OP_APPEND_Q:
+ case OP_PREPEND:
+ case OP_PREPEND_Q:
+ case OP_STAT:
+ /* Must not have extras */
+ if (extras_len) {
+ illegal = TRUE;
+ }
+ break;
+
+ default:
+ if (extras_len) {
+ /* Decode as unknown extras */
+ proto_tree_add_item (extras_tree, hf_extras_unknown, tvb, offset, extras_len, FALSE);
+ offset += extras_len;
+ }
+ break;
+ }
+
+ if (illegal) {
+ ti = proto_tree_add_item (extras_tree, hf_extras_unknown, tvb, offset, extras_len, FALSE);
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s shall not have Extras",
+ val_to_str (opcode, opcode_vals, "Opcode %d"),
+ request ? "Request" : "Response");
+ offset += extras_len;
+ } else if (missing) {
+ ti = proto_tree_add_item (tree, hf_extras_missing, tvb, offset, 0, FALSE);
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s must have Extras",
+ val_to_str (opcode, opcode_vals, "Opcode %d"),
+ request ? "Request" : "Response");
+ }
+
+ if ((offset - save_offset) != extras_len) {
+ expert_add_info_format (pinfo, extras_item, PI_UNDECODED, PI_WARN, "Illegal Extras length, should be %d", offset - save_offset);
+ }
+}
+
+static void
+dissect_key (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ gint offset, guint8 key_len, guint8 opcode, gboolean request)
+{
+ proto_item *ti = NULL;
+ gboolean illegal = FALSE; /* Set when key shall not be present */
+ gboolean missing = FALSE; /* Set when key is missing */
+
+ if (key_len) {
+ ti = proto_tree_add_item (tree, hf_key, tvb, offset, key_len, FALSE);
+ offset += key_len;
+ }
+
+ /* Sanity check */
+ if (key_len) {
+ if ((opcode == OP_QUIT) || (opcode == OP_QUIT_Q) || (opcode == OP_NO_OP) || (opcode == OP_VERSION)) {
+ /* Request and Response must not have key */
+ illegal = TRUE;
+ }
+ if ((opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) || (opcode == OP_DELETE) ||
+ (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) || (opcode == OP_DELETE_Q) ||
+ (opcode == OP_FLUSH) || (opcode == OP_APPEND) || (opcode == OP_PREPEND) ||
+ (opcode == OP_FLUSH_Q) || (opcode == OP_APPEND_Q) || (opcode == OP_PREPEND_Q))
+ {
+ /* Response must not have a key */
+ if (!request) {
+ illegal = TRUE;
+ }
+ }
+ } else {
+ if ((opcode == OP_GET) || (opcode == OP_GET_Q) || (opcode == OP_GET_K) || (opcode == OP_GET_K_Q) ||
+ (opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) || (opcode == OP_DELETE) ||
+ (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) || (opcode == OP_DELETE_Q) ||
+ (opcode == OP_INCREMENT) || (opcode == OP_DECREMENT) || (opcode == OP_INCREMENT_Q) || (opcode == OP_DECREMENT_Q))
+ {
+ /* Request must have key */
+ if (request) {
+ missing = TRUE;
+ }
+ }
+ }
+
+ if (illegal) {
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s shall not have Key",
+ val_to_str (opcode, opcode_vals, "Opcode %d"),
+ request ? "Request" : "Response");
+ } else if (missing) {
+ ti = proto_tree_add_item (tree, hf_key_missing, tvb, offset, 0, FALSE);
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s must have Key",
+ val_to_str (opcode, opcode_vals, "Opcode %d"),
+ request ? "Request" : "Response");
+ }
+}
+
+static void
+dissect_value (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ gint offset, guint8 value_len, guint8 opcode, gboolean request)
+{
+ proto_item *ti = NULL;
+ gboolean illegal = FALSE; /* Set when value shall not be present */
+ gboolean missing = FALSE; /* Set when value is missing */
+
+ if (value_len > 0) {
+ if (!request && ((opcode == OP_INCREMENT) || (opcode == OP_DECREMENT))) {
+ ti = proto_tree_add_item (tree, hf_uint64_response, tvb, offset, 8, FALSE);
+ if (value_len != 8) {
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "Illegal Value length, should be 8");
+ }
+ } else {
+ ti = proto_tree_add_item (tree, hf_value, tvb, offset, value_len, FALSE);
+ }
+ offset += value_len;
+ }
+
+ /* Sanity check */
+ if (value_len) {
+ if ((opcode == OP_GET) || (opcode == OP_GET_Q) || (opcode == OP_GET_K) || (opcode == OP_GET_K_Q) ||
+ (opcode == OP_INCREMENT) || (opcode == OP_DECREMENT) || (opcode == OP_VERSION) ||
+ (opcode == OP_INCREMENT_Q) || (opcode == OP_DECREMENT_Q))
+ {
+ /* Request must not have value */
+ if (request) {
+ illegal = TRUE;
+ }
+ }
+ if ((opcode == OP_DELETE) || (opcode == OP_QUIT) || (opcode == OP_FLUSH) || (opcode == OP_NO_OP) ||
+ (opcode == OP_DELETE_Q) || (opcode == OP_QUIT_Q) || (opcode == OP_FLUSH_Q))
+ {
+ /* Request and Response must not have value */
+ illegal = TRUE;
+ }
+ if ((opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) ||
+ (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) ||
+ (opcode == OP_APPEND) || (opcode == OP_PREPEND) || (opcode == OP_APPEND_Q) || (opcode == OP_PREPEND_Q))
+ {
+ /* Response must not have value */
+ if (!request) {
+ illegal = TRUE;
+ }
+ }
+ } else {
+ if ((opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) ||
+ (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) ||
+ (opcode == OP_APPEND) || (opcode == OP_PREPEND) || (opcode == OP_APPEND_Q) || (opcode == OP_PREPEND_Q))
+ {
+ /* Request must have a value */
+ if (request) {
+ missing = TRUE;
+ }
+ }
+ }
+
+ if (illegal) {
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s shall not have Value",
+ val_to_str (opcode, opcode_vals, "Opcode %d"),
+ request ? "Request" : "Response");
+ } else if (missing) {
+ ti = proto_tree_add_item (tree, hf_value_missing, tvb, offset, 0, FALSE);
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s %s must have Value",
+ val_to_str (opcode, opcode_vals, "Opcode %d"),
+ request ? "Request" : "Response");
+ }
+}
+
+static void
+dissect_memcache (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_tree *memcache_tree;
+ proto_item *memcache_item, *ti;
+ gint offset = 0;
+ guint8 magic, opcode, extras_len;
+ guint16 key_len, status = 0;
+ guint32 body_len, value_len;
+ gboolean request;
+
+ if (check_col (pinfo->cinfo, COL_PROTOCOL))
+ col_set_str (pinfo->cinfo, COL_PROTOCOL, PSNAME);
+
+ if (check_col (pinfo->cinfo, COL_INFO))
+ col_clear (pinfo->cinfo, COL_INFO);
+
+ memcache_item = proto_tree_add_item (tree, proto_memcache, tvb, offset, -1, FALSE);
+ memcache_tree = proto_item_add_subtree (memcache_item, ett_memcache);
+
+ magic = tvb_get_guint8 (tvb, offset);
+ ti = proto_tree_add_item (memcache_tree, hf_magic, tvb, offset, 1, FALSE);
+ offset += 1;
+
+ if (match_strval (magic, magic_vals) == NULL) {
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "Unknown magic byte: %d", magic);
+ }
+
+ opcode = tvb_get_guint8 (tvb, offset);
+ ti = proto_tree_add_item (memcache_tree, hf_opcode, tvb, offset, 1, FALSE);
+ offset += 1;
+
+ if (match_strval (opcode, opcode_vals) == NULL) {
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "Unknown opcode: %d", opcode);
+ }
+
+ proto_item_append_text (memcache_item, ", %s %s", val_to_str (opcode, opcode_vals, "Unknown opcode (%d)"),
+ val_to_str (magic, magic_vals, "Unknown magic (%d)"));
+
+ if (check_col (pinfo->cinfo, COL_INFO))
+ col_append_fstr (pinfo->cinfo, COL_INFO, "%s %s",
+ val_to_str (opcode, opcode_vals, "Unknown opcode (%d)"),
+ val_to_str (magic, magic_vals, "Unknown magic (%d)"));
+
+ key_len = tvb_get_ntohs (tvb, offset);
+ proto_tree_add_item (memcache_tree, hf_key_length, tvb, offset, 2, FALSE);
+ offset += 2;
+
+ extras_len = tvb_get_guint8 (tvb, offset);
+ proto_tree_add_item (memcache_tree, hf_extras_length, tvb, offset, 1, FALSE);
+ offset += 1;
+
+ proto_tree_add_item (memcache_tree, hf_data_type, tvb, offset, 1, FALSE);
+ offset += 1;
+
+ status = tvb_get_ntohs (tvb, offset);
+ if (magic & 0x01) { /* We suppose this is a response, even when unknown magic byte */
+ request = FALSE;
+ ti = proto_tree_add_item (memcache_tree, hf_status, tvb, offset, 2, FALSE);
+ if (status != 0) {
+ expert_add_info_format (pinfo, ti, PI_RESPONSE_CODE, PI_NOTE, "%s: %s",
+ val_to_str (opcode, opcode_vals, "Unknown opcode (%d)"),
+ val_to_str (status, status_vals, "Status: %d"));
+ }
+ } else {
+ request = TRUE;
+ ti = proto_tree_add_item (memcache_tree, hf_reserved, tvb, offset, 2, FALSE);
+ if (status != 0) {
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "Reserved value: %d", status);
+ }
+ }
+ offset += 2;
+
+ body_len = tvb_get_ntohl (tvb, offset);
+ value_len = body_len - extras_len - key_len;
+ ti = proto_tree_add_uint (memcache_tree, hf_value_length, tvb, offset, 0, value_len);
+ PROTO_ITEM_SET_GENERATED (ti);
+
+ proto_tree_add_item (memcache_tree, hf_total_body_length, tvb, offset, 4, FALSE);
+ offset += 4;
+
+ proto_tree_add_item (memcache_tree, hf_opaque, tvb, offset, 4, FALSE);
+ offset += 4;
+
+ proto_tree_add_item (memcache_tree, hf_cas, tvb, offset, 8, FALSE);
+ offset += 8;
+
+ if (status == 0) {
+ dissect_extras (tvb, pinfo, memcache_tree, offset, extras_len, opcode, request);
+ offset += extras_len;
+
+ dissect_key (tvb, pinfo, memcache_tree, offset, key_len, opcode, request);
+ offset += key_len;
+
+ dissect_value (tvb, pinfo, memcache_tree, offset, value_len, opcode, request);
+ offset += value_len;
+ } else if (body_len) {
+ proto_tree_add_item (memcache_tree, hf_value, tvb, offset, body_len, FALSE);
+ offset += body_len;
+
+ if (check_col (pinfo->cinfo, COL_INFO))
+ col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)",
+ val_to_str (status, status_vals, "Unknown status: %d"));
+ } else {
+ ti = proto_tree_add_item (memcache_tree, hf_value_missing, tvb, offset, 0, FALSE);
+ expert_add_info_format (pinfo, ti, PI_UNDECODED, PI_WARN, "%s with status %s (%d) must have Value",
+ val_to_str (opcode, opcode_vals, "Opcode %d"),
+ val_to_str (status, status_vals, "Unknown"), status);
+ }
+}
+
+static void
+dissect_memcache_tcp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ tcp_dissect_pdus (tvb, pinfo, tree, memcache_desegment, 12,
+ get_memcache_pdu_len, dissect_memcache);
+}
+
+static void
+dissect_memcache_udp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ dissect_memcache (tvb, pinfo, tree);
+}
+
+void
+proto_register_memcache (void)
+{
+ static hf_register_info hf[] = {
+ { &hf_magic,
+ { "Magic", "memcache.magic", FT_UINT8, BASE_DEC, VALS(magic_vals), 0x0, "Magic number", HFILL } },
+ { &hf_opcode,
+ { "Opcode", "memcache.opcode", FT_UINT8, BASE_DEC, VALS(opcode_vals), 0x0, "Command code", HFILL } },
+ { &hf_extras_length,
+ { "Extras length", "memcache.extras.length", FT_UINT8, BASE_DEC, NULL, 0x0, "Length in bytes of the command extras", HFILL } },
+ { &hf_key_length,
+ { "Key Length", "memcache.key.length", FT_UINT16, BASE_DEC, NULL, 0x0, "Length in bytes of the text key that follows the command extras", HFILL } },
+ { &hf_value_length,
+ { "Value length", "memcache.value.length", FT_UINT32, BASE_DEC, NULL, 0x0, "Length in bytes of the value that follows the key", HFILL } },
+ { &hf_data_type,
+ { "Data type", "memcache.data_type", FT_UINT8, BASE_DEC, VALS(data_type_vals), 0x0, NULL, HFILL } },
+ { &hf_reserved,
+ { "Reserved", "memcache.reserved", FT_UINT16, BASE_DEC, NULL, 0x0, "Reserved for future use", HFILL } },
+ { &hf_status,
+ { "Status", "memcache.status", FT_UINT16, BASE_DEC, VALS(status_vals), 0x0, "Status of the response", HFILL } },
+ { &hf_total_body_length,
+ { "Total body length", "memcache.total_body_length", FT_UINT32, BASE_DEC, NULL, 0x0, "Length in bytes of extra + key + value", HFILL } },
+ { &hf_opaque,
+ { "Opaque", "memcache.opaque", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_cas,
+ { "CAS", "memcache.cas", FT_UINT64, BASE_DEC, NULL, 0x0, "Data version check", HFILL } },
+ { &hf_extras,
+ { "Extras", "memcache.extras", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_extras_flags,
+ { "Flags", "memcache.extras.flags", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_extras_expiration,
+ { "Expiration", "memcache.extras.expiration", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_extras_delta,
+ { "Amount to add", "memcache.extras.delta", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_extras_initial,
+ { "Initial value", "memcache.extras.initial", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_extras_unknown,
+ { "Unknown", "memcache.extras.unknown", FT_BYTES, BASE_DEC, NULL, 0x0, "Unknown Extras", HFILL } },
+ { &hf_extras_missing,
+ { "Extras missing", "memcache.extras.missing", FT_NONE, BASE_NONE, NULL, 0x0, "Extras is mandatory for this command", HFILL } },
+ { &hf_key,
+ { "Key", "memcache.key", FT_STRING, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_key_missing,
+ { "Key missing", "memcache.key.missing", FT_NONE, BASE_NONE, NULL, 0x0, "Key is mandatory for this command", HFILL } },
+ { &hf_value,
+ { "Value", "memcache.value", FT_STRING, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_value_missing,
+ { "Value missing", "memcache.value.missing", FT_NONE, BASE_NONE, NULL, 0x0, "Value is mandatory for this command", HFILL } },
+ { &hf_uint64_response,
+ { "Response", "memcache.extras.response", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ };
+
+ static gint *ett[] = {
+ &ett_memcache,
+ &ett_extras
+ };
+
+ module_t *memcache_module;
+
+ proto_memcache = proto_register_protocol (PNAME, PSNAME, PFNAME);
+ register_dissector ("memcache.tcp", dissect_memcache_tcp, proto_memcache);
+ register_dissector ("memcache.udp", dissect_memcache_udp, proto_memcache);
+
+ proto_register_field_array (proto_memcache, hf, array_length (hf));
+ proto_register_subtree_array (ett, array_length (ett));
+
+ /* Register our configuration options */
+ memcache_module = prefs_register_protocol (proto_memcache, proto_reg_handoff_memcache);
+
+ prefs_register_bool_preference (memcache_module, "desegment_pdus",
+ "Reassemble PDUs spanning multiple TCP segments",
+ "Whether the memcache dissector should reassemble PDUs"
+ " spanning multiple TCP segments."
+ " To use this option, you must also enable \"Allow subdissectors"
+ " to reassemble TCP streams\" in the TCP protocol settings.",
+ &memcache_desegment);
+}
+
+void
+proto_reg_handoff_memcache (void)
+{
+ dissector_handle_t memcache_tcp_handle;
+ dissector_handle_t memcache_udp_handle;
+
+ memcache_tcp_handle = find_dissector ("memcache.tcp");
+ memcache_udp_handle = find_dissector ("memcache.udp");
+
+ dissector_add ("tcp.port", MEMCACHE_PORT, memcache_tcp_handle);
+ dissector_add ("udp.port", MEMCACHE_PORT, memcache_udp_handle);
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 noexpandtab
+ * :indentSize=2:tabSize=8:noTabs=false:
+ */