diff options
author | Stig Bjørlykke <stig@bjorlykke.org> | 2009-03-03 21:57:43 +0000 |
---|---|---|
committer | Stig Bjørlykke <stig@bjorlykke.org> | 2009-03-03 21:57:43 +0000 |
commit | dce5b2bc42f8d1517d835a3563e87d98e9d361de (patch) | |
tree | 1d0fd27020919dd82a28a36147a5b638072a9020 /epan/dissectors/packet-memcache.c | |
parent | c84b8b1c4bc26de15e6665c14a7e53d3124e807b (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.c | 674 |
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: + */ |