aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-couchbase.c
diff options
context:
space:
mode:
authorDave Rigby <daver@couchbase.com>2015-08-20 16:05:57 +0100
committerAnders Broman <a.broman58@gmail.com>2015-08-21 14:23:51 +0000
commitb5ca69b1a687747d4e0c775ec2d8134268beadac (patch)
treec73d7dce311a41e4a1ec4f4de2d903ac7b6d7522 /epan/dissectors/packet-couchbase.c
parentd097968285c132d3d2c7922013bbf4297622daa6 (diff)
Couchbase: dissect sub-document commands
Add support for dissecting Couchbase sub-document commands (those which operate on a subset of a whole document). Example pcap file: https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=couchbase_subdoc_multi.pcap Change-Id: I5394a6f86c036ebd339574bac4e52f7d0821e4bf Reviewed-on: https://code.wireshark.org/review/10151 Reviewed-by: Michael Mann <mmann78@netscape.net> Petri-Dish: Michael Mann <mmann78@netscape.net> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan/dissectors/packet-couchbase.c')
-rw-r--r--epan/dissectors/packet-couchbase.c349
1 files changed, 338 insertions, 11 deletions
diff --git a/epan/dissectors/packet-couchbase.c b/epan/dissectors/packet-couchbase.c
index 82b3a31d1e..082963652c 100644
--- a/epan/dissectors/packet-couchbase.c
+++ b/epan/dissectors/packet-couchbase.c
@@ -1,6 +1,7 @@
/* packet-couchbase.c
*
* Routines for Couchbase Protocol
+ * Copyright 2015, Dave Rigby <daver@couchbase.com>
* Copyright 2011, Sergey Avseyev <sergey.avseyev@gmail.com>
*
* With contributions from Mark Woosey <mark@markwoosey.com>
@@ -78,6 +79,19 @@
#define PROTOCOL_BINARY_RESPONSE_EINTERNAL 0x84
#define PROTOCOL_BINARY_RESPONSE_EBUSY 0x85
#define PROTOCOL_BINARY_RESPONSE_ETMPFAIL 0x86
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT 0xc0
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH 0xc1
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EINVAL 0xc2
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_E2BIG 0xc3
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_E2DEEP 0xc4
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_CANTINSERT 0xc5
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_NOTJSON 0xc6
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_NUM_ERANGE 0xc7
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_DELTA_ERANGE 0xc8
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EEXISTS 0xc9
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_ETOODEEP 0xca
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_INVALID_COMBO 0xcb
+#define PROTOCOL_BINARY_RESPONSE_SUBDOC_MULTI_PATH_FAILURE 0xcc
/* Command Opcodes */
#define PROTOCOL_BINARY_CMD_GET 0x00
@@ -206,6 +220,20 @@
#define PROTOCOL_BINARY_CMD_SET_CLUSTER_CONFIG 0xb4
#define PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG 0xb5
+/* Sub-document API commands */
+#define PROTOCOL_BINARY_CMD_SUBDOC_GET 0xc5
+#define PROTOCOL_BINARY_CMD_SUBDOC_EXISTS 0xc6
+#define PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD 0xc7
+#define PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT 0xc8
+#define PROTOCOL_BINARY_CMD_SUBDOC_DELETE 0xc9
+#define PROTOCOL_BINARY_CMD_SUBDOC_REPLACE 0xca
+#define PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST 0xcb
+#define PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST 0xcc
+#define PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT 0xcd
+#define PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE 0xce
+#define PROTOCOL_BINARY_CMD_SUBDOC_COUNTER 0xcf
+#define PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP 0xd0
+#define PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION 0xd1
/* DCP commands */
#define PROTOCOL_BINARY_DCP_OPEN_CONNECTION 0x50
@@ -278,6 +306,8 @@ static int hf_extras_flags_dcp_snapshot_marker_memory = -1;
static int hf_extras_flags_dcp_snapshot_marker_disk = -1;
static int hf_extras_flags_dcp_snapshot_marker_chk = -1;
static int hf_extras_flags_dcp_snapshot_marker_ack = -1;
+static int hf_subdoc_flags = -1;
+static int hf_subdoc_flags_mkdirp = -1;
static int hf_extras_seqno = -1;
static int hf_extras_opaque = -1;
static int hf_extras_reserved = -1;
@@ -295,7 +325,9 @@ static int hf_extras_rev_seqno = -1;
static int hf_extras_lock_time = -1;
static int hf_extras_nmeta = -1;
static int hf_extras_nru = -1;
+static int hf_extras_pathlen = -1;
static int hf_key = -1;
+static int hf_path = -1;
static int hf_value = -1;
static int hf_uint64_response = -1;
static int hf_observe = -1;
@@ -310,6 +342,14 @@ static int hf_failover_log_size = -1;
static int hf_failover_log_vbucket_uuid = -1;
static int hf_failover_log_vbucket_seqno = -1;
+static int hf_multipath = -1;
+static int hf_multipath_opcode = -1;
+static int hf_multipath_index = -1;
+static int hf_multipath_pathlen = -1;
+static int hf_multipath_path = -1;
+static int hf_multipath_valuelen = -1;
+static int hf_multipath_value = -1;
+
static expert_field ef_warn_shall_not_have_value = EI_INIT;
static expert_field ef_warn_shall_not_have_extras = EI_INIT;
static expert_field ef_warn_shall_not_have_key = EI_INIT;
@@ -328,6 +368,7 @@ static gint ett_extras = -1;
static gint ett_extras_flags = -1;
static gint ett_observe = -1;
static gint ett_failover_log = -1;
+static gint ett_multipath = -1;
static const value_string magic_vals[] = {
{ MAGIC_REQUEST, "Request" },
@@ -355,6 +396,33 @@ static const value_string status_vals[] = {
{ PROTOCOL_BINARY_RESPONSE_EINTERNAL, "Internal error" },
{ PROTOCOL_BINARY_RESPONSE_EBUSY, "Server is busy" },
{ PROTOCOL_BINARY_RESPONSE_ETMPFAIL, "Temporary failure" },
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT,
+ "Subdoc: Path not does not exist"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH,
+ "Subdoc: Path mismatch"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EINVAL,
+ "Subdoc: Invalid path"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_E2BIG,
+ "Subdoc: Path too large"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_E2DEEP,
+ "Subdoc: Document too deep"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_CANTINSERT,
+ "Subdoc: Cannot insert specified value"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_NOTJSON,
+ "Subdoc: Existing document not JSON"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_NUM_ERANGE,
+ "Subdoc: Existing number outside valid arithmetic range"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_DELTA_ERANGE,
+ "Subdoc: Delta outside valid arithmetic range"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EEXISTS,
+ "Subdoc: Document path already exists"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_ETOODEEP,
+ "Subdoc: Inserting value would make document too deep"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_INVALID_COMBO,
+ "Subdoc: Invalid combination for multi-path command"},
+ { PROTOCOL_BINARY_RESPONSE_SUBDOC_MULTI_PATH_FAILURE,
+ "Subdoc: One or more paths in a multi-path command failed"},
+
{ 0, NULL }
};
@@ -485,6 +553,19 @@ static const value_string opcode_vals[] = {
{ PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG, "Get Cluster Config" },
{ PROTOCOL_BINARY_CMD_GET_RANDOM_KEY, "Get Random Key" },
{ PROTOCOL_BINARY_CMD_SEQNO_PERSISTENCE, "Seqno Persistence" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_GET, "Subdoc Get" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_EXISTS, "Subdoc Exists" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD, "Subdoc Dictionary Add" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT, "Subdoc Dictionary Upsert" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "Subdoc Delete" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_REPLACE, "Subdoc Replace" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST, "Subdoc Array Push Last" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST, "Subdoc Array Push First" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT, "Subdoc Array Insert" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE, "Subdoc Array Add Unique" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_COUNTER, "Subdoc Counter" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP, "Subdoc Multipath Lookup" },
+ { PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION, "Subdoc Multipath Mutation"},
{ PROTOCOL_BINARY_CMD_SCRUB, "Scrub" },
{ PROTOCOL_BINARY_CMD_ISASL_REFRESH, "isasl Refresh" },
{ PROTOCOL_BINARY_CMD_SSL_CERTS_REFRESH, "SSL Certificates Refresh" },
@@ -510,6 +591,11 @@ static const value_string datatype_vals[] = {
{ 0, NULL }
};
+static const int * subdoc_flags[] = {
+ &hf_subdoc_flags_mkdirp,
+ NULL
+};
+
static dissector_handle_t couchbase_tcp_handle;
static dissector_handle_t json_handle;
@@ -538,9 +624,25 @@ get_couchbase_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb,
}
}
+
+/* Returns true if the specified opcode's response value is JSON. */
+static gboolean
+has_json_value(guint8 opcode)
+{
+ switch (opcode) {
+ case PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG:
+ case PROTOCOL_BINARY_CMD_SUBDOC_GET:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
static void
dissect_extras(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
- gint offset, guint8 extlen, guint8 opcode, gboolean request)
+ gint offset, guint8 extlen, guint8 opcode, gboolean request,
+ guint16 *path_len)
{
proto_tree *extras_tree = NULL;
proto_item *extras_item = NULL;
@@ -556,6 +658,8 @@ dissect_extras(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
"SUPPORT_ACK", "REQUEST_KEYS_ONLY", "CHECKPOINT", "REGISTERED_CLIENT"
};
+ *path_len = 0;
+
if (extlen) {
extras_item = proto_tree_add_item(tree, hf_extras, tvb, offset, extlen, ENC_NA);
extras_tree = proto_item_add_subtree(extras_item, ett_extras);
@@ -848,6 +952,41 @@ dissect_extras(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
}
break;
+ case PROTOCOL_BINARY_CMD_SUBDOC_GET:
+ case PROTOCOL_BINARY_CMD_SUBDOC_EXISTS:
+ case PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD:
+ case PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT:
+ case PROTOCOL_BINARY_CMD_SUBDOC_DELETE:
+ case PROTOCOL_BINARY_CMD_SUBDOC_REPLACE:
+ case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST:
+ case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST:
+ case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT:
+ case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE:
+ case PROTOCOL_BINARY_CMD_SUBDOC_COUNTER:
+ if (extlen) {
+ if (request) {
+ *path_len = tvb_get_ntohs(tvb, offset);
+ proto_tree_add_item(extras_tree, hf_extras_pathlen, tvb, offset, 2, ENC_BIG_ENDIAN);
+ offset += 2;
+
+ proto_tree_add_bitmask(extras_tree, tvb, offset, hf_subdoc_flags, ett_extras_flags, subdoc_flags, ENC_BIG_ENDIAN);
+ offset += 1;
+ }
+ } else if (request) {
+ /* Request must have extras */
+ missing = TRUE;
+ }
+ break;
+
+ case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP:
+ case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION:
+ if (request) {
+ if (extlen) {
+ illegal = TRUE;
+ }
+ }
+ break;
+
default:
if (extlen) {
/* Decode as unknown extras */
@@ -976,8 +1115,123 @@ dissect_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
}
static void
+dissect_multipath_lookup_response(tvbuff_t *tvb, packet_info *pinfo,
+ proto_tree *tree, gint offset, guint32 value_len)
+{
+ gint end = offset + value_len;
+ int spec_idx = 0;
+
+ while (offset < end) {
+ proto_item *ti;
+ proto_tree *multipath_tree;
+ tvbuff_t *json_tvb;
+ guint32 result_len;
+ gint start_offset = offset;
+
+ ti = proto_tree_add_subtree_format(tree, tvb, offset, -1, ett_multipath,
+ &multipath_tree, "Lookup Result [ %u ]",
+ spec_idx);
+
+ proto_tree_add_item(multipath_tree, hf_status, tvb, offset, 2,
+ ENC_BIG_ENDIAN);
+ offset += 2;
+ proto_tree_add_item_ret_uint(multipath_tree, hf_value_length, tvb, offset,
+ 4, ENC_BIG_ENDIAN, &result_len);
+ offset += 4;
+
+ proto_tree_add_item(multipath_tree, hf_value, tvb, offset, result_len,
+ ENC_ASCII | ENC_NA);
+ if (result_len > 0) {
+ json_tvb = tvb_new_subset(tvb, offset, result_len, result_len);
+ call_dissector(json_handle, json_tvb, pinfo, multipath_tree);
+ }
+ offset += result_len;
+
+ proto_item_set_len(ti, offset - start_offset);
+
+ spec_idx++;
+ }
+}
+
+static void
+dissect_multipath_value(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ gint offset, guint32 value_len, gboolean is_mutation,
+ gboolean request)
+{
+ gint end = offset + value_len;
+ int spec_idx = 0;
+ proto_item *ti;
+ proto_tree *multipath_tree;
+
+ if (request) {
+ gint min_spec_size;
+
+ /* Minimum size is the fixed header plus at least 1 byte for path. */
+ min_spec_size = (is_mutation ? 8 : 4) + 1;
+
+ while (offset + min_spec_size < end) {
+ guint32 path_len;
+ guint32 spec_value_len = 0;
+ gint start_offset = offset;
+
+ ti = proto_tree_add_subtree_format(tree, tvb, offset, -1, ett_multipath,
+ &multipath_tree,
+ (is_mutation ? "Mutation spec [ %u ]"
+ : "Lookup spec [ %u ]"),
+ spec_idx);
+
+ proto_tree_add_item(multipath_tree, hf_multipath_opcode, tvb, offset, 1,
+ ENC_BIG_ENDIAN);
+ offset += 1;
+ proto_tree_add_bitmask(multipath_tree, tvb, offset, hf_subdoc_flags,
+ ett_extras_flags, subdoc_flags, ENC_BIG_ENDIAN);
+ offset += 1;
+
+ proto_tree_add_item_ret_uint(multipath_tree, hf_multipath_pathlen, tvb,
+ offset, 2, ENC_BIG_ENDIAN, &path_len);
+ offset += 2;
+
+ if (is_mutation) {
+ proto_tree_add_item_ret_uint(multipath_tree, hf_multipath_valuelen,
+ tvb, offset, 4, ENC_BIG_ENDIAN,
+ &spec_value_len);
+ offset += 4;
+ }
+
+ proto_tree_add_item(multipath_tree, hf_path, tvb, offset, path_len,
+ ENC_ASCII | ENC_NA);
+ offset += path_len;
+
+ if (spec_value_len > 0) {
+ proto_tree_add_item(multipath_tree, hf_value, tvb, offset,
+ spec_value_len, ENC_ASCII | ENC_NA);
+ offset += spec_value_len;
+ }
+
+ proto_item_set_len(ti, offset - start_offset);
+
+ spec_idx++;
+ }
+ } else {
+ /* Response - for lookup we expect one lookup_result per path. */
+ if (is_mutation) {
+ ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len,
+ ENC_ASCII | ENC_NA);
+
+ expert_add_info_format(pinfo, ti, &ef_warn_shall_not_have_value,
+ "%s Response shall not have Value",
+ val_to_str_ext(PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION,
+ &opcode_vals_ext, "Opcode 0x%x"));
+ } else {
+ dissect_multipath_lookup_response(tvb, pinfo, tree, offset, value_len);
+ }
+ }
+}
+
+static void
dissect_value(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
- gint offset, guint32 value_len, guint8 opcode, gboolean request)
+ gint offset, guint32 value_len, guint16 path_len, guint8 opcode,
+ gboolean request)
{
proto_item *ti = NULL;
gboolean illegal = FALSE; /* Set when value shall not be present */
@@ -1027,11 +1281,25 @@ dissect_value(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
if (value_len != 8) {
expert_add_info_format(pinfo, ti, &ef_warn_illegal_value_length, "Illegal Value length, should be 8");
}
- } else if (!request && (opcode == PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG)) {
+ } else if (!request && has_json_value(opcode)) {
tvbuff_t *json_tvb;
ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII | ENC_NA);
json_tvb = tvb_new_subset(tvb, offset, value_len, value_len);
call_dissector(json_handle, json_tvb, pinfo, tree);
+
+ } else if (opcode == PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP ||
+ opcode == PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION) {
+ dissect_multipath_value(tvb, pinfo, tree, offset, value_len,
+ (opcode == PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION),
+ request);
+
+ } else if (path_len != 0) {
+ ti = proto_tree_add_item(tree, hf_path, tvb, offset, path_len, ENC_ASCII | ENC_NA);
+ value_len -= path_len;
+ if (value_len > 0) {
+ ti = proto_tree_add_item(tree, hf_value, tvb, offset + path_len,
+ value_len, ENC_ASCII | ENC_NA);
+ }
} else {
ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII | ENC_NA);
}
@@ -1210,27 +1478,71 @@ dissect_couchbase(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
}
if (status == 0) {
- dissect_extras(tvb, pinfo, couchbase_tree, offset, extlen, opcode, request);
+ guint16 path_len = 0;
+
+ dissect_extras(tvb, pinfo, couchbase_tree, offset, extlen, opcode, request,
+ &path_len);
offset += extlen;
dissect_key(tvb, pinfo, couchbase_tree, offset, keylen, opcode, request);
offset += keylen;
- dissect_value(tvb, pinfo, couchbase_tree, offset, value_len, opcode, request);
+ dissect_value(tvb, pinfo, couchbase_tree, offset, value_len, path_len,
+ opcode, request);
} else if (bodylen) {
- proto_tree_add_item(couchbase_tree, hf_value, tvb, offset, bodylen, ENC_ASCII | ENC_NA);
+ ti = proto_tree_add_item(couchbase_tree, hf_value, tvb, offset, bodylen,
+ ENC_ASCII | ENC_NA);
if (status == PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET) {
tvbuff_t *json_tvb;
json_tvb = tvb_new_subset(tvb, offset, bodylen, bodylen);
call_dissector(json_handle, json_tvb, pinfo, couchbase_tree);
+
+ } else if (opcode == PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP) {
+ dissect_multipath_lookup_response(tvb, pinfo, tree, offset, value_len);
+
+ } else if (opcode == PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION) {
+ /* Upon non-success includes the index and status code of first path
+ * to fail.
+ */
+ proto_tree *multipath_tree;
+ multipath_tree = proto_item_add_subtree(ti, ett_multipath);
+
+ proto_tree_add_item(multipath_tree, hf_status, tvb, offset, 2,
+ ENC_BIG_ENDIAN);
+ offset += 2;
+ proto_tree_add_item(multipath_tree, hf_multipath_index, tvb, offset, 1,
+ ENC_BIG_ENDIAN);
}
col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
val_to_str_ext(status, &status_vals_ext, "Unknown status: 0x%x"));
} else {
- proto_tree_add_expert_format(couchbase_tree, pinfo, &ei_value_missing, tvb, offset, 0,
- "%s with status %s (0x%x) must have Value",
- val_to_str_ext(opcode, &opcode_vals_ext, "Opcode 0x%x"),
- val_to_str_ext(status, &status_vals_ext, "Unknown"), status);
+ /* Newer opcodes do not include a value in non-SUCCESS responses. */
+ switch (opcode) {
+ case PROTOCOL_BINARY_CMD_SUBDOC_GET:
+ case PROTOCOL_BINARY_CMD_SUBDOC_EXISTS:
+ case PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD:
+ case PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT:
+ case PROTOCOL_BINARY_CMD_SUBDOC_DELETE:
+ case PROTOCOL_BINARY_CMD_SUBDOC_REPLACE:
+ case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST:
+ case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST:
+ case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT:
+ case PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE:
+ case PROTOCOL_BINARY_CMD_SUBDOC_COUNTER:
+ case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP:
+ case PROTOCOL_BINARY_CMD_SUBDOC_MULTI_MUTATION:
+ break;
+
+ default:
+ ti = proto_tree_add_item(tree, hf_value, tvb, offset, 0,
+ ENC_ASCII | ENC_NA);
+ expert_add_info_format(pinfo, ti, &ei_value_missing,
+ "%s with status %s (0x%x) must have Value",
+ val_to_str_ext(opcode, &opcode_vals_ext, "Opcode 0x%x"),
+ val_to_str_ext(status, &status_vals_ext, "Unknown"),
+ status);
+ break;
+ }
}
return tvb_reported_length(tvb);
}
@@ -1284,6 +1596,11 @@ proto_register_couchbase(void)
{ &hf_extras_flags_request_keys_only, { "Request Keys Only", "couchbase.extras.flags.request_keys_only", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x20, NULL, HFILL } },
{ &hf_extras_flags_checkpoint, { "Checkpoint", "couchbase.extras.flags.checkpoint", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x40, NULL, HFILL } },
+ /* Sub-document */
+ { &hf_subdoc_flags, {"Subdoc flags", "couchbase.extras.subdoc.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL} },
+ { &hf_subdoc_flags_mkdirp, {"MKDIR_P", "couchbase.extras.subdoc.flags.mkdir_p", FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x01, "Create non-existent intermediate paths", HFILL} },
+ { &hf_extras_pathlen, { "Path Length", "couchbase.extras.pathlen", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+
/* DCP flags */
{ &hf_extras_flags_dcp_connection_type, {"Connection Type", "couchbase.extras.flags.dcp_connection_type", FT_UINT32, BASE_HEX, VALS(dcp_connection_type_vals), 0x03, NULL, HFILL } },
{ &hf_extras_flags_dcp_add_stream_takeover, {"Take Over", "couchbase.extras.flags.dcp_add_stream_takeover", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x01, NULL, HFILL } },
@@ -1317,6 +1634,7 @@ proto_register_couchbase(void)
{ &hf_extras_initial, { "Initial Value", "couchbase.extras.initial", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_extras_unknown, { "Unknown", "couchbase.extras.unknown", FT_BYTES, BASE_NONE, NULL, 0x0, "Unknown Extras", HFILL } },
{ &hf_key, { "Key", "couchbase.key", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_path, { "Path", "couchbase.path", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_value, { "Value", "couchbase.value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
{ &hf_uint64_response, { "Response", "couchbase.extras.response", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
{ &hf_observe, { "Observe", "couchbase.observe", FT_STRING, BASE_NONE, NULL, 0x0, "The observe properties", HFILL } },
@@ -1325,6 +1643,14 @@ proto_register_couchbase(void)
{ &hf_observe_vbucket, { "VBucket", "couchbase.observe.vbucket", FT_UINT16, BASE_HEX, NULL, 0x0, "VBucket of the observable key", HFILL } },
{ &hf_observe_status, { "Status", "couchbase.observe.status", FT_UINT8, BASE_HEX, NULL, 0x0, "Status of the observable key", HFILL } },
{ &hf_observe_cas, { "CAS", "couchbase.observe.cas", FT_UINT64, BASE_HEX, NULL, 0x0, "CAS value of the observable key", HFILL } },
+
+ { &hf_multipath, { "Multipath", "couchbase.multipath", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_multipath_opcode, { "Opcode", "couchbase.multipath.opcode", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &opcode_vals_ext, 0x0, "Command code", HFILL } },
+ { &hf_multipath_index, { "Index", "couchbase.multipath.index", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_multipath_pathlen, { "Path Length", "couchbase.multipath.path.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_multipath_path, { "Path", "couchbase.multipath.path", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_multipath_valuelen, { "Value Length", "couchbase.multipath.value.length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_multipath_value, { "Value", "couchbase.multipath.value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
};
static ei_register_info ei[] = {
@@ -1346,7 +1672,8 @@ proto_register_couchbase(void)
&ett_extras,
&ett_extras_flags,
&ett_observe,
- &ett_failover_log
+ &ett_failover_log,
+ &ett_multipath
};
module_t *couchbase_module;