Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-couchbase.c
blob90e3c3ed7cd0247cc41992e40b67c6dbd6eda7d9
1 /* packet-couchbase.c
3 * Routines for Couchbase Protocol
5 * Copyright 2019, Trond Norbye <trond@couchbase.com>
6 * Copyright 2018, Jim Walker <jim@couchbase.com>
7 * Copyright 2015-2016, Dave Rigby <daver@couchbase.com>
8 * Copyright 2011, Sergey Avseyev <sergey.avseyev@gmail.com>
10 * With contributions from Mark Woosey <mark@markwoosey.com>
13 * Based on packet-memcache.c: mecmcache binary protocol.
15 * Routines for Memcache Binary Protocol
16 * http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol
18 * Copyright 2009, Stig Bjorlykke <stig@bjorlykke.org>
20 * Routines for Memcache Textual Protocol
21 * https://github.com/memcached/memcached/blob/master/doc/protocol.txt
23 * Copyright 2009, Rama Chitta <rama@gear6.com>
25 * Wireshark - Network traffic analyzer
26 * By Gerald Combs <gerald@wireshark.org>
27 * Copyright 1998 Gerald Combs
29 * SPDX-License-Identifier: GPL-2.0-or-later
32 #include "config.h"
35 #include <epan/packet.h>
36 #include <epan/prefs.h>
37 #include <epan/expert.h>
38 #include <epan/tfs.h>
39 #include <epan/unit_strings.h>
41 #ifdef HAVE_SNAPPY
42 #include <snappy-c.h>
43 #endif
45 #include "packet-tcp.h"
46 #include "packet-tls.h"
48 #include <math.h>
50 #define PNAME "Couchbase Protocol"
51 #define PSNAME "Couchbase"
52 #define PFNAME "couchbase"
54 #define COUCHBASE_DEFAULT_PORT "11210"
55 #define COUCHBASE_HEADER_LEN 24
57 /* Magic Byte */
58 enum {
59 /**
60 * The magic used for a normal request sent from the client to the
61 * server. Layout described in:
62 * https://github.com/couchbase/kv_engine/blob/master/docs/BinaryProtocol.md#request-header
64 MAGIC_CLIENT_REQUEST = 0x80,
65 /**
66 * The magic used for a normal response sent from the server to
67 * the client. Layout described in:
68 * https://github.com/couchbase/kv_engine/blob/master/docs/BinaryProtocol.md#response-header
70 MAGIC_CLIENT_RESPONSE = 0x81,
71 /**
72 * The magic used when the client want to inject a set of extensions
73 * to the command sent to the server. Layout described in:
74 * https://github.com/couchbase/kv_engine/blob/master/docs/BinaryProtocol.md#request-header-with-flexible-framing-extras
76 MAGIC_CLIENT_RESPONSE_FLEX = 0x18,
77 /**
78 * The magic used by the server when the server needs to inject a set of
79 * extensions in the response packet. Layout described in:
80 * https://github.com/couchbase/kv_engine/blob/master/docs/BinaryProtocol.md#response-header-with-flexible-framing-extras
82 MAGIC_CLIENT_REQUEST_FLEX = 0x08,
83 /**
84 * The magic used for server initiated push requests. These packets
85 * use the same layout as client flex request packets (with a different
86 * namespace for the frame id's)
88 MAGIC_SERVER_REQUEST = 0x82,
89 /**
90 * The magic used for responses to server initiated push requests. These
91 * packets use the same layout as client flex response packet (with
92 * a different namespace for the frame id's)
94 MAGIC_SERVER_RESPONSE = 0x83
97 /** Does the magic represent a flex encoded packet type */
98 static bool is_flex_encoded(uint8_t magic) {
99 switch (magic) {
100 case MAGIC_CLIENT_RESPONSE_FLEX:
101 case MAGIC_CLIENT_REQUEST_FLEX:
102 case MAGIC_SERVER_REQUEST:
103 case MAGIC_SERVER_RESPONSE:
104 return true;
106 case MAGIC_CLIENT_REQUEST:
107 case MAGIC_CLIENT_RESPONSE:
108 default:
109 return false;
113 /** Does the magic represent server initiated packet types */
114 static bool is_server_magic(uint8_t magic) {
115 switch (magic) {
116 case MAGIC_SERVER_REQUEST:
117 case MAGIC_SERVER_RESPONSE:
118 return true;
120 case MAGIC_CLIENT_RESPONSE_FLEX:
121 case MAGIC_CLIENT_REQUEST_FLEX:
122 case MAGIC_CLIENT_REQUEST:
123 case MAGIC_CLIENT_RESPONSE:
124 default:
125 return false;
129 /** Does the magic represent a request or a response */
130 static bool is_request_magic(uint8_t magic) {
131 switch (magic) {
132 case MAGIC_SERVER_REQUEST:
133 case MAGIC_CLIENT_REQUEST_FLEX:
134 case MAGIC_CLIENT_REQUEST:
135 return true;
137 case MAGIC_SERVER_RESPONSE:
138 case MAGIC_CLIENT_RESPONSE_FLEX:
139 case MAGIC_CLIENT_RESPONSE:
140 default:
141 return false;
145 /* Response Status */
146 #define STATUS_SUCCESS 0x00
147 #define STATUS_KEY_ENOENT 0x01
148 #define STATUS_KEY_EEXISTS 0x02
149 #define STATUS_E2BIG 0x03
150 #define STATUS_EINVAL 0x04
151 #define STATUS_NOT_STORED 0x05
152 #define STATUS_DELTA_BADVAL 0x06
153 #define STATUS_NOT_MY_VBUCKET 0x07
154 #define STATUS_NO_VBUCKET 0x08
155 #define STATUS_LOCKED 0x09
156 #define STATUS_DCP_STREAM_NOT_FOUND 0x0a
157 #define STATUS_OPAQUE_NO_MATCH 0x0b
158 #define STATUS_EWOULDTHROTTLE 0x0c
159 #define STATUS_ECONFIGONLY 0x0d
160 #define STATUS_NOT_LOCKED 0x0e
161 #define STATUS_AUTH_STALE 0x1f
162 #define STATUS_AUTH_ERROR 0x20
163 #define STATUS_AUTH_CONTINUE 0x21
164 #define STATUS_ERANGE 0x22
165 #define STATUS_ROLLBACK 0x23
166 #define STATUS_EACCESS 0x24
167 #define STATUS_NOT_INITIALIZED 0x25
168 #define STATUS_RATELIMITED_NETWORK_INGRESS 0x30
169 #define STATUS_RATELIMITED_NETWORK_EGRESS 0x31
170 #define STATUS_RATELIMITED_MAX_CONNECTIONS 0x32
171 #define STATUS_RATELIMITED_MAX_COMMANDS 0x33
172 #define STATUS_SCOPE_SIZE_LIMIT_EXCEEDED 0x34
173 #define STATUS_UNKNOWN_COMMAND 0x81
174 #define STATUS_ENOMEM 0x82
175 #define STATUS_NOT_SUPPORTED 0x83
176 #define STATUS_EINTERNAL 0x84
177 #define STATUS_EBUSY 0x85
178 #define STATUS_ETMPFAIL 0x86
179 #define STATUS_XATTR_EINVAL 0x87
180 #define STATUS_UNKNOWN_COLLECTION 0x88
181 #define STATUS_NO_COLLECTIONS_MANIFEST 0x89
182 #define STATUS_CANNOT_APPLY_MANIFEST 0x8a
183 #define STATUS_MANIFEST_IS_AHEAD 0x8b
184 #define STATUS_UNKNOWN_SCOPE 0x8c
185 #define STATUS_DCP_STREAMID_INVALID 0x8d
186 #define STATUS_DURABILITY_INVALID_LEVEL 0xa0
187 #define STATUS_DURABILITY_IMPOSSIBLE 0xa1
188 #define STATUS_SYNC_WRITE_IN_PROGRESS 0xa2
189 #define STATUS_SYNC_WRITE_AMBIGUOUS 0xa3
190 #define STATUS_SYNC_WRITE_RECOMMIT_IN_PROGRESS 0xa4
191 #define STATUS_RANGE_SCAN_CANCELLED 0xa5
192 #define STATUS_RANGE_SCAN_MORE 0xa6
193 #define STATUS_RANGE_SCAN_COMPLETE 0xa7
194 #define STATUS_VBUUID_NOT_EQUAL 0xa8
195 #define STATUS_SUBDOC_PATH_ENOENT 0xc0
196 #define STATUS_SUBDOC_PATH_MISMATCH 0xc1
197 #define STATUS_SUBDOC_PATH_EINVAL 0xc2
198 #define STATUS_SUBDOC_PATH_E2BIG 0xc3
199 #define STATUS_SUBDOC_DOC_E2DEEP 0xc4
200 #define STATUS_SUBDOC_VALUE_CANTINSERT 0xc5
201 #define STATUS_SUBDOC_DOC_NOTJSON 0xc6
202 #define STATUS_SUBDOC_NUM_ERANGE 0xc7
203 #define STATUS_SUBDOC_DELTA_ERANGE 0xc8
204 #define STATUS_SUBDOC_PATH_EEXISTS 0xc9
205 #define STATUS_SUBDOC_VALUE_ETOODEEP 0xca
206 #define STATUS_SUBDOC_INVALID_COMBO 0xcb
207 #define STATUS_SUBDOC_MULTI_PATH_FAILURE 0xcc
208 #define STATUS_SUBDOC_SUCCESS_DELETED 0xcd
209 #define STATUS_SUBDOC_XATTR_INVALID_FLAG_COMBO 0xce
210 #define STATUS_SUBDOC_XATTR_INVALID_KEY_COMBO 0xcf
211 #define STATUS_SUBDOC_XATTR_UNKNOWN_MACRO 0xd0
212 #define STATUS_SUBDOC_XATTR_UNKNOWN_VATTR 0xd1
213 #define STATUS_SUBDOC_XATTR_CANT_MODIFY_VATTR 0xd2
214 #define STATUS_SUBDOC_MULTI_PATH_FAILURE_DELETED 0xd3
215 #define STATUS_SUBDOC_INVALID_XATTR_ORDER 0xd4
216 #define STATUS_SUBDOC_XATTR_UNKNOWN_VATTR_MACRO 0xd5
217 #define STATUS_SUBDOC_CAN_ONLY_REVIVE_DELETED_DOCUMENTS 0xd6
218 #define STATUS_SUBDOC_DELETED_DOCUMENT_CANT_HAVE_VALUE 0xd7
220 /* Command Opcodes */
221 #define CLIENT_OPCODE_GET 0x00
222 #define CLIENT_OPCODE_SET 0x01
223 #define CLIENT_OPCODE_ADD 0x02
224 #define CLIENT_OPCODE_REPLACE 0x03
225 #define CLIENT_OPCODE_DELETE 0x04
226 #define CLIENT_OPCODE_INCREMENT 0x05
227 #define CLIENT_OPCODE_DECREMENT 0x06
228 #define CLIENT_OPCODE_QUIT 0x07
229 #define CLIENT_OPCODE_FLUSH 0x08
230 #define CLIENT_OPCODE_GETQ 0x09
231 #define CLIENT_OPCODE_NOOP 0x0a
232 #define CLIENT_OPCODE_VERSION 0x0b
233 #define CLIENT_OPCODE_GETK 0x0c
234 #define CLIENT_OPCODE_GETKQ 0x0d
235 #define CLIENT_OPCODE_APPEND 0x0e
236 #define CLIENT_OPCODE_PREPEND 0x0f
237 #define CLIENT_OPCODE_STAT 0x10
238 #define CLIENT_OPCODE_SETQ 0x11
239 #define CLIENT_OPCODE_ADDQ 0x12
240 #define CLIENT_OPCODE_REPLACEQ 0x13
241 #define CLIENT_OPCODE_DELETEQ 0x14
242 #define CLIENT_OPCODE_INCREMENTQ 0x15
243 #define CLIENT_OPCODE_DECREMENTQ 0x16
244 #define CLIENT_OPCODE_QUITQ 0x17
245 #define CLIENT_OPCODE_FLUSHQ 0x18
246 #define CLIENT_OPCODE_APPENDQ 0x19
247 #define CLIENT_OPCODE_PREPENDQ 0x1a
248 #define CLIENT_OPCODE_VERBOSITY 0x1b
249 #define CLIENT_OPCODE_TOUCH 0x1c
250 #define CLIENT_OPCODE_GAT 0x1d
251 #define CLIENT_OPCODE_GATQ 0x1e
252 #define CLIENT_OPCODE_HELLO 0x1f
254 /* SASL operations */
255 #define CLIENT_OPCODE_SASL_LIST_MECHS 0x20
256 #define CLIENT_OPCODE_SASL_AUTH 0x21
257 #define CLIENT_OPCODE_SASL_STEP 0x22
259 /* Control */
260 #define CLIENT_OPCODE_IOCTL_GET 0x23
261 #define CLIENT_OPCODE_IOCTL_SET 0x24
262 #define CLIENT_OPCODE_CONFIG_VALIDATE 0x25
263 #define CLIENT_OPCODE_CONFIG_RELOAD 0x26
264 #define CLIENT_OPCODE_AUDIT_PUT 0x27
265 #define CLIENT_OPCODE_AUDIT_CONFIG_RELOAD 0x28
266 #define CLIENT_OPCODE_SHUTDOWN 0x29
268 /* Range operations.
269 * These commands are used for range operations and exist within
270 * protocol_binary.h for use in other projects. Range operations are
271 * not expected to be implemented in the memcached server itself.
273 #define CLIENT_OPCODE_RGET 0x30
274 #define CLIENT_OPCODE_RSET 0x31
275 #define CLIENT_OPCODE_RSETQ 0x32
276 #define CLIENT_OPCODE_RAPPEND 0x33
277 #define CLIENT_OPCODE_RAPPENDQ 0x34
278 #define CLIENT_OPCODE_RPREPEND 0x35
279 #define CLIENT_OPCODE_RPREPENDQ 0x36
280 #define CLIENT_OPCODE_RDELETE 0x37
281 #define CLIENT_OPCODE_RDELETEQ 0x38
282 #define CLIENT_OPCODE_RINCR 0x39
283 #define CLIENT_OPCODE_RINCRQ 0x3a
284 #define CLIENT_OPCODE_RDECR 0x3b
285 #define CLIENT_OPCODE_RDECRQ 0x3c
288 /* VBucket commands */
289 #define CLIENT_OPCODE_SET_VBUCKET 0x3d
290 #define CLIENT_OPCODE_GET_VBUCKET 0x3e
291 #define CLIENT_OPCODE_DEL_VBUCKET 0x3f
293 /* TAP commands */
294 #define CLIENT_OPCODE_TAP_CONNECT 0x40
295 #define CLIENT_OPCODE_TAP_MUTATION 0x41
296 #define CLIENT_OPCODE_TAP_DELETE 0x42
297 #define CLIENT_OPCODE_TAP_FLUSH 0x43
298 #define CLIENT_OPCODE_TAP_OPAQUE 0x44
299 #define CLIENT_OPCODE_TAP_VBUCKET_SET 0x45
300 #define CLIENT_OPCODE_TAP_CHECKPOINT_START 0x46
301 #define CLIENT_OPCODE_TAP_CHECKPOINT_END 0x47
303 #define CLIENT_OPCODE_GET_ALL_VB_SEQNOS 0x48
305 /* DCP commands */
306 #define CLIENT_OPCODE_DCP_OPEN_CONNECTION 0x50
307 #define CLIENT_OPCODE_DCP_ADD_STREAM 0x51
308 #define CLIENT_OPCODE_DCP_CLOSE_STREAM 0x52
309 #define CLIENT_OPCODE_DCP_STREAM_REQUEST 0x53
310 #define CLIENT_OPCODE_DCP_FAILOVER_LOG_REQUEST 0x54
311 #define CLIENT_OPCODE_DCP_STREAM_END 0x55
312 #define CLIENT_OPCODE_DCP_SNAPSHOT_MARKER 0x56
313 #define CLIENT_OPCODE_DCP_MUTATION 0x57
314 #define CLIENT_OPCODE_DCP_DELETION 0x58
315 #define CLIENT_OPCODE_DCP_EXPIRATION 0x59
316 #define CLIENT_OPCODE_DCP_FLUSH 0x5a
317 #define CLIENT_OPCODE_DCP_SET_VBUCKET_STATE 0x5b
318 #define CLIENT_OPCODE_DCP_NOOP 0x5c
319 #define CLIENT_OPCODE_DCP_BUFFER_ACKNOWLEDGEMENT 0x5d
320 #define CLIENT_OPCODE_DCP_CONTROL 0x5e
321 #define CLIENT_OPCODE_DCP_SYSTEM_EVENT 0x5f
322 #define CLIENT_OPCODE_DCP_PREPARE 0x60
323 #define CLIENT_OPCODE_DCP_SEQNO_ACK 0x61
324 #define CLIENT_OPCODE_DCP_COMMIT 0x62
325 #define CLIENT_OPCODE_DCP_ABORT 0x63
326 #define CLIENT_OPCODE_DCP_SEQNO_ADVANCED 0x64
327 #define CLIENT_OPCODE_DCP_OSO_SNAPSHOT 0x65
329 /* Commands from EP (eventually persistent) and bucket engines */
330 #define CLIENT_OPCODE_STOP_PERSISTENCE 0x80
331 #define CLIENT_OPCODE_START_PERSISTENCE 0x81
332 #define CLIENT_OPCODE_SET_PARAM 0x82
333 #define CLIENT_OPCODE_GET_REPLICA 0x83
334 #define CLIENT_OPCODE_CREATE_BUCKET 0x85
335 #define CLIENT_OPCODE_DELETE_BUCKET 0x86
336 #define CLIENT_OPCODE_LIST_BUCKETS 0x87
337 #define CLIENT_OPCODE_EXPAND_BUCKET 0x88
338 #define CLIENT_OPCODE_SELECT_BUCKET 0x89
339 #define CLIENT_OPCODE_START_REPLICATION 0x90
340 #define CLIENT_OPCODE_OBSERVE_SEQNO 0x91
341 #define CLIENT_OPCODE_OBSERVE 0x92
342 #define CLIENT_OPCODE_EVICT_KEY 0x93
343 #define CLIENT_OPCODE_GET_LOCKED 0x94
344 #define CLIENT_OPCODE_UNLOCK_KEY 0x95
345 #define CLIENT_OPCODE_SYNC 0x96
346 #define CLIENT_OPCODE_LAST_CLOSED_CHECKPOINT 0x97
347 #define CLIENT_OPCODE_RESTORE_FILE 0x98
348 #define CLIENT_OPCODE_RESTORE_ABORT 0x99
349 #define CLIENT_OPCODE_RESTORE_COMPLETE 0x9a
350 #define CLIENT_OPCODE_ONLINE_UPDATE_START 0x9b
351 #define CLIENT_OPCODE_ONLINE_UPDATE_COMPLETE 0x9c
352 #define CLIENT_OPCODE_ONLINE_UPDATE_REVERT 0x9d
353 #define CLIENT_OPCODE_DEREGISTER_TAP_CLIENT 0x9e
354 #define CLIENT_OPCODE_RESET_REPLICATION_CHAIN 0x9f
355 #define CLIENT_OPCODE_GET_META 0xa0
356 #define CLIENT_OPCODE_GETQ_META 0xa1
357 #define CLIENT_OPCODE_SET_WITH_META 0xa2
358 #define CLIENT_OPCODE_SETQ_WITH_META 0xa3
359 #define CLIENT_OPCODE_ADD_WITH_META 0xa4
360 #define CLIENT_OPCODE_ADDQ_WITH_META 0xa5
361 #define CLIENT_OPCODE_SNAPSHOT_VB_STATES 0xa6
362 #define CLIENT_OPCODE_VBUCKET_BATCH_COUNT 0xa7
363 #define CLIENT_OPCODE_DEL_WITH_META 0xa8
364 #define CLIENT_OPCODE_DELQ_WITH_META 0xa9
365 #define CLIENT_OPCODE_CREATE_CHECKPOINT 0xaa
366 #define CLIENT_OPCODE_NOTIFY_VBUCKET_UPDATE 0xac
367 #define CLIENT_OPCODE_ENABLE_TRAFFIC 0xad
368 #define CLIENT_OPCODE_DISABLE_TRAFFIC 0xae
369 #define CLIENT_OPCODE_IFCONFIG 0xaf
370 #define CLIENT_OPCODE_CHANGE_VB_FILTER 0xb0
371 #define CLIENT_OPCODE_CHECKPOINT_PERSISTENCE 0xb1
372 #define CLIENT_OPCODE_RETURN_META 0xb2
373 #define CLIENT_OPCODE_COMPACT_DB 0xb3
376 #define CLIENT_OPCODE_SET_CLUSTER_CONFIG 0xb4
377 #define CLIENT_OPCODE_GET_CLUSTER_CONFIG 0xb5
378 #define CLIENT_OPCODE_GET_RANDOM_KEY 0xb6
379 #define CLIENT_OPCODE_SEQNO_PERSISTENCE 0xb7
380 #define CLIENT_OPCODE_GET_KEYS 0xb8
381 #define CLIENT_OPCODE_COLLECTIONS_SET_MANIFEST 0xb9
382 #define CLIENT_OPCODE_COLLECTIONS_GET_MANIFEST 0xba
383 #define CLIENT_OPCODE_COLLECTIONS_GET_ID 0xbb
384 #define CLIENT_OPCODE_COLLECTIONS_GET_SCOPE_ID 0xbc
386 #define CLIENT_OPCODE_SET_DRIFT_COUNTER_STATE 0xc1
387 #define CLIENT_OPCODE_GET_ADJUSTED_TIME 0xc2
389 /* Sub-document API commands */
390 #define CLIENT_OPCODE_SUBDOC_GET 0xc5
391 #define CLIENT_OPCODE_SUBDOC_EXISTS 0xc6
392 #define CLIENT_OPCODE_SUBDOC_DICT_ADD 0xc7
393 #define CLIENT_OPCODE_SUBDOC_DICT_UPSERT 0xc8
394 #define CLIENT_OPCODE_SUBDOC_DELETE 0xc9
395 #define CLIENT_OPCODE_SUBDOC_REPLACE 0xca
396 #define CLIENT_OPCODE_SUBDOC_ARRAY_PUSH_LAST 0xcb
397 #define CLIENT_OPCODE_SUBDOC_ARRAY_PUSH_FIRST 0xcc
398 #define CLIENT_OPCODE_SUBDOC_ARRAY_INSERT 0xcd
399 #define CLIENT_OPCODE_SUBDOC_ARRAY_ADD_UNIQUE 0xce
400 #define CLIENT_OPCODE_SUBDOC_COUNTER 0xcf
401 #define CLIENT_OPCODE_SUBDOC_MULTI_LOOKUP 0xd0
402 #define CLIENT_OPCODE_SUBDOC_MULTI_MUTATION 0xd1
403 #define CLIENT_OPCODE_SUBDOC_GET_COUNT 0xd2
404 #define CLIENT_OPCODE_SUBDOC_REPLACE_BODY_WITH_XATTR 0xd3
406 #define CLIENT_OPCODE_RANGE_SCAN_CREATE 0xda
407 #define CLIENT_OPCODE_RANGE_SCAN_CONTINUE 0xdb
408 #define CLIENT_OPCODE_RANGE_SCAN_CANCEL 0xdc
410 #define CLIENT_OPCODE_SCRUB 0xf0
411 #define CLIENT_OPCODE_ISASL_REFRESH 0xf1
412 #define CLIENT_OPCODE_SSL_CERTS_REFRESH 0xf2
413 #define CLIENT_OPCODE_GET_CMD_TIMER 0xf3
414 #define CLIENT_OPCODE_SET_CTRL_TOKEN 0xf4
415 #define CLIENT_OPCODE_GET_CTRL_TOKEN 0xf5
416 #define CLIENT_OPCODE_UPDATE_EXTERNAL_USER_PERMISSIONS 0xf6
417 #define CLIENT_OPCODE_RBAC_REFRESH 0xf7
418 #define CLIENT_OPCODE_AUTH_PROVIDER 0xf8
419 #define CLIENT_OPCODE_DROP_PRIVILEGE 0xfb
420 #define CLIENT_OPCODE_ADJUST_TIMEOFDAY 0xfc
421 #define CLIENT_OPCODE_EWOULDBLOCK_CTL 0xfd
422 #define CLIENT_OPCODE_GET_ERROR_MAP 0xfe
424 /* vBucket states */
425 #define VBUCKET_ACTIVE 0x01
426 #define VBUCKET_PENDING 0x02
427 #define VBUCKET_REPLICA 0x03
428 #define VBUCKET_DEAD 0x04
430 /* Data Types */
431 #define DT_RAW_BYTES 0x00
432 #define DT_JSON 0x01
433 #define DT_SNAPPY 0x02
434 #define DT_XATTR 0x04
436 void proto_register_couchbase(void);
437 void proto_reg_handoff_couchbase(void);
439 static int proto_couchbase;
441 static int hf_magic;
442 static int hf_opcode;
443 static int hf_server_opcode;
444 static int hf_extlength;
445 static int hf_keylength;
446 static int hf_value_length;
447 static int hf_datatype;
448 static int hf_datatype_json;
449 static int hf_datatype_snappy;
450 static int hf_datatype_xattr;
451 static int hf_vbucket;
452 static int hf_status;
453 static int hf_total_bodylength;
454 static int hf_opaque;
455 static int hf_cas;
456 static int hf_ttp;
457 static int hf_ttr;
458 static int hf_collection_key_id;
459 static int hf_collection_key_logical;
460 static int hf_collection_manifest_id;
462 static int hf_flex_extras_length;
463 static int hf_flex_keylength;
464 static int hf_extras;
465 static int hf_extras_flags;
466 static int hf_extras_flags_backfill;
467 static int hf_extras_flags_dump;
468 static int hf_extras_flags_list_vbuckets;
469 static int hf_extras_flags_takeover_vbuckets;
470 static int hf_extras_flags_support_ack;
471 static int hf_extras_flags_request_keys_only;
472 static int hf_extras_flags_checkpoint;
473 static int hf_extras_flags_dcp_connection_type;
474 static int hf_extras_flags_dcp_add_stream_takeover;
475 static int hf_extras_flags_dcp_add_stream_diskonly;
476 static int hf_extras_flags_dcp_add_stream_latest;
477 static int hf_extras_flags_dcp_snapshot_marker_memory;
478 static int hf_extras_flags_dcp_snapshot_marker_disk;
479 static int hf_extras_flags_dcp_snapshot_marker_chk;
480 static int hf_extras_flags_dcp_snapshot_marker_ack;
481 static int hf_extras_flags_dcp_snapshot_marker_history;
482 static int hf_extras_flags_dcp_snapshot_marker_may_contain_dups;
483 static int hf_extras_flags_dcp_include_xattrs;
484 static int hf_extras_flags_dcp_no_value;
485 static int hf_extras_flags_dcp_include_delete_times;
486 static int hf_extras_flags_dcp_collections;
487 static int hf_extras_flags_dcp_oso_snapshot_begin;
488 static int hf_extras_flags_dcp_oso_snapshot_end;
489 static int hf_subdoc_doc_flags;
490 static int hf_subdoc_doc_flags_mkdoc;
491 static int hf_subdoc_doc_flags_add;
492 static int hf_subdoc_doc_flags_accessdeleted;
493 static int hf_subdoc_doc_flags_createasdeleted;
494 static int hf_subdoc_doc_flags_revivedocument;
495 static int hf_subdoc_doc_flags_replicaread;
496 static int hf_subdoc_doc_flags_reserved;
497 static int hf_subdoc_flags;
498 static int hf_subdoc_flags_mkdirp;
499 static int hf_subdoc_flags_xattrpath;
500 static int hf_subdoc_flags_expandmacros;
501 static int hf_subdoc_flags_reserved;
502 static int hf_extras_seqno;
503 static int hf_extras_mutation_seqno;
504 static int hf_extras_opaque;
505 static int hf_extras_reserved;
506 static int hf_extras_start_seqno;
507 static int hf_extras_end_seqno;
508 static int hf_extras_high_completed_seqno;
509 static int hf_extras_max_visible_seqno;
510 static int hf_extras_timestamp;
511 static int hf_extras_marker_version;
512 static int hf_extras_vbucket_uuid;
513 static int hf_extras_snap_start_seqno;
514 static int hf_extras_snap_end_seqno;
515 static int hf_extras_expiration;
516 static int hf_extras_delta;
517 static int hf_extras_initial;
518 static int hf_extras_unknown;
519 static int hf_extras_by_seqno;
520 static int hf_extras_rev_seqno;
521 static int hf_extras_prepared_seqno;
522 static int hf_extras_commit_seqno;
523 static int hf_extras_abort_seqno;
524 static int hf_extras_deleted;
525 static int hf_extras_lock_time;
526 static int hf_extras_nmeta;
527 static int hf_extras_nru;
528 static int hf_extras_bytes_to_ack;
529 static int hf_extras_delete_time;
530 static int hf_extras_delete_unused;
531 static int hf_extras_system_event_id;
532 static int hf_extras_system_event_version;
533 static int hf_extras_pathlen;
534 static int hf_extras_dcp_oso_snapshot_flags;
535 static int hf_server_extras_cccp_epoch;
536 static int hf_server_extras_cccp_revno;
537 static int hf_server_clustermap_value;
538 static int hf_server_authentication;
539 static int hf_server_external_users;
540 static int hf_server_get_authorization;
542 static int hf_key;
543 static int hf_path;
544 static int hf_value;
545 static int hf_uint64_response;
546 static int hf_observe;
547 static int hf_observe_vbucket;
548 static int hf_observe_keylength;
549 static int hf_observe_key;
550 static int hf_observe_status;
551 static int hf_observe_cas;
552 static int hf_observe_vbucket_uuid;
553 static int hf_observe_failed_over;
554 static int hf_observe_last_persisted_seqno;
555 static int hf_observe_current_seqno;
556 static int hf_observe_old_vbucket_uuid;
557 static int hf_observe_last_received_seqno;
559 static int hf_get_errmap_version;
561 static int hf_failover_log;
562 static int hf_failover_log_size;
563 static int hf_failover_log_vbucket_uuid;
564 static int hf_failover_log_vbucket_seqno;
566 static int hf_vbucket_states;
567 static int hf_vbucket_states_state;
568 static int hf_vbucket_states_size;
569 static int hf_vbucket_states_id;
570 static int hf_vbucket_states_seqno;
572 static int hf_bucket_type;
573 static int hf_bucket_config;
574 static int hf_config_key;
575 static int hf_config_value;
577 static int hf_multipath_opcode;
578 static int hf_multipath_index;
579 static int hf_multipath_pathlen;
580 static int hf_multipath_path;
581 static int hf_multipath_valuelen;
582 static int hf_multipath_value;
584 static int hf_meta_flags;
585 static int hf_meta_expiration;
586 static int hf_meta_revseqno;
587 static int hf_meta_cas;
588 static int hf_skip_conflict;
589 static int hf_force_accept;
590 static int hf_regenerate_cas;
591 static int hf_force_meta;
592 static int hf_is_expiration;
593 static int hf_meta_options;
594 static int hf_metalen;
595 static int hf_meta_reqextmeta;
596 static int hf_meta_deleted;
597 static int hf_exptime;
598 static int hf_extras_meta_seqno;
599 static int hf_confres;
600 static int hf_hello_features;
601 static int hf_hello_features_feature;
603 static int hf_xattr_length;
604 static int hf_xattr_pair_length;
605 static int hf_xattr_key;
606 static int hf_xattr_value;
607 static int hf_xattrs;
609 static int hf_flex_extras;
610 static int hf_flex_extras_n;
611 static int hf_flex_frame_id_byte0;
612 static int hf_flex_frame_id_req;
613 static int hf_flex_frame_id_res;
614 static int hf_flex_frame_id_req_esc;
615 static int hf_flex_frame_id_res_esc;
616 static int hf_flex_frame_len;
617 static int hf_flex_frame_len_esc;
618 static int hf_flex_frame_tracing_duration;
619 static int hf_flex_frame_ru_count;
620 static int hf_flex_frame_wu_count;
621 static int hf_flex_frame_durability_req;
622 static int hf_flex_frame_dcp_stream_id;
623 static int hf_flex_frame_impersonated_user;
625 static int hf_range_scan_uuid;
626 static int hf_range_scan_item_limit;
627 static int hf_range_scan_time_limit;
628 static int hf_range_scan_byte_limit;
630 static expert_field ei_warn_shall_not_have_value;
631 static expert_field ei_warn_shall_not_have_extras;
632 static expert_field ei_warn_shall_not_have_key;
633 static expert_field ei_compression_error;
634 static expert_field ei_warn_unknown_flex_unsupported;
635 static expert_field ei_warn_unknown_flex_id;
636 static expert_field ei_warn_unknown_flex_len;
638 static expert_field ei_value_missing;
639 static expert_field ei_warn_must_have_extras;
640 static expert_field ei_warn_must_have_key;
641 static expert_field ei_warn_illegal_extras_length;
642 static expert_field ei_warn_illegal_value_length;
643 static expert_field ei_warn_unknown_magic_byte;
644 static expert_field ei_warn_unknown_opcode;
645 static expert_field ei_warn_unknown_extras;
646 static expert_field ei_note_status_code;
647 static expert_field ei_separator_not_found;
648 static expert_field ei_illegal_value;
650 static int ett_couchbase;
651 static int ett_extras;
652 static int ett_extras_flags;
653 static int ett_observe;
654 static int ett_failover_log;
655 static int ett_vbucket_states;
656 static int ett_multipath;
657 static int ett_config;
658 static int ett_config_key;
659 static int ett_hello_features;
660 static int ett_datatype;
661 static int ett_xattrs;
662 static int ett_xattr_pair;
663 static int ett_flex_frame_extras;
664 static int ett_collection_key;
666 static const value_string magic_vals[] = {
667 { MAGIC_CLIENT_REQUEST, "Request" },
668 { MAGIC_CLIENT_RESPONSE, "Response" },
669 { MAGIC_CLIENT_RESPONSE_FLEX, "Response with flexible framing extras" },
670 { MAGIC_CLIENT_REQUEST_FLEX, "Request with flexible framing extras" },
671 { MAGIC_SERVER_REQUEST, "Server Request"},
672 { MAGIC_SERVER_RESPONSE, "Server Response"},
673 { 0, NULL }
676 #define FLEX_ESCAPE 0x0F
679 The flex extension identifiers are different for request/response
680 i.e. 0 in a response is not 0 in a request
681 Response IDs
683 #define FLEX_RESPONSE_ID_RX_TX_DURATION 0
684 #define FLEX_RESPONSE_ID_RU_USAGE 1
685 #define FLEX_RESPONSE_ID_WU_USAGE 2
687 /* Request IDs */
688 #define FLEX_REQUEST_ID_REORDER 0
689 #define FLEX_REQUEST_ID_DURABILITY 1
690 #define FLEX_REQUEST_ID_DCP_STREAM_ID 2
691 #define FLEX_REQUEST_ID_OPEN_TRACING 3
692 #define FLEX_REQUEST_ID_IMPERSONATE 4
693 #define FLEX_REQUEST_ID_PRESERVE_TTL 5
695 static const value_string flex_frame_response_ids[] = {
696 { FLEX_RESPONSE_ID_RX_TX_DURATION, "Server Recv->Send duration"},
697 { FLEX_RESPONSE_ID_RU_USAGE, "Read units"},
698 { FLEX_RESPONSE_ID_WU_USAGE, "Write units"},
699 { 0, NULL }
702 static const value_string flex_frame_request_ids[] = {
703 { FLEX_REQUEST_ID_REORDER, "Out of order Execution"},
704 { FLEX_REQUEST_ID_DURABILITY, "Durability Requirements"},
705 { FLEX_REQUEST_ID_DCP_STREAM_ID, "DCP Stream Identifier"},
706 { FLEX_REQUEST_ID_OPEN_TRACING, "Open Tracing"},
707 { FLEX_REQUEST_ID_IMPERSONATE, "Impersonate User"},
708 { FLEX_REQUEST_ID_PRESERVE_TTL, "Preserve TTL"},
709 { 0, NULL }
712 static const value_string flex_frame_durability_req[] = {
713 { 1, "Majority"},
714 { 2, "Majority and persist on active"},
715 { 3, "Persist to majority"},
716 { 0, NULL }
719 static const value_string status_vals[] = {
720 { STATUS_SUCCESS, "Success" },
721 { STATUS_KEY_ENOENT, "Key not found" },
722 { STATUS_KEY_EEXISTS, "Key exists" },
723 { STATUS_E2BIG, "Value too big" },
724 { STATUS_EINVAL, "Invalid arguments" },
725 { STATUS_NOT_STORED, "Key not stored" },
726 { STATUS_DELTA_BADVAL, "Bad value to incr/decr" },
727 { STATUS_NOT_MY_VBUCKET, "Not my vBucket" },
728 { STATUS_NO_VBUCKET, "Not connected to a bucket" },
729 { STATUS_LOCKED, "The requested resource is locked" },
730 { STATUS_DCP_STREAM_NOT_FOUND, "No DCP Stream for this request" },
731 { STATUS_OPAQUE_NO_MATCH, "Opaque does not match" },
732 { STATUS_EWOULDTHROTTLE, "Command would have been throttled" },
733 { STATUS_ECONFIGONLY, "Command can't be executed in config-only bucket" },
734 { STATUS_NOT_LOCKED, "Unlock request for an unlocked document" },
735 { STATUS_AUTH_STALE, "Authentication context is stale. Should reauthenticate." },
736 { STATUS_AUTH_ERROR, "Authentication error" },
737 { STATUS_AUTH_CONTINUE, "Authentication continue" },
738 { STATUS_ERANGE, "Range error" },
739 { STATUS_ROLLBACK, "Rollback" },
740 { STATUS_EACCESS, "Access error" },
741 { STATUS_NOT_INITIALIZED,
742 "The Couchbase cluster is currently initializing this node, and "
743 "the Cluster manager has not yet granted all users access to the cluster."},
744 { STATUS_RATELIMITED_NETWORK_INGRESS, "Rate limit: Network ingress"},
745 { STATUS_RATELIMITED_NETWORK_EGRESS, "Rate limit: Network Egress"},
746 { STATUS_RATELIMITED_MAX_CONNECTIONS, "Rate limit: Max Connections"},
747 { STATUS_RATELIMITED_MAX_COMMANDS, "Rate limit: Max Commands"},
748 {STATUS_SCOPE_SIZE_LIMIT_EXCEEDED, "To much data in Scope"},
749 { STATUS_UNKNOWN_COMMAND, "Unknown command" },
750 { STATUS_ENOMEM, "Out of memory" },
751 { STATUS_NOT_SUPPORTED, "Command isn't supported" },
752 { STATUS_EINTERNAL, "Internal error" },
753 { STATUS_EBUSY, "Server is busy" },
754 { STATUS_ETMPFAIL, "Temporary failure" },
755 { STATUS_XATTR_EINVAL,
756 "There is something wrong with the syntax of the provided XATTR."},
757 { STATUS_UNKNOWN_COLLECTION,
758 "Operation attempted with an unknown collection."},
759 { STATUS_NO_COLLECTIONS_MANIFEST,
760 "No collections manifest has been set"},
761 { STATUS_CANNOT_APPLY_MANIFEST,
762 "Cannot apply the given manifest"},
763 { STATUS_MANIFEST_IS_AHEAD,
764 "Operation attempted with a manifest ahead of the server"},
765 { STATUS_UNKNOWN_SCOPE,
766 "Operation attempted with an unknown scope."},
767 { STATUS_DCP_STREAMID_INVALID,
768 "DCP Stream ID is invalid"},
769 { STATUS_DURABILITY_INVALID_LEVEL,
770 "The specified durability level is invalid" },
771 { STATUS_DURABILITY_IMPOSSIBLE,
772 "The specified durability requirements are not currently possible" },
773 { STATUS_SYNC_WRITE_IN_PROGRESS,
774 "A SyncWrite is already in progress on the specified key"},
775 { STATUS_SYNC_WRITE_AMBIGUOUS,
776 "The SyncWrite request has not completed in the specified time and has ambiguous result"},
777 { STATUS_SYNC_WRITE_RECOMMIT_IN_PROGRESS,
778 "The SyncWrite is being re-committed after a change in active node"},
779 { STATUS_RANGE_SCAN_CANCELLED, "RangeScan was cancelled"},
780 { STATUS_RANGE_SCAN_MORE, "RangeScan has more data available"},
781 { STATUS_RANGE_SCAN_COMPLETE, "RangeScan has completed"},
782 { STATUS_VBUUID_NOT_EQUAL, "VB UUID does not equal server value"},
783 { STATUS_SUBDOC_PATH_ENOENT,
784 "Subdoc: Path not does not exist"},
785 { STATUS_SUBDOC_PATH_MISMATCH,
786 "Subdoc: Path mismatch"},
787 { STATUS_SUBDOC_PATH_EINVAL,
788 "Subdoc: Invalid path"},
789 { STATUS_SUBDOC_PATH_E2BIG,
790 "Subdoc: Path too large"},
791 { STATUS_SUBDOC_DOC_E2DEEP,
792 "Subdoc: Document too deep"},
793 { STATUS_SUBDOC_VALUE_CANTINSERT,
794 "Subdoc: Cannot insert specified value"},
795 { STATUS_SUBDOC_DOC_NOTJSON,
796 "Subdoc: Existing document not JSON"},
797 { STATUS_SUBDOC_NUM_ERANGE,
798 "Subdoc: Existing number outside valid arithmetic range"},
799 { STATUS_SUBDOC_DELTA_ERANGE,
800 "Subdoc: Delta outside valid arithmetic range"},
801 { STATUS_SUBDOC_PATH_EEXISTS,
802 "Subdoc: Document path already exists"},
803 { STATUS_SUBDOC_VALUE_ETOODEEP,
804 "Subdoc: Inserting value would make document too deep"},
805 { STATUS_SUBDOC_INVALID_COMBO,
806 "Subdoc: Invalid combination for multi-path command"},
807 { STATUS_SUBDOC_MULTI_PATH_FAILURE,
808 "Subdoc: One or more paths in a multi-path command failed"},
809 { STATUS_SUBDOC_SUCCESS_DELETED,
810 "Subdoc: The operation completed successfully, but operated on a deleted document."},
811 { STATUS_SUBDOC_XATTR_INVALID_FLAG_COMBO,
812 "Subdoc: The combination of the subdoc flags for the xattrs doesn't make any sense."},
813 { STATUS_SUBDOC_XATTR_INVALID_KEY_COMBO,
814 "Subdoc: Only a single xattr key may be accessed at the same time."},
815 { STATUS_SUBDOC_XATTR_UNKNOWN_MACRO,
816 "Subdoc: The server has no knowledge of the requested macro."},
817 { STATUS_SUBDOC_XATTR_UNKNOWN_VATTR,
818 "Subdoc: The server has no knowledge of the requested virtual xattr."},
819 { STATUS_SUBDOC_XATTR_CANT_MODIFY_VATTR,
820 "Subdoc: Virtual xattrs can't be modified."},
821 { STATUS_SUBDOC_MULTI_PATH_FAILURE_DELETED,
822 "Subdoc: Specified key was found as a deleted document, but one or more path operations failed."},
823 { STATUS_SUBDOC_INVALID_XATTR_ORDER,
824 "Subdoc: According to the spec all xattr commands should come first, followed by the commands for the document body."},
825 { STATUS_SUBDOC_XATTR_UNKNOWN_VATTR_MACRO,
826 "Subdoc: The server does not know about this virtual macro."},
827 { STATUS_SUBDOC_CAN_ONLY_REVIVE_DELETED_DOCUMENTS,
828 "Subdoc: The document isn't dead (and we wanted to revive the document)."},
829 { STATUS_SUBDOC_DELETED_DOCUMENT_CANT_HAVE_VALUE,
830 "Subdoc: A deleted document can't have a user value."},
831 { 0, NULL }
834 static value_string_ext status_vals_ext = VALUE_STRING_EXT_INIT(status_vals);
836 static const value_string client_opcode_vals[] = {
837 { CLIENT_OPCODE_GET, "Get" },
838 { CLIENT_OPCODE_SET, "Set" },
839 { CLIENT_OPCODE_ADD, "Add" },
840 { CLIENT_OPCODE_REPLACE, "Replace" },
841 { CLIENT_OPCODE_DELETE, "Delete" },
842 { CLIENT_OPCODE_INCREMENT, "Increment" },
843 { CLIENT_OPCODE_DECREMENT, "Decrement" },
844 { CLIENT_OPCODE_QUIT, "Quit" },
845 { CLIENT_OPCODE_FLUSH, "Flush" },
846 { CLIENT_OPCODE_GETQ, "Get Quietly" },
847 { CLIENT_OPCODE_NOOP, "NOOP" },
848 { CLIENT_OPCODE_VERSION, "Version" },
849 { CLIENT_OPCODE_GETK, "Get Key" },
850 { CLIENT_OPCODE_GETKQ, "Get Key Quietly" },
851 { CLIENT_OPCODE_APPEND, "Append" },
852 { CLIENT_OPCODE_PREPEND, "Prepend" },
853 { CLIENT_OPCODE_STAT, "Statistics" },
854 { CLIENT_OPCODE_SETQ, "Set Quietly" },
855 { CLIENT_OPCODE_ADDQ, "Add Quietly" },
856 { CLIENT_OPCODE_REPLACEQ, "Replace Quietly" },
857 { CLIENT_OPCODE_DELETEQ, "Delete Quietly" },
858 { CLIENT_OPCODE_INCREMENTQ, "Increment Quietly" },
859 { CLIENT_OPCODE_DECREMENTQ, "Decrement Quietly" },
860 { CLIENT_OPCODE_QUITQ, "Quit Quietly" },
861 { CLIENT_OPCODE_FLUSHQ, "Flush Quietly" },
862 { CLIENT_OPCODE_APPENDQ, "Append Quietly" },
863 { CLIENT_OPCODE_PREPENDQ, "Prepend Quietly" },
864 { CLIENT_OPCODE_VERBOSITY, "Verbosity" },
865 { CLIENT_OPCODE_TOUCH, "Touch" },
866 { CLIENT_OPCODE_GAT, "Get and Touch" },
867 { CLIENT_OPCODE_GATQ, "Gat and Touch Quietly" },
868 { CLIENT_OPCODE_HELLO, "Hello" },
869 { CLIENT_OPCODE_SASL_LIST_MECHS, "List SASL Mechanisms" },
870 { CLIENT_OPCODE_SASL_AUTH, "SASL Authenticate" },
871 { CLIENT_OPCODE_SASL_STEP, "SASL Step" },
872 { CLIENT_OPCODE_IOCTL_GET, "IOCTL Get" },
873 { CLIENT_OPCODE_IOCTL_SET, "IOCTL Set" },
874 { CLIENT_OPCODE_CONFIG_VALIDATE, "Config Validate" },
875 { CLIENT_OPCODE_CONFIG_RELOAD, "Config Reload" },
876 { CLIENT_OPCODE_AUDIT_PUT, "Audit Put" },
877 { CLIENT_OPCODE_AUDIT_CONFIG_RELOAD, "Audit Config Reload" },
878 { CLIENT_OPCODE_SHUTDOWN, "Shutdown" },
879 { CLIENT_OPCODE_RGET, "Range Get" },
880 { CLIENT_OPCODE_RSET, "Range Set" },
881 { CLIENT_OPCODE_RSETQ, "Range Set Quietly" },
882 { CLIENT_OPCODE_RAPPEND, "Range Append" },
883 { CLIENT_OPCODE_RAPPENDQ, "Range Append Quietly" },
884 { CLIENT_OPCODE_RPREPEND, "Range Prepend" },
885 { CLIENT_OPCODE_RPREPENDQ, "Range Prepend Quietly" },
886 { CLIENT_OPCODE_RDELETE, "Range Delete" },
887 { CLIENT_OPCODE_RDELETEQ, "Range Delete Quietly" },
888 { CLIENT_OPCODE_RINCR, "Range Increment" },
889 { CLIENT_OPCODE_RINCRQ, "Range Increment Quietly" },
890 { CLIENT_OPCODE_RDECR, "Range Decrement" },
891 { CLIENT_OPCODE_RDECRQ, "Range Decrement Quietly" },
892 { CLIENT_OPCODE_SET_VBUCKET, "Set VBucket" },
893 { CLIENT_OPCODE_GET_VBUCKET, "Get VBucket" },
894 { CLIENT_OPCODE_DEL_VBUCKET, "Delete VBucket" },
895 { CLIENT_OPCODE_TAP_CONNECT, "TAP Connect" },
896 { CLIENT_OPCODE_TAP_MUTATION, "TAP Mutation" },
897 { CLIENT_OPCODE_TAP_DELETE, "TAP Delete" },
898 { CLIENT_OPCODE_TAP_FLUSH, "TAP Flush" },
899 { CLIENT_OPCODE_TAP_OPAQUE, "TAP Opaque" },
900 { CLIENT_OPCODE_TAP_VBUCKET_SET, "TAP VBucket Set" },
901 { CLIENT_OPCODE_TAP_CHECKPOINT_START, "TAP Checkpoint Start" },
902 { CLIENT_OPCODE_TAP_CHECKPOINT_END, "TAP Checkpoint End" },
903 { CLIENT_OPCODE_GET_ALL_VB_SEQNOS, "Get All VBucket Seqnos" },
904 { CLIENT_OPCODE_DCP_OPEN_CONNECTION, "DCP Open Connection" },
905 { CLIENT_OPCODE_DCP_ADD_STREAM, "DCP Add Stream" },
906 { CLIENT_OPCODE_DCP_CLOSE_STREAM, "DCP Close Stream" },
907 { CLIENT_OPCODE_DCP_STREAM_REQUEST, "DCP Stream Request" },
908 { CLIENT_OPCODE_DCP_FAILOVER_LOG_REQUEST, "DCP Get Failover Log" },
909 { CLIENT_OPCODE_DCP_STREAM_END, "DCP Stream End" },
910 { CLIENT_OPCODE_DCP_SNAPSHOT_MARKER, "DCP Snapshot Marker" },
911 { CLIENT_OPCODE_DCP_MUTATION, "DCP (Key) Mutation" },
912 { CLIENT_OPCODE_DCP_DELETION, "DCP (Key) Deletion" },
913 { CLIENT_OPCODE_DCP_EXPIRATION, "DCP (Key) Expiration" },
914 { CLIENT_OPCODE_DCP_FLUSH, "DCP Flush" },
915 { CLIENT_OPCODE_DCP_SET_VBUCKET_STATE, "DCP Set VBucket State" },
916 { CLIENT_OPCODE_DCP_NOOP, "DCP NOOP" },
917 { CLIENT_OPCODE_DCP_BUFFER_ACKNOWLEDGEMENT, "DCP Buffer Acknowledgement"},
918 { CLIENT_OPCODE_DCP_CONTROL, "DCP Control" },
919 { CLIENT_OPCODE_DCP_SYSTEM_EVENT, "DCP System Event" },
920 { CLIENT_OPCODE_DCP_PREPARE, "DCP Prepare" },
921 { CLIENT_OPCODE_DCP_SEQNO_ACK, "DCP Seqno Acknowledgement"},
922 { CLIENT_OPCODE_DCP_COMMIT, "DCP Commit" },
923 { CLIENT_OPCODE_DCP_ABORT, "DCP Abort" },
924 { CLIENT_OPCODE_DCP_SEQNO_ADVANCED, "DCP Seqno Advanced" },
925 { CLIENT_OPCODE_DCP_OSO_SNAPSHOT, "DCP Out of Sequence Order Snapshot"},
926 { CLIENT_OPCODE_STOP_PERSISTENCE, "Stop Persistence" },
927 { CLIENT_OPCODE_START_PERSISTENCE, "Start Persistence" },
928 { CLIENT_OPCODE_SET_PARAM, "Set Parameter" },
929 { CLIENT_OPCODE_GET_REPLICA, "Get Replica" },
930 { CLIENT_OPCODE_CREATE_BUCKET, "Create Bucket" },
931 { CLIENT_OPCODE_DELETE_BUCKET, "Delete Bucket" },
932 { CLIENT_OPCODE_LIST_BUCKETS, "List Buckets" },
933 { CLIENT_OPCODE_EXPAND_BUCKET, "Expand Bucket" },
934 { CLIENT_OPCODE_SELECT_BUCKET, "Select Bucket" },
935 { CLIENT_OPCODE_START_REPLICATION, "Start Replication" },
936 { CLIENT_OPCODE_OBSERVE_SEQNO, "Observe Sequence Number" },
937 { CLIENT_OPCODE_OBSERVE, "Observe" },
938 { CLIENT_OPCODE_EVICT_KEY, "Evict Key" },
939 { CLIENT_OPCODE_GET_LOCKED, "Get Locked" },
940 { CLIENT_OPCODE_UNLOCK_KEY, "Unlock Key" },
941 { CLIENT_OPCODE_SYNC, "Sync" },
942 { CLIENT_OPCODE_LAST_CLOSED_CHECKPOINT, "Last Closed Checkpoint" },
943 { CLIENT_OPCODE_RESTORE_FILE, "Restore File" },
944 { CLIENT_OPCODE_RESTORE_ABORT, "Restore Abort" },
945 { CLIENT_OPCODE_RESTORE_COMPLETE, "Restore Complete" },
946 { CLIENT_OPCODE_ONLINE_UPDATE_START, "Online Update Start" },
947 { CLIENT_OPCODE_ONLINE_UPDATE_COMPLETE, "Online Update Complete" },
948 { CLIENT_OPCODE_ONLINE_UPDATE_REVERT, "Online Update Revert" },
949 { CLIENT_OPCODE_DEREGISTER_TAP_CLIENT, "Deregister TAP Client" },
950 { CLIENT_OPCODE_RESET_REPLICATION_CHAIN, "Reset Replication Chain" },
951 { CLIENT_OPCODE_GET_META, "Get Meta" },
952 { CLIENT_OPCODE_GETQ_META, "Get Meta Quietly" },
953 { CLIENT_OPCODE_SET_WITH_META, "Set with Meta" },
954 { CLIENT_OPCODE_SETQ_WITH_META, "Set with Meta Quietly" },
955 { CLIENT_OPCODE_ADD_WITH_META, "Add with Meta" },
956 { CLIENT_OPCODE_ADDQ_WITH_META, "Add with Meta Quietly" },
957 { CLIENT_OPCODE_SNAPSHOT_VB_STATES, "Snapshot VBuckets States" },
958 { CLIENT_OPCODE_VBUCKET_BATCH_COUNT, "VBucket Batch Count" },
959 { CLIENT_OPCODE_DEL_WITH_META, "Delete with Meta" },
960 { CLIENT_OPCODE_DELQ_WITH_META, "Delete with Meta Quietly" },
961 { CLIENT_OPCODE_CREATE_CHECKPOINT, "Create Checkpoint" },
962 { CLIENT_OPCODE_NOTIFY_VBUCKET_UPDATE, "Notify VBucket Update" },
963 { CLIENT_OPCODE_ENABLE_TRAFFIC, "Enable Traffic" },
964 { CLIENT_OPCODE_DISABLE_TRAFFIC, "Disable Traffic" },
965 { CLIENT_OPCODE_IFCONFIG, "Ifconfig" },
966 { CLIENT_OPCODE_CHANGE_VB_FILTER, "Change VBucket Filter" },
967 { CLIENT_OPCODE_CHECKPOINT_PERSISTENCE, "Checkpoint Persistence" },
968 { CLIENT_OPCODE_RETURN_META, "Return Meta" },
969 { CLIENT_OPCODE_COMPACT_DB, "Compact Database" },
970 { CLIENT_OPCODE_SET_CLUSTER_CONFIG, "Set Cluster Config" },
971 { CLIENT_OPCODE_GET_CLUSTER_CONFIG, "Get Cluster Config" },
972 { CLIENT_OPCODE_GET_RANDOM_KEY, "Get Random Key" },
973 { CLIENT_OPCODE_SEQNO_PERSISTENCE, "Seqno Persistence" },
974 { CLIENT_OPCODE_GET_KEYS, "Get Keys" },
975 { CLIENT_OPCODE_COLLECTIONS_SET_MANIFEST, "Set Collection's Manifest" },
976 { CLIENT_OPCODE_COLLECTIONS_GET_MANIFEST, "Get Collection's Manifest" },
977 { CLIENT_OPCODE_COLLECTIONS_GET_ID, "Get Collection ID" },
978 { CLIENT_OPCODE_COLLECTIONS_GET_SCOPE_ID, "Get Scope ID" },
979 { CLIENT_OPCODE_SET_DRIFT_COUNTER_STATE, "Set Drift Counter State" },
980 { CLIENT_OPCODE_GET_ADJUSTED_TIME, "Get Adjusted Time" },
981 { CLIENT_OPCODE_SUBDOC_GET, "Subdoc Get" },
982 { CLIENT_OPCODE_SUBDOC_EXISTS, "Subdoc Exists" },
983 { CLIENT_OPCODE_SUBDOC_DICT_ADD, "Subdoc Dictionary Add" },
984 { CLIENT_OPCODE_SUBDOC_DICT_UPSERT, "Subdoc Dictionary Upsert" },
985 { CLIENT_OPCODE_SUBDOC_DELETE, "Subdoc Delete" },
986 { CLIENT_OPCODE_SUBDOC_REPLACE, "Subdoc Replace" },
987 { CLIENT_OPCODE_SUBDOC_ARRAY_PUSH_LAST, "Subdoc Array Push Last" },
988 { CLIENT_OPCODE_SUBDOC_ARRAY_PUSH_FIRST, "Subdoc Array Push First" },
989 { CLIENT_OPCODE_SUBDOC_ARRAY_INSERT, "Subdoc Array Insert" },
990 { CLIENT_OPCODE_SUBDOC_ARRAY_ADD_UNIQUE, "Subdoc Array Add Unique" },
991 { CLIENT_OPCODE_SUBDOC_COUNTER, "Subdoc Counter" },
992 { CLIENT_OPCODE_SUBDOC_MULTI_LOOKUP, "Subdoc Multipath Lookup" },
993 { CLIENT_OPCODE_SUBDOC_MULTI_MUTATION, "Subdoc Multipath Mutation"},
994 { CLIENT_OPCODE_SUBDOC_GET_COUNT, "Subdoc Get Count" },
995 { CLIENT_OPCODE_SUBDOC_REPLACE_BODY_WITH_XATTR, "Subdoc Replace Body With Xattr"},
996 { CLIENT_OPCODE_RANGE_SCAN_CREATE, "RangeScan Create" },
997 { CLIENT_OPCODE_RANGE_SCAN_CONTINUE, "RangeScan Continue" },
998 { CLIENT_OPCODE_RANGE_SCAN_CANCEL, "RangeScan Cancel" },
999 { CLIENT_OPCODE_SCRUB, "Scrub" },
1000 { CLIENT_OPCODE_ISASL_REFRESH, "isasl Refresh" },
1001 { CLIENT_OPCODE_SSL_CERTS_REFRESH, "SSL Certificates Refresh" },
1002 { CLIENT_OPCODE_GET_CMD_TIMER, "Internal Timer Control" },
1003 { CLIENT_OPCODE_SET_CTRL_TOKEN, "Set Control Token" },
1004 { CLIENT_OPCODE_GET_CTRL_TOKEN, "Get Control Token" },
1005 { CLIENT_OPCODE_UPDATE_EXTERNAL_USER_PERMISSIONS, "Update External User Permissions"},
1006 { CLIENT_OPCODE_RBAC_REFRESH, "RBAC Refresh" },
1007 { CLIENT_OPCODE_AUTH_PROVIDER, "Auth Provider" },
1008 { CLIENT_OPCODE_DROP_PRIVILEGE, "Drop Privilege" },
1009 { CLIENT_OPCODE_ADJUST_TIMEOFDAY, "Adjust Timeofday" },
1010 { CLIENT_OPCODE_EWOULDBLOCK_CTL, "EWOULDBLOCK Control" },
1011 { CLIENT_OPCODE_GET_ERROR_MAP, "Get Error Map" },
1013 /* Internally defined values not valid here */
1014 { 0, NULL }
1017 static value_string_ext client_opcode_vals_ext = VALUE_STRING_EXT_INIT(client_opcode_vals);
1019 typedef enum {
1020 SERVER_OPCODE_CLUSTERMAP_CHANGE_NOTIFICATION = 0x01,
1021 SERVER_OPCODE_AUTHENTICATE = 0x02,
1022 SERVER_OPCODE_ACTIVE_EXTERNAL_USERS = 0x03,
1023 SERVER_OPCODE_GET_AUTHORIZATION = 0x04
1024 } server_opcode_t;
1026 static const value_string server_opcode_vals[] = {
1027 { SERVER_OPCODE_CLUSTERMAP_CHANGE_NOTIFICATION, "ClustermapChangeNotification"},
1028 { SERVER_OPCODE_AUTHENTICATE, "Authenticate"},
1029 { SERVER_OPCODE_ACTIVE_EXTERNAL_USERS, "ActiveExternalUsers"},
1030 { SERVER_OPCODE_GET_AUTHORIZATION, "GetAuthorization"},
1031 {0, NULL}
1033 static value_string_ext server_opcode_vals_ext = VALUE_STRING_EXT_INIT(server_opcode_vals);
1035 static const value_string dcp_connection_type_vals[] = {
1036 {0, "Consumer"},
1037 {1, "Producer"},
1038 {2, "Notifier"},
1039 {0, NULL}
1042 static const value_string vbucket_states_vals[] = {
1043 {1, "Active"},
1044 {2, "Replica"},
1045 {3, "Pending"},
1046 {4, "Dead"},
1047 {0, NULL}
1050 static int * const datatype_vals[] = {
1051 &hf_datatype_json,
1052 &hf_datatype_snappy,
1053 &hf_datatype_xattr,
1054 NULL
1057 static int * const subdoc_flags[] = {
1058 &hf_subdoc_flags_mkdirp,
1059 &hf_subdoc_flags_xattrpath,
1060 &hf_subdoc_flags_expandmacros,
1061 &hf_subdoc_flags_reserved,
1062 NULL
1065 static int * const subdoc_doc_flags[] = {
1066 &hf_subdoc_doc_flags_mkdoc,
1067 &hf_subdoc_doc_flags_add,
1068 &hf_subdoc_doc_flags_accessdeleted,
1069 &hf_subdoc_doc_flags_createasdeleted,
1070 &hf_subdoc_doc_flags_revivedocument,
1071 &hf_subdoc_doc_flags_replicaread,
1072 &hf_subdoc_doc_flags_reserved,
1073 NULL
1076 static int * const set_with_meta_extra_flags[] = {
1077 &hf_force_meta,
1078 &hf_force_accept,
1079 &hf_regenerate_cas,
1080 &hf_skip_conflict,
1081 NULL
1084 static int * const del_with_meta_extra_flags[] = {
1085 &hf_force_meta,
1086 &hf_force_accept,
1087 &hf_regenerate_cas,
1088 &hf_skip_conflict,
1089 &hf_is_expiration,
1090 NULL
1093 static const value_string feature_vals[] = {
1094 {0x01, "Datatype (deprecated)"},
1095 {0x02, "TLS"},
1096 {0x03, "TCP Nodelay"},
1097 {0x04, "Mutation Seqno"},
1098 {0x05, "TCP Delay"},
1099 {0x06, "XATTR"},
1100 {0x07, "Error Map"},
1101 {0x08, "Select Bucket"},
1102 {0x09, "Collections (deprecated)"},
1103 {0x0a, "Snappy"},
1104 {0x0b, "JSON"},
1105 {0x0c, "Duplex"},
1106 {0x0d, "Clustermap Change Notification"},
1107 {0x0e, "Unordered Execution"},
1108 {0x0f, "Tracing"},
1109 {0x10, "AltRequestSupport"},
1110 {0x11, "SyncReplication"},
1111 {0x12, "Collections"},
1112 {0x13, "OpenTracing"},
1113 {0x14, "PreserveTtl"},
1114 {0x15, "VAttr"},
1115 {0x16, "Point in Time Recovery"},
1116 {0x17, "SubdocCreateAsDeleted"},
1117 {0x18, "SubdocDocumentMacroSupport"},
1118 {0x19, "SubdocReplaceBodyWithXattr"},
1119 {0x1a, "ReportUnitUsage"},
1120 {0x1b, "NonBlockingThrottlingMode"},
1121 {0x1c, "SubdocReplicaRead"},
1122 {0x1d, "GetClusterConfigWithKnownVersion"},
1123 {0x1e, "DedupeNotMyVbucketClustermap"},
1124 {0x1f, "ClustermapChangeNotificationBrief"},
1125 {0x20, "SubdocAllowsAccessOnMultipleXattrKeys"},
1126 {0, NULL}
1129 static const value_string dcp_system_event_id_vals [] = {
1130 {0, "CreateCollection"},
1131 {1, "DropCollection"},
1132 {2, "Unused"},
1133 {3, "CreateScope"},
1134 {4, "DropScope"},
1135 {5, "ModifyCollection"},
1136 {0, NULL}
1139 static int * const snapshot_marker_flags [] = {
1140 &hf_extras_flags_dcp_snapshot_marker_memory,
1141 &hf_extras_flags_dcp_snapshot_marker_disk,
1142 &hf_extras_flags_dcp_snapshot_marker_chk,
1143 &hf_extras_flags_dcp_snapshot_marker_ack,
1144 &hf_extras_flags_dcp_snapshot_marker_history,
1145 &hf_extras_flags_dcp_snapshot_marker_may_contain_dups,
1146 NULL
1149 static dissector_handle_t couchbase_handle;
1150 static dissector_handle_t json_handle;
1152 /* desegmentation of COUCHBASE payload */
1153 static bool couchbase_desegment_body = true;
1154 static unsigned couchbase_ssl_port = 11207;
1155 static unsigned couchbase_ssl_port_pref = 11207;
1157 /** Read out the magic byte (located at offset 0 in the header) */
1158 static uint8_t get_magic(tvbuff_t *tvb) {
1159 return tvb_get_uint8(tvb, 0);
1162 /** Read out the opcode (located at offset 1 in the header) */
1163 static uint8_t get_opcode(tvbuff_t *tvb) {
1164 return tvb_get_uint8(tvb, 1);
1167 /** Read out the status code from the header (only "valid" for response packets) */
1168 static uint16_t get_status(tvbuff_t *tvb) {
1169 return tvb_get_ntohs(tvb, 6);
1172 /** Read out flex size (using the upper bits of the key length when using flex encoding) */
1173 static uint8_t get_flex_framing_extras_length(tvbuff_t *tvb) {
1174 if (is_flex_encoded(get_magic(tvb))) {
1175 return tvb_get_uint8(tvb, 2);
1177 return 0;
1180 /** Read out the size of the extras section (located at offset 4) */
1181 static uint8_t get_extras_length(tvbuff_t *tvb) {
1182 return tvb_get_uint8(tvb, 4);
1185 /** Read out the datatype section (located at offset 5) */
1186 static uint8_t get_datatype(tvbuff_t *tvb) {
1187 return tvb_get_uint8(tvb, 5);
1190 /** Read out the length of the key (1 or 2 bytes depending on the encoding) */
1191 static uint16_t get_key_length(tvbuff_t *tvb) {
1192 if (is_flex_encoded(get_magic(tvb))) {
1193 return tvb_get_uint8(tvb, 3);
1195 return tvb_get_ntohs(tvb, 2);
1198 /** Read out the size for the rest of the frame data */
1199 static uint32_t get_body_length(tvbuff_t *tvb) {
1200 return tvb_get_ntohl(tvb, 8);
1203 /* Returns true if the specified opcode's response value is JSON. */
1204 static bool
1205 has_json_value(bool is_request, uint8_t opcode)
1207 if (is_request) {
1208 switch (opcode) {
1209 case CLIENT_OPCODE_AUDIT_PUT:
1210 case CLIENT_OPCODE_RANGE_SCAN_CREATE:
1211 return true;
1213 default:
1214 return false;
1216 } else {
1217 switch (opcode) {
1218 case CLIENT_OPCODE_GET_CLUSTER_CONFIG:
1219 case CLIENT_OPCODE_SUBDOC_GET:
1220 case CLIENT_OPCODE_COLLECTIONS_GET_MANIFEST:
1221 case CLIENT_OPCODE_COLLECTIONS_SET_MANIFEST:
1222 return true;
1224 default:
1225 return false;
1230 static void dissect_dcp_xattrs(tvbuff_t *tvb, proto_tree *tree,
1231 uint32_t value_len, int offset,
1232 packet_info *pinfo) {
1233 uint32_t xattr_size, pair_len;
1234 int mark;
1235 proto_tree *xattr_tree, *pair_tree;
1236 proto_item *ti;
1238 proto_tree_add_item_ret_uint(tree, hf_xattr_length, tvb, offset, 4, ENC_BIG_ENDIAN, &xattr_size);
1239 value_len = value_len - (xattr_size + 4);
1240 offset += 4;
1242 ti = proto_tree_add_item(tree, hf_xattrs, tvb, offset, xattr_size, ENC_NA);
1243 xattr_tree = proto_item_add_subtree(ti, ett_xattrs);
1245 while (xattr_size > 0) {
1247 ti = proto_tree_add_item_ret_uint(xattr_tree, hf_xattr_pair_length, tvb, offset, 4, ENC_BIG_ENDIAN, &pair_len);
1248 pair_tree = proto_item_add_subtree(ti, ett_xattr_pair);
1249 offset += 4;
1250 xattr_size -= 4;
1252 mark = tvb_find_uint8(tvb, offset, pair_len, 0x00);
1253 if (mark == -1) {
1254 expert_add_info_format(pinfo, ti, &ei_separator_not_found, "Null byte not found");
1255 return;
1258 ti = proto_tree_add_item(pair_tree, hf_xattr_key, tvb, offset, mark - offset, ENC_ASCII | ENC_NA);
1259 xattr_size -= (mark - offset) + 1;
1260 pair_len -= (mark - offset) + 1;
1261 offset = mark + 1;
1263 mark = tvb_find_uint8(tvb, offset, pair_len, 0x00);
1264 if (mark == -1) {
1265 expert_add_info_format(pinfo, ti, &ei_separator_not_found, "Null byte not found");
1266 return;
1269 proto_tree_add_item(pair_tree, hf_xattr_value, tvb, offset, mark - offset, ENC_ASCII | ENC_NA);
1270 xattr_size -= (mark - offset) + 1;
1271 offset = mark + 1;
1274 //The regular value
1275 proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII | ENC_NA);
1278 /* Dissects the required extras for subdoc single-path packets */
1279 static void
1280 dissect_subdoc_spath_required_extras(tvbuff_t *tvb, proto_tree *extras_tree,
1281 uint8_t extlen, bool request, int* offset,
1282 uint16_t *path_len, bool *illegal)
1284 if (request) {
1285 if (extlen >= 3) {
1286 *path_len = tvb_get_ntohs(tvb, *offset);
1287 proto_tree_add_item(extras_tree, hf_extras_pathlen, tvb, *offset, 2,
1288 ENC_BIG_ENDIAN);
1289 *offset += 2;
1291 proto_tree_add_bitmask(extras_tree, tvb, *offset, hf_subdoc_flags,
1292 ett_extras_flags, subdoc_flags, ENC_BIG_ENDIAN);
1293 *offset += 1;
1294 } else {
1295 /* Must always have at least 3 bytes of extras */
1296 *illegal = true;
1301 static void dissect_server_request_extras(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree, int offset, uint8_t extlen, uint8_t opcode) {
1302 if (extlen == 0) {
1303 switch (opcode) {
1304 case SERVER_OPCODE_CLUSTERMAP_CHANGE_NOTIFICATION:
1305 proto_tree_add_expert_format(tree, pinfo, &ei_warn_must_have_extras, tvb, offset, 0,
1306 "ClustermapChangeNotification request must have extras");
1307 return;
1309 case SERVER_OPCODE_GET_AUTHORIZATION:
1310 case SERVER_OPCODE_AUTHENTICATE:
1311 case SERVER_OPCODE_ACTIVE_EXTERNAL_USERS:
1312 // Success! none of these commands use extras
1314 default:
1315 // Probably ok as we don't know about the opcode
1316 return;
1321 proto_item *extras_item = proto_tree_add_item(tree, hf_extras, tvb, offset, extlen, ENC_NA);
1322 proto_tree *extras_tree = proto_item_add_subtree(extras_item, ett_extras);
1324 if (opcode == SERVER_OPCODE_CLUSTERMAP_CHANGE_NOTIFICATION) {
1325 // Expected 16 bytes of extras!
1326 if (extlen < 16) {
1327 proto_tree_add_expert_format(extras_tree, pinfo,
1328 &ei_warn_illegal_extras_length, tvb,
1329 offset, extlen,
1330 "ClustermapChangeNotification should have 16 bytes of extras");
1331 return;
1334 proto_tree_add_item(extras_tree, hf_server_extras_cccp_epoch, tvb, offset, 8, ENC_BIG_ENDIAN);
1335 offset += 8;
1336 proto_tree_add_item(extras_tree, hf_server_extras_cccp_revno, tvb, offset, 8, ENC_BIG_ENDIAN);
1338 if (extlen > 16) {
1339 proto_tree_add_expert_format(extras_tree, pinfo,
1340 &ei_warn_illegal_extras_length, tvb,
1341 offset + 16, extlen - 16,
1342 "Unexpected amount of extras");
1344 return;
1347 // we don't know how to decode this!
1348 proto_tree_add_item(extras_tree, hf_extras_unknown, tvb, offset, extlen, ENC_NA);
1351 static void
1352 dissect_server_response_extras(tvbuff_t *tvb, packet_info *pinfo,
1353 proto_tree *tree, int offset, uint8_t extlen,
1354 uint8_t opcode _U_) {
1355 if (extlen == 0) {
1356 // Success! none of the known commands use extras
1357 return;
1360 proto_item *extras_item = proto_tree_add_item(tree, hf_extras, tvb, offset,
1361 extlen, ENC_NA);
1362 proto_tree *extras_tree = proto_item_add_subtree(extras_item, ett_extras);
1363 proto_tree_add_expert_format(extras_tree, pinfo,
1364 &ei_warn_illegal_extras_length, tvb,
1365 offset, extlen,
1366 "Unexpected amount of extras");
1369 static void
1370 dissect_client_extras(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1371 int offset, uint8_t extlen, uint8_t opcode, bool request,
1372 uint16_t *path_len)
1374 proto_tree *extras_tree = NULL;
1375 proto_item *extras_item = NULL;
1376 int save_offset = offset, ii;
1377 unsigned bpos;
1378 bool illegal = false; /* Set when extras shall not be present */
1379 bool missing = false; /* Set when extras is missing */
1380 bool first_flag;
1381 uint32_t flags;
1382 proto_item *tf;
1383 const char *tap_connect_flags[] = {
1384 "BACKFILL", "DUMP", "LIST_VBUCKETS", "TAKEOVER_VBUCKETS",
1385 "SUPPORT_ACK", "REQUEST_KEYS_ONLY", "CHECKPOINT", "REGISTERED_CLIENT"
1388 *path_len = 0;
1390 if (extlen) {
1391 extras_item = proto_tree_add_item(tree, hf_extras, tvb, offset, extlen, ENC_NA);
1392 extras_tree = proto_item_add_subtree(extras_item, ett_extras);
1395 switch (opcode) {
1397 case CLIENT_OPCODE_GET:
1398 case CLIENT_OPCODE_GETQ:
1399 case CLIENT_OPCODE_GETK:
1400 case CLIENT_OPCODE_GETKQ:
1401 if (extlen) {
1402 if (request) {
1403 /* Request shall not have extras */
1404 illegal = true;
1405 } else {
1406 proto_tree_add_item(extras_tree, hf_extras_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
1407 offset += 4;
1409 } else if (!request) {
1410 /* Response must have extras */
1411 missing = true;
1413 break;
1415 case CLIENT_OPCODE_SET:
1416 case CLIENT_OPCODE_SETQ:
1417 case CLIENT_OPCODE_ADD:
1418 case CLIENT_OPCODE_ADDQ:
1419 case CLIENT_OPCODE_REPLACE:
1420 case CLIENT_OPCODE_REPLACEQ:
1421 if (extlen) {
1422 if (request) {
1423 proto_tree_add_item(extras_tree, hf_extras_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
1424 offset += 4;
1426 proto_tree_add_item(extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
1427 offset += 4;
1428 } else {
1429 proto_tree_add_item(extras_tree, hf_extras_vbucket_uuid, tvb, offset, 8, ENC_BIG_ENDIAN);
1430 offset += 8;
1432 proto_tree_add_item(extras_tree, hf_extras_mutation_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1433 offset += 8;
1435 } else if (request) {
1436 /* Request must have extras */
1437 missing = true;
1439 break;
1441 case CLIENT_OPCODE_INCREMENT:
1442 case CLIENT_OPCODE_INCREMENTQ:
1443 case CLIENT_OPCODE_DECREMENT:
1444 case CLIENT_OPCODE_DECREMENTQ:
1445 if (extlen) {
1446 if (request) {
1447 proto_tree_add_item(extras_tree, hf_extras_delta, tvb, offset, 8, ENC_BIG_ENDIAN);
1448 offset += 8;
1450 proto_tree_add_item(extras_tree, hf_extras_initial, tvb, offset, 8, ENC_BIG_ENDIAN);
1451 offset += 8;
1453 proto_tree_add_item(extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
1454 offset += 4;
1455 } else {
1456 proto_tree_add_item(extras_tree, hf_extras_vbucket_uuid, tvb, offset, 8, ENC_BIG_ENDIAN);
1457 offset += 8;
1459 proto_tree_add_item(extras_tree, hf_extras_mutation_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1460 offset += 8;
1462 } else if (request) {
1463 /* Request must have extras */
1464 missing = true;
1466 break;
1468 case CLIENT_OPCODE_FLUSH:
1469 case CLIENT_OPCODE_FLUSHQ:
1470 if (extlen) {
1471 proto_tree_add_item(extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
1472 offset += 4;
1474 break;
1476 case CLIENT_OPCODE_DELETE:
1477 case CLIENT_OPCODE_DELETEQ:
1478 case CLIENT_OPCODE_APPEND:
1479 case CLIENT_OPCODE_APPENDQ:
1480 case CLIENT_OPCODE_PREPEND:
1481 case CLIENT_OPCODE_PREPENDQ:
1482 if (extlen) {
1483 if (request) {
1484 /* Must not have extras */
1485 illegal = true;
1486 } else {
1487 proto_tree_add_item(extras_tree, hf_extras_vbucket_uuid, tvb, offset, 8, ENC_BIG_ENDIAN);
1488 offset += 8;
1490 proto_tree_add_item(extras_tree, hf_extras_mutation_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1491 offset += 8;
1494 break;
1496 case CLIENT_OPCODE_QUIT:
1497 case CLIENT_OPCODE_QUITQ:
1498 case CLIENT_OPCODE_VERSION:
1499 case CLIENT_OPCODE_STAT:
1500 case CLIENT_OPCODE_OBSERVE:
1501 case CLIENT_OPCODE_OBSERVE_SEQNO:
1502 /* Must not have extras */
1503 if (extlen) {
1504 illegal = true;
1506 break;
1508 case CLIENT_OPCODE_GET_ALL_VB_SEQNOS:
1509 if (extlen) {
1510 if (request) {
1511 /* May have extras */
1512 proto_tree_add_item(extras_tree, hf_vbucket_states_state, tvb, offset, 4, ENC_BIG_ENDIAN);
1513 offset += 4;
1514 } else {
1515 /* Must not have extras */
1516 illegal = true;
1519 break;
1521 case CLIENT_OPCODE_TAP_CONNECT:
1523 static int * const extra_flags[] = {
1524 &hf_extras_flags_backfill,
1525 &hf_extras_flags_dump,
1526 &hf_extras_flags_list_vbuckets,
1527 &hf_extras_flags_takeover_vbuckets,
1528 &hf_extras_flags_support_ack,
1529 &hf_extras_flags_request_keys_only,
1530 &hf_extras_flags_checkpoint,
1531 NULL
1534 /* TODO: extra_flags fields are 16-bits wide, whereas flags is 4 bytes? */
1535 tf = proto_tree_add_bitmask(extras_tree, tvb, offset, hf_extras_flags, ett_extras_flags, extra_flags, ENC_BIG_ENDIAN);
1537 flags = tvb_get_ntohl(tvb, offset);
1538 first_flag = true;
1539 for (ii = 0; ii < 8; ii++) {
1540 bpos = 1 << ii;
1541 if (flags & bpos) {
1542 if (first_flag) {
1543 proto_item_append_text(tf, " (");
1545 proto_item_append_text(tf, "%s%s",
1546 first_flag ? "" : ", ",
1547 tap_connect_flags[ii]);
1548 first_flag = false;
1551 if (first_flag == true) {
1552 proto_item_append_text(tf, " <None>");
1553 } else {
1554 proto_item_append_text(tf, ")");
1557 offset += 4;
1559 break;
1561 case CLIENT_OPCODE_TAP_MUTATION:
1562 case CLIENT_OPCODE_TAP_DELETE:
1563 case CLIENT_OPCODE_TAP_FLUSH:
1564 case CLIENT_OPCODE_TAP_OPAQUE:
1565 case CLIENT_OPCODE_TAP_VBUCKET_SET:
1566 case CLIENT_OPCODE_TAP_CHECKPOINT_START:
1567 case CLIENT_OPCODE_TAP_CHECKPOINT_END:
1568 break;
1570 case CLIENT_OPCODE_DCP_OPEN_CONNECTION:
1571 if (extlen) {
1572 if (request) {
1573 static int * const extra_flags[] = {
1574 &hf_extras_flags_dcp_connection_type,
1575 &hf_extras_flags_dcp_include_xattrs,
1576 &hf_extras_flags_dcp_no_value,
1577 &hf_extras_flags_dcp_collections,
1578 &hf_extras_flags_dcp_include_delete_times,
1579 NULL
1582 proto_tree_add_item(extras_tree, hf_extras_seqno, tvb, offset, 4, ENC_BIG_ENDIAN);
1583 offset += 4;
1584 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_extras_flags, ett_extras_flags, extra_flags, ENC_BIG_ENDIAN);
1585 offset += 4;
1586 } else {
1587 /* Response must not have extras (response is in Value) */
1588 illegal = true;
1590 } else if (request) {
1591 /* Request must have extras */
1592 missing = true;
1594 break;
1596 case CLIENT_OPCODE_DCP_ADD_STREAM:
1597 if (extlen) {
1598 if (request) {
1599 static int * const extra_flags[] = {
1600 &hf_extras_flags_dcp_add_stream_takeover,
1601 &hf_extras_flags_dcp_add_stream_diskonly,
1602 &hf_extras_flags_dcp_add_stream_latest,
1603 NULL
1606 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_extras_flags, ett_extras_flags, extra_flags, ENC_BIG_ENDIAN);
1607 offset += 4;
1608 } else {
1609 proto_tree_add_item(extras_tree, hf_extras_opaque, tvb, offset, 4, ENC_BIG_ENDIAN);
1610 offset += 4;
1612 } else {
1613 missing = true;
1615 break;
1617 case CLIENT_OPCODE_DCP_STREAM_REQUEST:
1618 if (extlen) {
1619 if (request) {
1620 /* No extra_flags and proto_tree_add_bitmask don't work with empty flags See Bug:17890
1621 static int * const extra_flags[] = {
1622 NULL
1625 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_extras_flags, ett_extras_flags, extra_flags, ENC_BIG_ENDIAN);
1627 proto_tree_add_item(extras_tree, hf_extras_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
1628 offset += 4;
1629 proto_tree_add_item(extras_tree, hf_extras_reserved, tvb, offset, 4, ENC_BIG_ENDIAN);
1630 offset += 4;
1631 proto_tree_add_item(extras_tree, hf_extras_start_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1632 offset += 8;
1633 proto_tree_add_item(extras_tree, hf_extras_end_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1634 offset += 8;
1635 proto_tree_add_item(extras_tree, hf_extras_vbucket_uuid, tvb, offset, 8, ENC_BIG_ENDIAN);
1636 offset += 8;
1637 proto_tree_add_item(extras_tree, hf_extras_snap_start_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1638 offset += 8;
1639 proto_tree_add_item(extras_tree, hf_extras_snap_end_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1640 offset += 8;
1642 } else if (request) {
1643 /* Request must have extras */
1644 missing = true;
1646 break;
1648 case CLIENT_OPCODE_DCP_SNAPSHOT_MARKER:
1649 if (extlen) {
1650 if (request) {
1651 // Two formats exist and the extlen allows us to know which is which
1652 if (extlen == 1) {
1653 proto_tree_add_item(extras_tree, hf_extras_marker_version, tvb, offset, 1, ENC_BIG_ENDIAN);
1654 offset += 1;
1655 } else if (extlen == 20){
1656 proto_tree_add_item(extras_tree, hf_extras_start_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1657 offset += 8;
1658 proto_tree_add_item(extras_tree, hf_extras_end_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1659 offset += 8;
1660 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_extras_flags, ett_extras_flags, snapshot_marker_flags, ENC_BIG_ENDIAN);
1661 offset += 4;
1662 } else {
1663 illegal = true;
1665 } else {
1666 illegal = true;
1668 } else if (request) {
1669 /* Request must have extras */
1670 missing = true;
1672 break;
1674 case CLIENT_OPCODE_DCP_MUTATION:
1675 if (extlen) {
1676 if (request) {
1677 /* No extra_flags and proto_tree_add_bitmask don't work with empty flags See Bug:17890
1678 static int * const extra_flags[] = {
1679 NULL
1683 proto_tree_add_item(extras_tree, hf_extras_by_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1684 offset += 8;
1685 proto_tree_add_item(extras_tree, hf_extras_rev_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1686 offset += 8;
1688 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_extras_flags, ett_extras_flags, extra_flags, ENC_BIG_ENDIAN);
1690 proto_tree_add_item(extras_tree, hf_extras_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
1691 offset += 4;
1692 proto_tree_add_item(extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
1693 offset += 4;
1694 proto_tree_add_item(extras_tree, hf_extras_lock_time, tvb, offset, 4, ENC_BIG_ENDIAN);
1695 offset += 4;
1696 proto_tree_add_item(extras_tree, hf_extras_nmeta, tvb, offset, 2, ENC_BIG_ENDIAN);
1697 offset += 2;
1698 proto_tree_add_item(extras_tree, hf_extras_nru, tvb, offset, 1, ENC_BIG_ENDIAN);
1699 offset += 1;
1700 } else {
1701 illegal = true;
1703 } else if (request) {
1704 /* Request must have extras */
1705 missing = true;
1707 break;
1709 case CLIENT_OPCODE_DCP_DELETION:
1710 if (request) {
1711 if (extlen == 18 || extlen == 21) {
1712 proto_tree_add_item(extras_tree, hf_extras_by_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1713 offset += 8;
1714 proto_tree_add_item(extras_tree, hf_extras_rev_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1715 offset += 8;
1717 // Is this a delete with delete_time (21 bytes) or not (18 bytes)?
1718 if (extlen == 18) {
1719 proto_tree_add_item(extras_tree, hf_extras_nmeta, tvb, offset, 2, ENC_BIG_ENDIAN);
1720 offset += 2;
1721 } else if (extlen == 21) {
1722 proto_tree_add_item(extras_tree, hf_extras_delete_time, tvb, offset, 4, ENC_BIG_ENDIAN);
1723 offset += 4;
1724 proto_tree_add_item(extras_tree, hf_extras_delete_unused, tvb, offset, 1, ENC_BIG_ENDIAN);
1725 offset += 1;
1727 } else if (extlen == 0) {
1728 missing = true; // request with no extras
1730 } else if (extlen) {
1731 illegal = true; // response with extras
1733 break;
1734 case CLIENT_OPCODE_DCP_EXPIRATION:
1735 if (extlen) {
1736 if (request) {
1737 proto_tree_add_item(extras_tree, hf_extras_by_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1738 offset += 8;
1739 proto_tree_add_item(extras_tree, hf_extras_rev_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1740 offset += 8;
1741 if (extlen == 20) {
1742 proto_tree_add_item(extras_tree, hf_extras_delete_time, tvb, offset, 4, ENC_BIG_ENDIAN);
1743 offset += 4;
1744 } else {
1745 // Handle legacy expiration packet (despite its lack of use)
1746 proto_tree_add_item(extras_tree, hf_extras_nmeta, tvb, offset, 2, ENC_BIG_ENDIAN);
1747 offset += 2;
1749 } else {
1750 illegal = true;
1752 } else if (request) {
1753 /* Request must have extras */
1754 missing = true;
1756 break;
1757 case CLIENT_OPCODE_DCP_FLUSH:
1758 if (extlen) {
1759 if (request) {
1760 proto_tree_add_item(extras_tree, hf_extras_by_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1761 offset += 8;
1762 proto_tree_add_item(extras_tree, hf_extras_rev_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1763 offset += 8;
1764 proto_tree_add_item(extras_tree, hf_extras_nmeta, tvb, offset, 2, ENC_BIG_ENDIAN);
1765 offset += 2;
1766 } else {
1767 illegal = true;
1769 } else if (request) {
1770 /* Request must have extras */
1771 missing = true;
1773 break;
1775 case CLIENT_OPCODE_DCP_BUFFER_ACKNOWLEDGEMENT:
1776 if (extlen) {
1777 if (request) {
1778 proto_tree_add_item(extras_tree, hf_extras_bytes_to_ack, tvb, offset, 4, ENC_BIG_ENDIAN);
1779 offset += 4;
1780 } else {
1781 illegal = true;
1783 } else if (request) {
1784 /* Request must have extras */
1785 missing = true;
1787 break;
1788 case CLIENT_OPCODE_DCP_SYSTEM_EVENT: {
1789 if (request && extlen == 13) {
1790 proto_tree_add_item(extras_tree, hf_extras_by_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1791 offset += 8;
1792 proto_tree_add_item(extras_tree, hf_extras_system_event_id, tvb, offset, 4, ENC_BIG_ENDIAN);
1793 offset += 4;
1794 proto_tree_add_item(extras_tree, hf_extras_system_event_version, tvb, offset, 1, ENC_BIG_ENDIAN);
1795 offset += 1;
1797 break;
1799 case CLIENT_OPCODE_DCP_PREPARE: {
1800 if (extlen) {
1801 if (request) {
1802 /* No extra_flags and proto_tree_add_bitmask don't work with empty flags See Bug:17890
1803 static int * const extra_flags[] = {
1804 NULL
1808 proto_tree_add_item(extras_tree, hf_extras_by_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1809 offset += 8;
1810 proto_tree_add_item(extras_tree, hf_extras_rev_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1811 offset += 8;
1813 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_extras_flags, ett_extras_flags, extra_flags, ENC_BIG_ENDIAN);
1815 proto_tree_add_item(extras_tree, hf_extras_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
1816 offset += 4;
1817 proto_tree_add_item(extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
1818 offset += 4;
1819 proto_tree_add_item(extras_tree, hf_extras_lock_time, tvb, offset, 4, ENC_BIG_ENDIAN);
1820 offset += 4;
1821 proto_tree_add_item(extras_tree, hf_extras_nru, tvb, offset, 1, ENC_BIG_ENDIAN);
1822 offset += 1;
1823 proto_tree_add_item(extras_tree, hf_extras_deleted, tvb, offset, 1, ENC_BIG_ENDIAN);
1824 offset += 1;
1825 proto_tree_add_item(extras_tree, hf_flex_frame_durability_req, tvb, offset, 1, ENC_BIG_ENDIAN);
1826 offset += 1;
1827 } else {
1828 illegal = true;
1830 } else if (request) {
1831 /* Request must have extras */
1832 missing = true;
1834 break;
1836 case CLIENT_OPCODE_DCP_SEQNO_ACK: {
1837 if (extlen) {
1838 if (request) {
1839 proto_tree_add_item(extras_tree, hf_extras_by_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1840 offset += 8;
1841 } else {
1842 illegal = true;
1844 } else if (request) {
1845 /* Request must have extras */
1846 missing = true;
1848 break;
1850 case CLIENT_OPCODE_DCP_COMMIT: {
1851 if (extlen) {
1852 if (request) {
1853 proto_tree_add_item(extras_tree, hf_extras_prepared_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1854 offset += 8;
1855 proto_tree_add_item(extras_tree, hf_extras_by_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1856 offset += 8;
1857 } else {
1858 illegal = true;
1860 } else if (request) {
1861 /* Request must have extras */
1862 missing = true;
1864 break;
1866 case CLIENT_OPCODE_DCP_ABORT: {
1867 if (extlen) {
1868 if (request) {
1869 proto_tree_add_item(extras_tree, hf_extras_prepared_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1870 offset += 8;
1871 proto_tree_add_item(extras_tree, hf_extras_abort_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1872 offset += 8;
1873 } else {
1874 illegal = true;
1876 } else if (request) {
1877 /* Request must have extras */
1878 missing = true;
1880 break;
1882 case CLIENT_OPCODE_DCP_SEQNO_ADVANCED: {
1883 if (extlen) {
1884 if (request) {
1885 proto_tree_add_item(extras_tree, hf_extras_by_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
1886 offset += 8;
1887 } else {
1888 illegal = true;
1890 } else if (request) {
1891 /* Request must have extras */
1892 missing = true;
1894 break;
1896 case CLIENT_OPCODE_DCP_OSO_SNAPSHOT: {
1897 if (extlen) {
1898 if (request) {
1899 static int * const extra_flags[] = {
1900 &hf_extras_flags_dcp_oso_snapshot_begin,
1901 &hf_extras_flags_dcp_oso_snapshot_end,
1902 NULL
1904 proto_tree_add_bitmask(extras_tree,
1905 tvb,
1906 offset,
1907 hf_extras_dcp_oso_snapshot_flags,
1908 ett_extras_flags,
1909 extra_flags,
1910 ENC_BIG_ENDIAN);
1911 offset += 4;
1912 } else {
1913 illegal = true;
1915 } else if (request) {
1916 /* Request must have extras */
1917 missing = true;
1919 break;
1921 case CLIENT_OPCODE_SUBDOC_GET:
1922 case CLIENT_OPCODE_SUBDOC_EXISTS:
1923 dissect_subdoc_spath_required_extras(tvb, extras_tree, extlen, request,
1924 &offset, path_len, &illegal);
1925 if (extlen == 4) {
1926 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_subdoc_doc_flags,
1927 ett_extras_flags, subdoc_doc_flags, ENC_BIG_ENDIAN);
1928 offset += 1;
1930 break;
1932 case CLIENT_OPCODE_SUBDOC_DICT_ADD:
1933 case CLIENT_OPCODE_SUBDOC_DICT_UPSERT:
1934 case CLIENT_OPCODE_SUBDOC_DELETE:
1935 case CLIENT_OPCODE_SUBDOC_REPLACE:
1936 case CLIENT_OPCODE_SUBDOC_ARRAY_PUSH_LAST:
1937 case CLIENT_OPCODE_SUBDOC_ARRAY_PUSH_FIRST:
1938 case CLIENT_OPCODE_SUBDOC_ARRAY_INSERT:
1939 case CLIENT_OPCODE_SUBDOC_ARRAY_ADD_UNIQUE:
1940 case CLIENT_OPCODE_SUBDOC_COUNTER:
1941 dissect_subdoc_spath_required_extras(tvb, extras_tree, extlen, request,
1942 &offset, path_len, &illegal);
1943 if (request) {
1944 /* optional expiry only permitted for mutation requests,
1945 if and only if (extlen == 7 || extlen == 8) */
1946 if (extlen == 7 || extlen == 8) {
1947 proto_tree_add_item(extras_tree, hf_extras_expiration, tvb, offset,
1948 4, ENC_BIG_ENDIAN);
1949 offset += 4;
1951 /* optional doc flags only permitted if and only if
1952 (extlen == 4 || extlen == 8) */
1953 if (extlen == 4 || extlen == 8) {
1954 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_subdoc_doc_flags,
1955 ett_extras_flags, subdoc_doc_flags,
1956 ENC_BIG_ENDIAN);
1957 offset += 1;
1959 if (extlen != 3 && extlen != 7 && extlen != 4 && extlen != 8) {
1960 illegal = true;
1963 break;
1965 case CLIENT_OPCODE_SUBDOC_MULTI_LOOKUP:
1966 if (request) {
1967 if (extlen == 1) {
1968 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_subdoc_doc_flags,
1969 ett_extras_flags, subdoc_doc_flags,
1970 ENC_BIG_ENDIAN);
1971 offset += 1;
1972 } else {
1973 illegal = true;
1976 break;
1978 case CLIENT_OPCODE_SUBDOC_MULTI_MUTATION:
1979 if (request) {
1980 if (extlen == 4 || extlen == 5) {
1981 proto_tree_add_item(extras_tree, hf_extras_expiration, tvb, offset, 4,
1982 ENC_BIG_ENDIAN);
1983 offset += 4;
1985 if (extlen == 1 || extlen == 5) {
1986 proto_tree_add_bitmask(extras_tree, tvb, offset, hf_subdoc_doc_flags,
1987 ett_extras_flags, subdoc_doc_flags, ENC_BIG_ENDIAN);
1988 offset += 1;
1990 if (extlen != 1 && extlen != 4 && extlen != 5) {
1991 illegal = true;
1994 break;
1996 case CLIENT_OPCODE_DEL_WITH_META:
1997 case CLIENT_OPCODE_SET_WITH_META:
1998 if (request) {
1999 proto_tree_add_item(extras_tree, hf_meta_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
2000 offset += 4;
2001 proto_tree_add_item(extras_tree, hf_meta_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
2002 offset += 4;
2003 proto_tree_add_item(extras_tree, hf_meta_revseqno, tvb, offset, 8, ENC_BIG_ENDIAN);
2004 offset += 8;
2005 proto_tree_add_item(extras_tree, hf_meta_cas, tvb, offset, 8, ENC_BIG_ENDIAN);
2006 offset += 8;
2008 /*The previous 24 bytes are required. The next two fields are optional,
2009 * hence we are checking the extlen to see what fields we have. As they
2010 * are different lengths we can do this by just checking the length.*/
2012 // Options field (4 bytes)
2013 if (extlen == 28 || extlen == 30) {
2014 /* TODO: these fields are all 16 bits wide, but field is 32 bits? */
2015 proto_tree_add_bitmask(
2016 extras_tree,
2017 tvb,
2018 offset,
2019 hf_meta_options,
2020 ett_extras_flags,
2021 (opcode == CLIENT_OPCODE_DEL_WITH_META) ?
2022 del_with_meta_extra_flags : set_with_meta_extra_flags,
2023 ENC_BIG_ENDIAN);
2024 offset += 4;
2026 // Meta Length field (2 bytes)
2027 if (extlen == 26 || extlen == 30) {
2028 proto_tree_add_item(extras_tree, hf_metalen, tvb, offset, 2, ENC_BIG_ENDIAN);
2029 offset += 2;
2032 break;
2034 case CLIENT_OPCODE_GET_META:
2035 if (request) {
2036 if(extlen) {
2037 proto_tree_add_item(extras_tree, hf_meta_reqextmeta, tvb, offset, 1, ENC_BIG_ENDIAN);
2038 offset += 1;
2040 } else {
2041 proto_tree_add_item(extras_tree, hf_meta_deleted, tvb, offset, 4, ENC_BIG_ENDIAN);
2042 offset += 4;
2043 proto_tree_add_item(extras_tree, hf_meta_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
2044 offset += 4;
2045 proto_tree_add_item(extras_tree, hf_exptime, tvb, offset, 4, ENC_BIG_ENDIAN);
2046 offset += 4;
2047 proto_tree_add_item(extras_tree, hf_extras_meta_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
2048 offset += 8;
2049 if (extlen == 21) {
2050 proto_tree_add_item(extras_tree, hf_confres, tvb, offset, 1, ENC_BIG_ENDIAN);
2051 offset += 1;
2054 break;
2055 case CLIENT_OPCODE_COLLECTIONS_GET_ID:
2056 if (!request) {
2057 proto_tree_add_item(extras_tree, hf_collection_manifest_id, tvb, offset, 8, ENC_BIG_ENDIAN);
2058 offset += 8;
2059 proto_tree_add_item(extras_tree, hf_collection_key_id, tvb, offset, 4, ENC_BIG_ENDIAN);
2060 offset += 4;
2062 break;
2063 case CLIENT_OPCODE_RANGE_SCAN_CONTINUE:
2064 // https://github.com/couchbase/kv_engine/blob/master/docs/range_scans/range_scan_continue.md
2065 if (request) {
2066 proto_tree_add_item(extras_tree, hf_range_scan_uuid, tvb, offset, 16, ENC_BIG_ENDIAN);
2067 offset += 16;
2068 proto_tree_add_item(extras_tree, hf_range_scan_item_limit, tvb, offset, 4, ENC_BIG_ENDIAN);
2069 offset += 4;
2070 proto_tree_add_item(extras_tree, hf_range_scan_time_limit, tvb, offset, 4, ENC_BIG_ENDIAN);
2071 offset += 4;
2072 proto_tree_add_item(extras_tree, hf_range_scan_byte_limit, tvb, offset, 4, ENC_BIG_ENDIAN);
2073 offset += 4;
2075 break;
2076 case CLIENT_OPCODE_RANGE_SCAN_CANCEL:
2077 // https://github.com/couchbase/kv_engine/blob/master/docs/range_scans/range_scan_cancel.md
2078 if (request) {
2079 proto_tree_add_item(extras_tree, hf_range_scan_uuid, tvb, offset, 16, ENC_BIG_ENDIAN);
2080 offset += 16;
2082 break;
2083 default:
2084 if (extlen) {
2085 /* Decode as unknown extras */
2086 proto_tree_add_item(extras_tree, hf_extras_unknown, tvb, offset, extlen, ENC_NA);
2087 offset += extlen;
2089 break;
2091 if (illegal) {
2092 proto_tree_add_expert_format(extras_tree, pinfo, &ei_warn_shall_not_have_extras, tvb, offset, 0,
2093 "%s %s should not have extras",
2094 val_to_str_ext(opcode, &client_opcode_vals_ext, "Opcode 0x%x"),
2095 request ? "Request" : "Response");
2096 offset += extlen;
2097 } else if (missing) {
2099 proto_tree_add_expert_format(tree, pinfo, &ei_warn_must_have_extras, tvb, offset, 0,
2100 "%s %s must have Extras",
2101 val_to_str_ext(opcode, &client_opcode_vals_ext, "Opcode Ox%x"),
2102 request ? "Request" : "Response");
2105 if ((offset - save_offset) != extlen) {
2106 expert_add_info_format(pinfo, extras_item, &ei_warn_illegal_extras_length,
2107 "Illegal Extras length, should be %d", offset - save_offset);
2112 Decode an unsigned leb128 int from a slice within a tvbuff_t
2113 @param tvb buffer to read from
2114 @param start index of the first byte of 'slice'
2115 @param end index of the last byte of the buffer 'slice'
2116 @param [out] value the decoded value
2117 @returns next byte after the leb128 bytes or -1 if we failed to decode
2119 static int
2120 dissect_unsigned_leb128(tvbuff_t *tvb, int start, int end, uint32_t* value) {
2121 uint8_t byte = tvb_get_uint8(tvb, start);
2122 *value = byte & 0x7f;
2125 if ((byte & 0x80) == 0x80) {
2126 uint32_t shift = 7;
2127 int byte_idx;
2128 for (byte_idx = start+1; byte_idx < end; byte_idx++) {
2129 byte = tvb_get_uint8(tvb, byte_idx);
2130 /* Ensure we are using a valid shift */
2131 if (shift > 32)
2132 return -1;
2133 *value |= (byte & 0x7f) << shift;
2134 if ((byte & 0x80) == 0) {
2135 break;
2137 shift += 7;
2139 return (byte_idx == end) ? -1 : byte_idx + 1;
2141 return start + 1;
2144 static void dissect_server_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, int keylen, uint8_t opcode, bool request) {
2145 if (keylen == 0) {
2146 switch (opcode) {
2147 case SERVER_OPCODE_GET_AUTHORIZATION:
2148 if (request) {
2149 proto_tree_add_expert_format(tree, pinfo, &ei_warn_must_have_key,
2150 tvb, offset, 0,
2151 "GetAuthorization request must have key");
2153 return;
2154 case SERVER_OPCODE_CLUSTERMAP_CHANGE_NOTIFICATION:
2155 if (request) {
2156 proto_tree_add_expert_format(tree, pinfo, &ei_warn_must_have_key,
2157 tvb, offset, 0,
2158 "ClustermapChangeNotification request must have key");
2160 case SERVER_OPCODE_AUTHENTICATE:
2161 case SERVER_OPCODE_ACTIVE_EXTERNAL_USERS:
2162 // Success! none of these commands want a key
2163 default:
2164 // Probably ok as we don't know about the opcode
2165 return;
2169 proto_item *ti = proto_tree_add_item(tree, hf_key, tvb, offset, keylen, ENC_UTF_8 | ENC_STR_HEX);
2171 switch (opcode) {
2172 case SERVER_OPCODE_CLUSTERMAP_CHANGE_NOTIFICATION:
2173 if (!request) {
2174 expert_add_info_format(pinfo, ti, &ei_warn_shall_not_have_key,
2175 "ClustermapChangeNotification response shall not have key");
2177 break;
2179 case SERVER_OPCODE_AUTHENTICATE:
2180 case SERVER_OPCODE_ACTIVE_EXTERNAL_USERS:
2181 expert_add_info_format(pinfo, ti, &ei_warn_shall_not_have_key,
2182 "%s %s shall not have Key",
2183 val_to_str_ext(opcode,
2184 &server_opcode_vals_ext,
2185 "Opcode 0x%x"),
2186 request ? "Request" : "Response");
2187 break;
2189 case SERVER_OPCODE_GET_AUTHORIZATION:
2190 if (!request) {
2191 expert_add_info_format(pinfo, ti, &ei_warn_shall_not_have_key,
2192 "GetAuthorization response shall not have key");
2194 break;
2195 default:
2196 break;
2200 static void
2201 dissect_client_key(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
2202 int offset, int keylen, uint8_t opcode, bool request)
2204 proto_item *ti = NULL;
2205 bool illegal = false; /* Set when key shall not be present */
2206 bool missing = false; /* Set when key is missing */
2208 if (keylen) {
2209 bool collection_encoded_key = true;
2210 switch (opcode) {
2211 case CLIENT_OPCODE_STAT:
2212 case CLIENT_OPCODE_HELLO:
2213 case CLIENT_OPCODE_SASL_AUTH:
2214 case CLIENT_OPCODE_SASL_STEP:
2215 case CLIENT_OPCODE_IOCTL_GET:
2216 case CLIENT_OPCODE_IOCTL_SET:
2217 case CLIENT_OPCODE_DCP_CONTROL:
2218 case CLIENT_OPCODE_SET_PARAM:
2219 case CLIENT_OPCODE_CREATE_BUCKET:
2220 case CLIENT_OPCODE_DELETE_BUCKET:
2221 case CLIENT_OPCODE_SELECT_BUCKET:
2222 case CLIENT_OPCODE_IFCONFIG:
2223 collection_encoded_key = false;
2224 break;
2225 default:
2226 break;
2229 ti = proto_tree_add_item(tree, hf_key, tvb, offset, keylen, ENC_UTF_8|ENC_STR_HEX);
2231 if (collection_encoded_key) {
2232 /* assume collections are enabled and add a field for the CID */
2233 uint32_t cid = 0;
2234 int ok = dissect_unsigned_leb128(tvb, offset, offset + keylen, &cid);
2236 /* Add collection info to a subtree */
2237 proto_tree *cid_tree = proto_item_add_subtree(ti, ett_collection_key);
2239 if (ok == -1) {
2240 /* cid decode issue, could just be a non-collection stream, don't warn
2241 just add some info */
2242 proto_tree_add_string_format(cid_tree,
2243 hf_collection_key_logical,
2244 tvb,
2245 offset,
2246 keylen,
2247 NULL,
2248 "Collection ID didn't decode, maybe no CID.");
2249 } else {
2250 proto_tree_add_uint(cid_tree, hf_collection_key_id, tvb, offset,
2251 (ok - offset), cid);
2252 proto_tree_add_item(cid_tree, hf_collection_key_logical, tvb,
2253 ok, keylen - (ok - offset), ENC_UTF_8 | ENC_STR_HEX);
2256 offset += keylen;
2259 /* inSanity check */
2260 if (keylen) {
2261 switch (opcode) {
2262 case CLIENT_OPCODE_QUIT:
2263 case CLIENT_OPCODE_QUITQ:
2264 case CLIENT_OPCODE_NOOP:
2265 case CLIENT_OPCODE_VERSION:
2266 case CLIENT_OPCODE_DCP_FAILOVER_LOG_REQUEST:
2267 case CLIENT_OPCODE_DCP_BUFFER_ACKNOWLEDGEMENT:
2268 case CLIENT_OPCODE_GET_ALL_VB_SEQNOS:
2269 /* Request and Response must not have key */
2270 illegal = true;
2271 break;
2273 case CLIENT_OPCODE_SET:
2274 case CLIENT_OPCODE_ADD:
2275 case CLIENT_OPCODE_REPLACE:
2276 case CLIENT_OPCODE_DELETE:
2277 case CLIENT_OPCODE_SETQ:
2278 case CLIENT_OPCODE_ADDQ:
2279 case CLIENT_OPCODE_REPLACEQ:
2280 case CLIENT_OPCODE_DELETEQ:
2281 case CLIENT_OPCODE_FLUSH:
2282 case CLIENT_OPCODE_APPEND:
2283 case CLIENT_OPCODE_PREPEND:
2284 case CLIENT_OPCODE_FLUSHQ:
2285 case CLIENT_OPCODE_APPENDQ:
2286 case CLIENT_OPCODE_PREPENDQ:
2287 /* Response must not have a key */
2288 if (!request) {
2289 illegal = true;
2291 break;
2293 case CLIENT_OPCODE_DCP_ADD_STREAM:
2294 case CLIENT_OPCODE_DCP_CLOSE_STREAM:
2295 case CLIENT_OPCODE_DCP_STREAM_END:
2296 case CLIENT_OPCODE_DCP_SNAPSHOT_MARKER:
2297 case CLIENT_OPCODE_DCP_FLUSH:
2298 case CLIENT_OPCODE_DCP_SET_VBUCKET_STATE:
2299 /* Request must not have a key */
2300 if (request) {
2301 illegal = true;
2303 break;
2305 } else {
2306 switch (opcode) {
2307 case CLIENT_OPCODE_GET:
2308 case CLIENT_OPCODE_GETQ:
2309 case CLIENT_OPCODE_GETK:
2310 case CLIENT_OPCODE_GETKQ:
2311 case CLIENT_OPCODE_SET:
2312 case CLIENT_OPCODE_ADD:
2313 case CLIENT_OPCODE_REPLACE:
2314 case CLIENT_OPCODE_DELETE:
2315 case CLIENT_OPCODE_SETQ:
2316 case CLIENT_OPCODE_ADDQ:
2317 case CLIENT_OPCODE_REPLACEQ:
2318 case CLIENT_OPCODE_DELETEQ:
2319 case CLIENT_OPCODE_INCREMENT:
2320 case CLIENT_OPCODE_DECREMENT:
2321 case CLIENT_OPCODE_INCREMENTQ:
2322 case CLIENT_OPCODE_DECREMENTQ:
2323 case CLIENT_OPCODE_DCP_OPEN_CONNECTION:
2324 case CLIENT_OPCODE_DCP_MUTATION:
2325 case CLIENT_OPCODE_DCP_DELETION:
2326 case CLIENT_OPCODE_DCP_EXPIRATION:
2327 case CLIENT_OPCODE_DCP_SYSTEM_EVENT:
2328 /* Request must have key */
2329 if (request) {
2330 missing = true;
2332 break;
2336 if (illegal) {
2337 expert_add_info_format(pinfo, ti, &ei_warn_shall_not_have_key, "%s %s shall not have Key",
2338 val_to_str_ext(opcode, &client_opcode_vals_ext, "Opcode 0x%x"),
2339 request ? "Request" : "Response");
2340 } else if (missing) {
2341 proto_tree_add_expert_format(tree, pinfo, &ei_warn_must_have_key, tvb, offset, 0,
2342 "%s %s must have Key",
2343 val_to_str_ext(opcode, &client_opcode_vals_ext, "Opcode Ox%x"),
2344 request ? "Request" : "Response");
2348 static void
2349 dissect_multipath_lookup_response(tvbuff_t *tvb, packet_info *pinfo,
2350 proto_tree *tree, int offset, uint32_t value_len)
2352 int end = offset + value_len;
2353 int spec_idx = 0;
2355 while (offset < end) {
2356 proto_item *ti;
2357 proto_tree *multipath_tree;
2358 tvbuff_t *json_tvb;
2359 uint32_t result_len;
2360 int start_offset = offset;
2362 ti = proto_tree_add_subtree_format(tree, tvb, offset, -1, ett_multipath,
2363 &multipath_tree, "Lookup Result [ %u ]",
2364 spec_idx);
2366 proto_tree_add_item(multipath_tree, hf_status, tvb, offset, 2,
2367 ENC_BIG_ENDIAN);
2368 offset += 2;
2369 proto_tree_add_item_ret_uint(multipath_tree, hf_value_length, tvb, offset,
2370 4, ENC_BIG_ENDIAN, &result_len);
2371 offset += 4;
2373 proto_tree_add_item(multipath_tree, hf_value, tvb, offset, result_len,
2374 ENC_ASCII | ENC_NA);
2375 if (result_len > 0) {
2376 json_tvb = tvb_new_subset_length(tvb, offset, result_len);
2377 call_dissector(json_handle, json_tvb, pinfo, multipath_tree);
2379 offset += result_len;
2381 proto_item_set_len(ti, offset - start_offset);
2383 spec_idx++;
2387 static void
2388 dissect_multipath_mutation_response(tvbuff_t *tvb, packet_info *pinfo,
2389 proto_tree *tree, int offset, uint32_t value_len)
2391 int end = offset + value_len;
2392 int spec_idx = 0;
2394 /* Expect a variable number of mutation responses:
2395 * - If response.status == SUCCESS, zero to N responses, one for each mutation
2396 * spec which returns a value.
2397 * - If response.status != SUCCESS, exactly 1 response, for first failing
2398 * spec.
2400 while (offset < end) {
2401 proto_item *ti;
2402 proto_tree *multipath_tree;
2403 tvbuff_t *json_tvb;
2404 uint32_t status;
2405 int start_offset = offset;
2407 ti = proto_tree_add_subtree_format(tree, tvb, offset, -1, ett_multipath,
2408 &multipath_tree, "Mutation Result [ %u ]",
2409 spec_idx);
2411 proto_tree_add_item(multipath_tree, hf_multipath_index, tvb, offset, 1,
2412 ENC_BIG_ENDIAN);
2413 offset += 1;
2414 proto_tree_add_item_ret_uint(multipath_tree, hf_status, tvb, offset, 2,
2415 ENC_BIG_ENDIAN, &status);
2416 offset += 2;
2417 if (status == STATUS_SUCCESS) {
2418 uint32_t result_len;
2419 proto_tree_add_item_ret_uint(multipath_tree, hf_value_length, tvb,
2420 offset, 4, ENC_BIG_ENDIAN, &result_len);
2421 offset += 4;
2423 proto_tree_add_item(multipath_tree, hf_value, tvb, offset, result_len,
2424 ENC_ASCII | ENC_NA);
2425 if (result_len > 0) {
2426 json_tvb = tvb_new_subset_length(tvb, offset, result_len);
2427 call_dissector(json_handle, json_tvb, pinfo, multipath_tree);
2429 offset += result_len;
2431 proto_item_set_len(ti, offset - start_offset);
2433 spec_idx++;
2437 static void
2438 dissect_multipath_value(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
2439 int offset, uint32_t value_len, bool is_mutation,
2440 bool request)
2442 int end = offset + value_len;
2443 int spec_idx = 0;
2444 proto_item *ti;
2445 proto_tree *multipath_tree;
2447 if (request) {
2448 int min_spec_size;
2450 /* Minimum size is the fixed header. */
2451 min_spec_size = (is_mutation ? 8 : 4);
2453 while (offset + min_spec_size <= end) {
2454 uint32_t path_len;
2455 uint32_t spec_value_len = 0;
2456 int start_offset = offset;
2458 ti = proto_tree_add_subtree_format(tree, tvb, offset, -1, ett_multipath,
2459 &multipath_tree,
2460 (is_mutation ? "Mutation spec [ %u ]"
2461 : "Lookup spec [ %u ]"),
2462 spec_idx);
2464 proto_tree_add_item(multipath_tree, hf_multipath_opcode, tvb, offset, 1,
2465 ENC_BIG_ENDIAN);
2466 offset += 1;
2467 proto_tree_add_bitmask(multipath_tree, tvb, offset, hf_subdoc_flags,
2468 ett_extras_flags, subdoc_flags, ENC_BIG_ENDIAN);
2469 offset += 1;
2471 proto_tree_add_item_ret_uint(multipath_tree, hf_multipath_pathlen, tvb,
2472 offset, 2, ENC_BIG_ENDIAN, &path_len);
2473 offset += 2;
2475 if (is_mutation) {
2476 proto_tree_add_item_ret_uint(multipath_tree, hf_multipath_valuelen,
2477 tvb, offset, 4, ENC_BIG_ENDIAN,
2478 &spec_value_len);
2479 offset += 4;
2482 if (path_len) {
2483 proto_tree_add_item(multipath_tree, hf_multipath_path, tvb, offset, path_len,
2484 ENC_ASCII | ENC_NA);
2485 offset += path_len;
2488 if (spec_value_len > 0) {
2489 proto_tree_add_item(multipath_tree, hf_multipath_value, tvb, offset,
2490 spec_value_len, ENC_ASCII | ENC_NA);
2491 offset += spec_value_len;
2494 proto_item_set_len(ti, offset - start_offset);
2496 spec_idx++;
2498 } else {
2499 if (is_mutation) {
2500 dissect_multipath_mutation_response(tvb, pinfo, tree, offset, value_len);
2501 } else {
2502 dissect_multipath_lookup_response(tvb, pinfo, tree, offset, value_len);
2507 static void
2508 dissect_value(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
2509 int offset, uint32_t value_len, uint16_t path_len, uint8_t opcode,
2510 bool request, uint8_t datatype)
2512 proto_item *ti = NULL;
2513 bool illegal = false; /* Set when value shall not be present */
2514 bool missing = false; /* Set when value is missing */
2516 if (value_len > 0) {
2517 if (opcode == CLIENT_OPCODE_OBSERVE) {
2518 proto_tree *observe_tree;
2519 int oo = offset, end = offset + value_len;
2520 ti = proto_tree_add_item(tree, hf_observe, tvb, offset, value_len, ENC_ASCII);
2521 observe_tree = proto_item_add_subtree(ti, ett_observe);
2522 while (oo < end) {
2523 uint16_t kl; /* keylength */
2524 proto_tree_add_item(observe_tree, hf_observe_vbucket, tvb, oo, 2, ENC_BIG_ENDIAN);
2525 oo += 2;
2526 kl = tvb_get_ntohs(tvb, oo);
2527 proto_tree_add_item(observe_tree, hf_observe_keylength, tvb, oo, 2, ENC_BIG_ENDIAN);
2528 oo += 2;
2529 proto_tree_add_item(observe_tree, hf_observe_key, tvb, oo, kl, ENC_ASCII);
2530 oo += kl;
2531 if (!request) {
2532 proto_tree_add_item(observe_tree, hf_observe_status, tvb, oo, 1, ENC_BIG_ENDIAN);
2533 oo++;
2534 proto_tree_add_item(observe_tree, hf_observe_cas, tvb, oo, 8, ENC_BIG_ENDIAN);
2535 oo += 8;
2538 } else if (opcode == CLIENT_OPCODE_OBSERVE_SEQNO) {
2539 if (request) {
2540 ti = proto_tree_add_item(tree, hf_observe_vbucket_uuid, tvb, offset, 8, ENC_BIG_ENDIAN);
2541 if (value_len != 8) {
2542 expert_add_info_format(pinfo, ti, &ei_warn_illegal_value_length, "Illegal Value length, should be 8");
2544 } else {
2546 * <format_type, vbucket id, vbucket uuid, last_persisted_seqno, current_seqno>
2548 * - format_type is of type uint8_t and it describes whether
2549 * the vbucket has failed over or not. 1 indicates a hard
2550 * failover, 0 indicates otherwise.
2551 * - vbucket id is of type uint16_t and it is the identifier for
2552 * the vbucket.
2553 * - vbucket uuid is of type uint64_t and it represents a UUID for
2554 * the vbucket.
2555 * - last_persisted_seqno is of type uint64_t and it is the
2556 * last sequence number that was persisted for this
2557 * vbucket.
2558 * - current_seqno is of the type uint64_t and it is the
2559 * sequence number of the latest mutation in the vbucket.
2561 * In the case of a hard failover, the tuple is of the form
2562 * <format_type, vbucket id, vbucket uuid, last_persisted_seqno, current_seqno,
2563 * old vbucket uuid, last_received_seqno>
2565 * - old vbucket uuid is of type uint64_t and it is the
2566 * vbucket UUID of the vbucket prior to the hard failover.
2568 * - last_received_seqno is of type uint64_t and it is the
2569 * last received sequence number in the old vbucket uuid.
2571 * The other fields are the same as that mentioned in the normal case.
2573 uint8_t failed_over;
2575 proto_tree_add_item(tree, hf_observe_failed_over, tvb, offset, 1, ENC_BIG_ENDIAN);
2576 failed_over = tvb_get_uint8(tvb, offset);
2577 offset++;
2578 proto_tree_add_item(tree, hf_observe_vbucket, tvb, offset, 2, ENC_BIG_ENDIAN);
2579 offset += 2;
2580 proto_tree_add_item(tree, hf_observe_vbucket_uuid, tvb, offset, 8, ENC_BIG_ENDIAN);
2581 offset += 8;
2582 proto_tree_add_item(tree, hf_observe_last_persisted_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
2583 offset += 8;
2584 proto_tree_add_item(tree, hf_observe_current_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
2585 offset += 8;
2586 if (failed_over) {
2587 proto_tree_add_item(tree, hf_observe_old_vbucket_uuid, tvb, offset, 8, ENC_BIG_ENDIAN);
2588 offset += 8;
2589 proto_tree_add_item(tree, hf_observe_last_received_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
2592 } else if (!request && (opcode == CLIENT_OPCODE_DCP_STREAM_REQUEST || opcode == CLIENT_OPCODE_DCP_FAILOVER_LOG_REQUEST)) {
2593 if (value_len % 16 != 0) {
2594 expert_add_info_format(pinfo, ti, &ei_warn_illegal_value_length, "Response with bad failover log length");
2595 } else {
2596 proto_tree *failover_log_tree;
2597 int cur = offset, end = offset + value_len;
2598 ti = proto_tree_add_item(tree, hf_failover_log, tvb, offset, value_len, ENC_ASCII);
2599 failover_log_tree = proto_item_add_subtree(ti, ett_failover_log);
2600 ti = proto_tree_add_uint(failover_log_tree, hf_failover_log_size, tvb, offset, 0, (end - cur) / 16);
2601 proto_item_set_generated(ti);
2602 while (cur < end) {
2603 proto_tree_add_item(failover_log_tree, hf_failover_log_vbucket_uuid, tvb, cur, 8, ENC_BIG_ENDIAN);
2604 cur += 8;
2605 proto_tree_add_item(failover_log_tree, hf_failover_log_vbucket_seqno, tvb, cur, 8, ENC_BIG_ENDIAN);
2606 cur += 8;
2609 } else if (!request && opcode == CLIENT_OPCODE_GET_ALL_VB_SEQNOS) {
2610 if (value_len % 10 != 0) {
2611 expert_add_info_format(pinfo, ti, &ei_warn_illegal_value_length, "Response with bad body length");
2612 } else {
2613 proto_tree *vbucket_states_tree;
2614 int cur = offset, end = offset + value_len;
2615 ti = proto_tree_add_item(tree, hf_vbucket_states, tvb, offset, value_len, ENC_ASCII);
2616 vbucket_states_tree = proto_item_add_subtree(ti, ett_vbucket_states);
2617 ti = proto_tree_add_uint(vbucket_states_tree, hf_vbucket_states_size, tvb, offset, 0, (end - cur) / 10);
2618 proto_item_set_generated(ti);
2619 while (cur < end) {
2620 proto_tree_add_item(vbucket_states_tree, hf_vbucket_states_id, tvb, cur, 2, ENC_BIG_ENDIAN);
2621 cur += 2;
2622 proto_tree_add_item(vbucket_states_tree, hf_vbucket_states_seqno, tvb, cur, 8, ENC_BIG_ENDIAN);
2623 cur += 8;
2626 } else if (!request && (opcode == CLIENT_OPCODE_INCREMENT || opcode == CLIENT_OPCODE_DECREMENT)) {
2627 ti = proto_tree_add_item(tree, hf_uint64_response, tvb, offset, 8, ENC_BIG_ENDIAN);
2628 if (value_len != 8) {
2629 expert_add_info_format(pinfo, ti, &ei_warn_illegal_value_length, "Illegal Value length, should be 8");
2631 } else if (has_json_value(request, opcode)) {
2632 tvbuff_t *json_tvb;
2633 ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII | ENC_NA);
2634 json_tvb = tvb_new_subset_length(tvb, offset, value_len);
2635 call_dissector(json_handle, json_tvb, pinfo, tree);
2637 } else if (opcode == CLIENT_OPCODE_SUBDOC_MULTI_LOOKUP ||
2638 opcode == CLIENT_OPCODE_SUBDOC_MULTI_MUTATION) {
2639 dissect_multipath_value(tvb, pinfo, tree, offset, value_len,
2640 (opcode == CLIENT_OPCODE_SUBDOC_MULTI_MUTATION),
2641 request);
2643 } else if (opcode == CLIENT_OPCODE_HELLO) {
2644 int curr = offset, end = offset + value_len;
2645 proto_tree *hello_features_tree;
2646 ti = proto_tree_add_item(tree, hf_hello_features, tvb, offset, value_len, ENC_ASCII);
2647 hello_features_tree = proto_item_add_subtree(ti, ett_hello_features);
2648 while (curr < end) {
2649 proto_tree_add_item(hello_features_tree, hf_hello_features_feature, tvb, curr, 2, ENC_BIG_ENDIAN);
2650 curr += 2;
2652 } else if (!request && opcode == CLIENT_OPCODE_RANGE_SCAN_CREATE) {
2653 proto_tree_add_item(tree, hf_range_scan_uuid, tvb, offset, 16, ENC_BIG_ENDIAN);
2654 } else if (path_len != 0) {
2655 ti = proto_tree_add_item(tree, hf_path, tvb, offset, path_len, ENC_ASCII | ENC_NA);
2656 value_len -= path_len;
2657 if (value_len > 0) {
2658 ti = proto_tree_add_item(tree, hf_value, tvb, offset + path_len,
2659 value_len, ENC_ASCII | ENC_NA);
2661 } else if (request && opcode == CLIENT_OPCODE_CREATE_BUCKET) {
2662 int sep, equals_pos, sep_pos, config_len;
2663 proto_tree *key_tree, *config_tree = NULL;
2665 /* There are 2 main items stored in the value. The bucket type (represented by a path to the engine) and the
2666 * bucket config. These are separated by a NULL byte with the bucket type coming first.*/
2668 sep = tvb_find_uint8(tvb, offset, value_len, 0x00);
2669 if (sep == -1) {
2670 ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII);
2671 expert_add_info_format(pinfo, ti, &ei_separator_not_found, "Null byte not found");
2672 } else {
2673 proto_tree_add_item(tree, hf_bucket_type, tvb, offset, sep - offset, ENC_ASCII | ENC_NA);
2674 config_len = value_len - (sep - offset) - 1; //Don't include NULL byte in length
2675 if(config_len <= 0) {
2676 expert_add_info_format(pinfo, ti, &ei_separator_not_found, "Separator not found in expected location");
2677 } else {
2678 offset = sep + 1;// Ignore NULL byte
2680 ti = proto_tree_add_item(tree, hf_bucket_config, tvb, offset, config_len, ENC_ASCII | ENC_NA);
2681 config_tree = proto_item_add_subtree(ti, ett_config);
2684 /* The config is arranged as "key=value;key=value..."*/
2685 while (config_len > 0) {
2686 // Get the key
2687 equals_pos = tvb_find_uint8(tvb, offset, config_len, 0x3d);
2688 if (equals_pos == -1) {
2689 expert_add_info_format(pinfo, ti, &ei_illegal_value, "Each key needs a value");
2690 break; // Break out the while loop
2692 ti = proto_tree_add_item(config_tree, hf_config_key, tvb, offset, equals_pos - offset, ENC_ASCII | ENC_NA);
2693 key_tree = proto_item_add_subtree(ti, ett_config_key);
2694 config_len -= (equals_pos - offset + 1);
2695 offset = equals_pos + 1;
2696 if (config_len <= 0) {
2697 expert_add_info_format(pinfo, ti, &ei_illegal_value, "Corresponding value missing");
2698 break;//Break out of while loop
2701 // Get the value
2702 sep_pos = tvb_find_uint8(tvb, offset, config_len, 0x3b);
2703 if (sep_pos == -1) {
2704 expert_add_info_format(pinfo, ti, &ei_separator_not_found, "Each key-value pair must be terminated by semi-colon");
2705 break; // Break out the while loop
2707 proto_tree_add_item(key_tree, hf_config_value, tvb, offset, sep_pos - offset, ENC_ASCII | ENC_NA);
2708 config_len -= (sep_pos - offset + 1);
2709 offset = sep_pos + 1;
2712 } else if ((datatype & DT_XATTR) && (opcode == CLIENT_OPCODE_SET_WITH_META ||
2713 opcode == CLIENT_OPCODE_DCP_MUTATION || opcode == CLIENT_OPCODE_DCP_DELETION ||
2714 opcode == CLIENT_OPCODE_DCP_EXPIRATION || opcode == CLIENT_OPCODE_DCP_PREPARE ||
2715 opcode == CLIENT_OPCODE_DEL_WITH_META || opcode == CLIENT_OPCODE_ADD_WITH_META ||
2716 opcode == CLIENT_OPCODE_SETQ_WITH_META || opcode == CLIENT_OPCODE_DELQ_WITH_META ||
2717 opcode == CLIENT_OPCODE_ADDQ_WITH_META )) {
2719 dissect_dcp_xattrs(tvb, tree, value_len, offset, pinfo);
2720 } else if (request && opcode == CLIENT_OPCODE_GET_ERROR_MAP) {
2721 if (value_len != 2) {
2722 expert_add_info_format(pinfo, ti, &ei_warn_illegal_value_length, "Illegal Value length, should be 2");
2723 ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII | ENC_NA);
2724 } else {
2725 ti = proto_tree_add_item(tree, hf_get_errmap_version, tvb, offset, value_len, ENC_BIG_ENDIAN);
2727 } else if (request && opcode == CLIENT_OPCODE_DCP_SNAPSHOT_MARKER) {
2728 if (value_len < 20) {
2729 expert_add_info_format(pinfo, ti, &ei_warn_illegal_value_length, "Illegal Value length, should be at least 20");
2730 ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII | ENC_NA);
2733 proto_tree_add_item(tree, hf_extras_start_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
2734 offset += 8;
2735 proto_tree_add_item(tree, hf_extras_end_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
2736 offset += 8;
2737 proto_tree_add_bitmask(tree, tvb, offset, hf_extras_flags, ett_extras_flags, snapshot_marker_flags, ENC_BIG_ENDIAN);
2738 offset += 4;
2740 if (value_len > 20) {
2741 if (value_len < 36) {
2742 expert_add_info_format(pinfo, ti, &ei_warn_illegal_value_length, "Illegal Value length, should be at least 36");
2743 ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII | ENC_NA);
2746 proto_tree_add_item(tree, hf_extras_max_visible_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
2747 offset += 8;
2748 proto_tree_add_item(tree, hf_extras_high_completed_seqno, tvb, offset, 8, ENC_BIG_ENDIAN);
2749 offset += 8;
2751 if (value_len > 36) {
2752 if (value_len != 44) {
2753 expert_add_info_format(pinfo, ti, &ei_warn_illegal_value_length, "Illegal Value length, should be 44");
2754 ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII | ENC_NA);
2757 proto_tree_add_item(tree, hf_extras_timestamp, tvb, offset, 8, ENC_BIG_ENDIAN);
2760 } else {
2761 ti = proto_tree_add_item(tree, hf_value, tvb, offset, value_len, ENC_ASCII | ENC_NA);
2762 #ifdef HAVE_SNAPPY
2763 if (datatype & DT_SNAPPY) {
2764 tvbuff_t* decompressed_tvb = tvb_child_uncompress_snappy(tvb, tvb, offset, tvb_captured_length_remaining(tvb, offset));
2765 if (decompressed_tvb != NULL) {
2766 add_new_data_source(pinfo, decompressed_tvb, "Decompressed Data");
2767 if (datatype & DT_JSON) {
2768 call_dissector(json_handle, decompressed_tvb, pinfo, tree);
2770 } else {
2771 expert_add_info_format(pinfo, ti, &ei_compression_error, "Error uncompressing snappy data");
2774 #endif
2778 /* Sanity check */
2779 if (value_len) {
2780 switch (opcode) {
2781 case CLIENT_OPCODE_GET:
2782 case CLIENT_OPCODE_GETQ:
2783 case CLIENT_OPCODE_GETK:
2784 case CLIENT_OPCODE_GETKQ:
2785 case CLIENT_OPCODE_INCREMENT:
2786 case CLIENT_OPCODE_DECREMENT:
2787 case CLIENT_OPCODE_VERSION:
2788 case CLIENT_OPCODE_INCREMENTQ:
2789 case CLIENT_OPCODE_DECREMENTQ:
2790 case CLIENT_OPCODE_DCP_OPEN_CONNECTION:
2791 case CLIENT_OPCODE_DCP_ADD_STREAM:
2792 case CLIENT_OPCODE_DCP_CLOSE_STREAM:
2793 case CLIENT_OPCODE_DCP_FAILOVER_LOG_REQUEST:
2794 case CLIENT_OPCODE_DCP_STREAM_END:
2795 case CLIENT_OPCODE_DCP_DELETION:
2796 case CLIENT_OPCODE_DCP_EXPIRATION:
2797 case CLIENT_OPCODE_DCP_FLUSH:
2798 case CLIENT_OPCODE_DCP_SET_VBUCKET_STATE:
2799 /* Request must not have value */
2800 if (request) {
2801 illegal = true;
2803 break;
2804 case CLIENT_OPCODE_DELETE:
2805 case CLIENT_OPCODE_QUIT:
2806 case CLIENT_OPCODE_FLUSH:
2807 case CLIENT_OPCODE_NOOP:
2808 case CLIENT_OPCODE_DELETEQ:
2809 case CLIENT_OPCODE_QUITQ:
2810 case CLIENT_OPCODE_FLUSHQ:
2811 /* Request and Response must not have value */
2812 illegal = true;
2813 break;
2814 case CLIENT_OPCODE_SET:
2815 case CLIENT_OPCODE_ADD:
2816 case CLIENT_OPCODE_REPLACE:
2817 case CLIENT_OPCODE_SETQ:
2818 case CLIENT_OPCODE_ADDQ:
2819 case CLIENT_OPCODE_REPLACEQ:
2820 case CLIENT_OPCODE_APPEND:
2821 case CLIENT_OPCODE_PREPEND:
2822 case CLIENT_OPCODE_APPENDQ:
2823 case CLIENT_OPCODE_PREPENDQ:
2824 /* Response must not have value */
2825 if (!request) {
2826 illegal = true;
2828 break;
2830 } else {
2831 switch (opcode) {
2832 case CLIENT_OPCODE_DCP_FAILOVER_LOG_REQUEST:
2833 /* Successful response must have value */
2834 if (!request) {
2835 missing = true;
2837 break;
2841 if (illegal) {
2842 expert_add_info_format(pinfo, ti, &ei_warn_shall_not_have_value, "%s %s shall not have Value",
2843 val_to_str_ext(opcode, &client_opcode_vals_ext, "Opcode 0x%x"),
2844 request ? "Request" : "Response");
2845 } else if (missing) {
2846 expert_add_info_format(pinfo, ti, &ei_value_missing, "%s %s must have Value",
2847 val_to_str_ext(opcode, &client_opcode_vals_ext, "Opcode 0x%x"),
2848 request ? "Request" : "Response");
2852 static void flex_frame_duration_dissect(tvbuff_t* tvb,
2853 proto_tree* frame_tree,
2854 int offset,
2855 int length) {
2857 if (length != 2) {
2858 proto_tree_add_expert_format(frame_tree,
2859 NULL,
2860 &ei_warn_unknown_flex_len,
2861 tvb,
2862 offset,
2863 length,
2864 "FlexFrame: RX/TX Duration with illegal length %d", length);
2865 } else {
2866 uint16_t encoded_micros = tvb_get_ntohs(tvb, offset);
2867 proto_tree_add_double(frame_tree,
2868 hf_flex_frame_tracing_duration,
2869 tvb,
2870 offset,
2872 pow(encoded_micros, 1.74) / 2);
2876 static void flex_frame_ru_usage_dissect(tvbuff_t* tvb,
2877 proto_tree* frame_tree,
2878 int offset,
2879 int length) {
2881 if (length != 2) {
2882 proto_tree_add_expert_format(frame_tree,
2883 NULL,
2884 &ei_warn_unknown_flex_len,
2885 tvb,
2886 offset,
2887 length,
2888 "Read unit illegal length %d", length);
2889 } else {
2890 uint16_t units = tvb_get_ntohs(tvb, offset);
2891 proto_tree_add_uint(frame_tree, hf_flex_frame_ru_count, tvb, offset, 2, units);
2895 static void flex_frame_wu_usage_dissect(tvbuff_t* tvb,
2896 proto_tree* frame_tree,
2897 int offset,
2898 int length) {
2900 if (length != 2) {
2901 proto_tree_add_expert_format(frame_tree,
2902 NULL,
2903 &ei_warn_unknown_flex_len,
2904 tvb,
2905 offset,
2906 length,
2907 "Write unit illegal length %d", length);
2908 } else {
2909 uint16_t units = tvb_get_ntohs(tvb, offset);
2910 proto_tree_add_uint(frame_tree, hf_flex_frame_wu_count, tvb, offset, 2, units);
2914 static void flex_frame_reorder_dissect(tvbuff_t* tvb,
2915 proto_tree* frame_tree,
2916 int offset,
2917 int length) {
2918 /* Expects no data, so just check len */
2919 if (length != 0) {
2920 proto_tree_add_expert_format(frame_tree,
2921 NULL,
2922 &ei_warn_unknown_flex_len,
2923 tvb,
2924 offset,
2925 length,
2926 "FlexFrame: Out Of Order with illegal length %d", length);
2930 static void flex_frame_durability_dissect(tvbuff_t* tvb,
2931 proto_tree* frame_tree,
2932 int offset,
2933 int length) {
2934 if (!(length == 1 || length == 3)) {
2935 proto_tree_add_expert_format(frame_tree,
2936 NULL,
2937 &ei_warn_unknown_flex_len,
2938 tvb,
2939 offset,
2940 length,
2941 "FlexFrame: Durability with illegal length %d", length);
2942 return;
2944 proto_tree_add_item(frame_tree, hf_flex_frame_durability_req, tvb, offset, 1, ENC_BIG_ENDIAN);
2947 static void flex_frame_dcp_stream_id_dissect(tvbuff_t* tvb,
2948 proto_tree* frame_tree,
2949 int offset,
2950 int length) {
2951 if (length != 2) {
2952 proto_tree_add_expert_format(frame_tree,
2953 NULL,
2954 &ei_warn_unknown_flex_len,
2955 tvb,
2956 offset,
2957 length,
2958 "FlexFrame: DCP Stream ID with illegal length %d", length);
2959 } else {
2960 uint16_t sid = tvb_get_ntohs(tvb, offset);
2961 proto_tree_add_uint(frame_tree, hf_flex_frame_dcp_stream_id, tvb, offset, 2, sid);
2965 static void flex_frame_impersonate_dissect(tvbuff_t* tvb,
2966 proto_tree* frame_tree,
2967 int offset,
2968 int length) {
2969 proto_tree_add_item(frame_tree,
2970 hf_flex_frame_impersonated_user,
2971 tvb,
2972 offset,
2973 length,
2974 ENC_UTF_8|ENC_STR_HEX);
2977 static void flex_frame_preserve_ttl(tvbuff_t* tvb,
2978 proto_tree* frame_tree,
2979 int offset,
2980 int length) {
2981 /* Expects no data, so just check len */
2982 if (length != 0) {
2983 proto_tree_add_expert_format(frame_tree,
2984 NULL,
2985 &ei_warn_unknown_flex_len,
2986 tvb,
2987 offset,
2988 length,
2989 "FlexFrame: Preserve TTL with illegal length %d", length);
2993 typedef void (*flex_frame_by_id_dissect_fn)(tvbuff_t*,
2994 proto_tree*,
2995 int,
2996 int);
2998 struct flex_frame_by_id_dissect {
2999 uint32_t id;
3000 flex_frame_by_id_dissect_fn handler;
3003 static const struct flex_frame_by_id_dissect flex_frame_response_dissect[] = {
3004 {FLEX_RESPONSE_ID_RX_TX_DURATION, &flex_frame_duration_dissect},
3005 {FLEX_RESPONSE_ID_RU_USAGE, &flex_frame_ru_usage_dissect},
3006 {FLEX_RESPONSE_ID_WU_USAGE, &flex_frame_wu_usage_dissect},
3007 {0, NULL }
3010 static const struct flex_frame_by_id_dissect flex_frame_request_dissect[] = {
3011 { FLEX_REQUEST_ID_REORDER, &flex_frame_reorder_dissect},
3012 { FLEX_REQUEST_ID_DURABILITY, &flex_frame_durability_dissect},
3013 { FLEX_REQUEST_ID_DCP_STREAM_ID, &flex_frame_dcp_stream_id_dissect},
3014 { FLEX_REQUEST_ID_IMPERSONATE, &flex_frame_impersonate_dissect},
3015 { FLEX_REQUEST_ID_PRESERVE_TTL, &flex_frame_preserve_ttl},
3016 { 0, NULL }
3020 Flexible Framing Extras:
3021 https://github.com/couchbase/kv_engine/blob/master/docs/BinaryProtocol.md
3023 static void dissect_flexible_framing_extras(tvbuff_t* tvb,
3024 packet_info* pinfo,
3025 proto_tree* tree,
3026 int offset,
3027 uint8_t flex_frame_extra_len,
3028 bool request) {
3030 /* select some request/response ID decoders */
3031 const struct flex_frame_by_id_dissect* id_dissectors = flex_frame_response_dissect;
3032 int info_id = hf_flex_frame_id_res;
3033 int info_id_esc = hf_flex_frame_id_res_esc;
3034 int info_len_id = hf_flex_frame_len;
3035 if (request) {
3036 id_dissectors = flex_frame_request_dissect;
3037 info_id = hf_flex_frame_id_req;
3038 info_id_esc = hf_flex_frame_id_req_esc;
3041 /* This first item shows the entire extent of all frame extras.
3042 If we have multiple frames, we will add them in the iteration */
3043 proto_tree_add_uint(tree,
3044 hf_flex_extras,
3045 tvb,
3046 offset,
3047 flex_frame_extra_len,
3048 flex_frame_extra_len);
3050 /* iterate until we've consumed the flex_frame_extra_len */
3051 int bytes_remaining = flex_frame_extra_len;
3052 int frame_index = 0;
3054 while (bytes_remaining > 0) {
3056 /* FrameInfo starts with a 'tag' byte which is formed from 2 nibbles */
3057 uint8_t tag_byte = tvb_get_uint8(tvb, offset);
3059 /* 0xff isn't defined yet in the spec as to what it should do */
3060 if (tag_byte == 0xFF) {
3061 proto_tree_add_expert_format(tree,
3062 pinfo,
3063 &ei_warn_unknown_flex_unsupported,
3064 tvb,
3065 offset,
3067 "Cannot decode 0xFF id/len byte");
3068 return;
3071 /* extract the nibbles into u16, if the id/len nibbles are escapes, their
3072 true values come from following bytes and can be larger than u8 */
3073 uint16_t id = tag_byte >> 4;
3074 uint16_t len = tag_byte & 0x0F;
3076 int id_size = 1;
3077 /* Calculate the id/len and add to the tree */
3078 if (id == FLEX_ESCAPE) {
3079 id = id + tvb_get_uint8(tvb, offset + 1);
3080 id_size++;
3081 info_id = info_id_esc;
3084 int len_size = 1;
3085 if (len == FLEX_ESCAPE) {
3086 len = len + tvb_get_uint8(tvb, offset + 1);
3087 len_size++;
3088 info_len_id = hf_flex_frame_len_esc;
3091 /* add a new sub-tree for this FrameInfo */
3092 proto_item* flex_item = proto_tree_add_string_format(tree,
3093 hf_flex_extras_n,
3094 tvb,
3095 offset,
3096 1 + len,
3097 NULL,
3098 "Flexible Frame %d",
3099 frame_index);
3101 proto_tree* frame_tree = proto_item_add_subtree(flex_item,
3102 ett_flex_frame_extras);
3104 /* Now add the info under the sub-tree */
3105 proto_tree_add_uint(frame_tree, info_id, tvb, offset, id_size, id);
3106 proto_tree_add_uint(frame_tree, info_len_id, tvb, offset, len_size, len);
3108 /* this is broken if both len and id are escaped, but we've returned earlier
3109 for that case (with a warning) */
3110 offset = offset + 1 + (len_size - 1) + (id_size - 1);
3111 bytes_remaining = bytes_remaining - 1 - (len_size - 1) - (id_size - 1);
3113 /* lookup a dissector function by id */
3114 int id_index = 0, found = 0;
3115 while (id_dissectors[id_index].handler) {
3116 if (id_dissectors[id_index].id == id) {
3117 id_dissectors[id_index].handler(tvb, frame_tree, offset, len);
3118 found = 1;
3119 break;
3121 id_index++;
3124 if (!found) {
3125 proto_tree_add_expert_format(frame_tree,
3126 pinfo,
3127 &ei_warn_unknown_flex_id,
3128 tvb,
3129 offset,
3130 len,
3131 "FlexFrame: no dissector function for %d", id);
3134 offset += len;
3135 bytes_remaining -= len;
3136 frame_index++;
3140 static bool
3141 is_xerror(uint8_t datatype, uint16_t status)
3143 if ((datatype & DT_JSON) && status != STATUS_SUBDOC_MULTI_PATH_FAILURE) {
3144 return true;
3146 return false;
3149 /// The following section contains dissector functions for the various
3150 /// server initiated push messages (and responses for them).
3151 /// It's easier to understand the logic with a single function per opcode
3152 /// than a long function with a ton of if/else statements
3154 static void d_s_o_clustermap_change_notification_req(tvbuff_t *tvb,
3155 packet_info *pinfo,
3156 proto_tree *tree,
3157 int offset,
3158 int size) {
3159 if (size == 0) {
3160 // this is an error!
3161 expert_add_info_format(pinfo, tree, &ei_warn_illegal_value_length,
3162 "Clustermap not present");
3163 return;
3165 // The payload is the clustermap in JSON
3166 proto_tree_add_item(tree, hf_server_clustermap_value, tvb, offset, size,
3167 ENC_ASCII | ENC_NA);
3168 tvbuff_t *json_tvb = tvb_new_subset_length(tvb, offset, size);
3169 call_dissector(json_handle, json_tvb, pinfo, tree);
3172 static void d_s_o_authenticate_req(tvbuff_t *tvb,
3173 packet_info *pinfo,
3174 proto_tree *tree,
3175 int offset,
3176 int size) {
3177 if (size == 0) {
3178 // this is an error!
3179 expert_add_info_format(pinfo, tree, &ei_warn_illegal_value_length,
3180 "Authentication payload not present");
3181 return;
3183 // The payload is an JSON object with the authentication request
3184 proto_tree_add_item(tree, hf_server_authentication, tvb, offset, size,
3185 ENC_ASCII | ENC_NA);
3186 tvbuff_t *json_tvb = tvb_new_subset_length(tvb, offset, size);
3187 call_dissector(json_handle, json_tvb, pinfo, tree);
3190 static void d_s_o_active_external_users_req(tvbuff_t *tvb,
3191 packet_info *pinfo,
3192 proto_tree *tree,
3193 int offset,
3194 int size) {
3195 if (size == 0) {
3196 // this is an error!
3197 expert_add_info_format(pinfo, tree, &ei_warn_illegal_value_length,
3198 "ActiveExternalUsers payload not present");
3199 return;
3201 // The payload is an JSON array with the list of the users
3202 proto_tree_add_item(tree, hf_server_external_users, tvb, offset, size,
3203 ENC_ASCII | ENC_NA);
3204 tvbuff_t *json_tvb = tvb_new_subset_length(tvb, offset, size);
3205 call_dissector(json_handle, json_tvb, pinfo, tree);
3208 static void d_s_o_get_authorization_req(tvbuff_t *tvb,
3209 packet_info *pinfo,
3210 proto_tree *tree,
3211 int offset,
3212 int size) {
3213 if (size > 0) {
3214 // this is an error!
3215 proto_item *ti = proto_tree_add_item(tree, hf_value, tvb, offset, size,
3216 ENC_ASCII | ENC_NA);
3217 expert_add_info_format(pinfo, ti, &ei_warn_shall_not_have_value,
3218 "GetAuthorization shall not have a value");
3222 /// Dissect the response to a server initiated push message which
3223 /// don't require a response (the client may send it, but the server
3224 /// will silently just ignore the response).
3225 /// If sent the body should not contain value unless the status code
3226 /// is an error and if so it shall be a JSON payload following the
3227 /// standard error format.
3228 static void d_s_o_server_ignored_response(tvbuff_t *tvb,
3229 packet_info *pinfo,
3230 proto_tree *tree,
3231 int offset,
3232 int size) {
3233 if (size == 0) {
3234 return;
3236 proto_item *ti = proto_tree_add_item(tree, hf_value, tvb, offset, size,
3237 ENC_ASCII | ENC_NA);
3238 if (get_status(tvb) == STATUS_SUCCESS) {
3239 expert_add_info_format(pinfo, ti, &ei_warn_shall_not_have_value,
3240 "Success should not carry value");
3241 } else {
3242 tvbuff_t *json_tvb = tvb_new_subset_length(tvb, offset, size);
3243 call_dissector(json_handle, json_tvb, pinfo, tree);
3247 static void d_s_o_authenticate_res(tvbuff_t *tvb ,
3248 packet_info *pinfo ,
3249 proto_tree *tree ,
3250 int offset ,
3251 int size ) {
3252 if (size == 0) {
3253 return;
3256 // Payload is JSON (for success and if there is an error)
3257 proto_tree_add_item(tree, hf_server_authentication, tvb, offset, size,
3258 ENC_ASCII | ENC_NA);
3259 tvbuff_t *json_tvb = tvb_new_subset_length(tvb, offset, size);
3260 call_dissector(json_handle, json_tvb, pinfo, tree);
3263 static void d_s_o_get_authorization_res(tvbuff_t *tvb,
3264 packet_info *pinfo,
3265 proto_tree *tree,
3266 int offset,
3267 int size) {
3268 if (size == 0) {
3269 return;
3272 // Payload is JSON (for success and if there is an error)
3273 proto_tree_add_item(tree, hf_server_get_authorization, tvb, offset, size,
3274 ENC_ASCII | ENC_NA);
3275 tvbuff_t *json_tvb = tvb_new_subset_length(tvb, offset, size);
3276 call_dissector(json_handle, json_tvb, pinfo, tree);
3281 * Does the opcode use the vbucket or not? (does it make any sense to
3282 * add the vbucket to the info)
3284 static bool opcode_use_vbucket(uint8_t magic _U_, uint8_t opcode) {
3285 switch (opcode) {
3286 case CLIENT_OPCODE_OBSERVE:
3287 case CLIENT_OPCODE_COLLECTIONS_GET_ID:
3288 case CLIENT_OPCODE_IFCONFIG:
3289 case CLIENT_OPCODE_SASL_LIST_MECHS:
3290 case CLIENT_OPCODE_SASL_AUTH:
3291 case CLIENT_OPCODE_SASL_STEP:
3292 case CLIENT_OPCODE_SHUTDOWN:
3293 case CLIENT_OPCODE_AUDIT_CONFIG_RELOAD:
3294 case CLIENT_OPCODE_AUDIT_PUT:
3295 case CLIENT_OPCODE_CONFIG_RELOAD:
3296 case CLIENT_OPCODE_CONFIG_VALIDATE:
3297 case CLIENT_OPCODE_IOCTL_SET:
3298 case CLIENT_OPCODE_IOCTL_GET:
3299 case CLIENT_OPCODE_HELLO:
3300 case CLIENT_OPCODE_VERBOSITY:
3301 case CLIENT_OPCODE_VERSION:
3302 case CLIENT_OPCODE_NOOP:
3303 case CLIENT_OPCODE_QUIT:
3304 case CLIENT_OPCODE_LIST_BUCKETS:
3305 case CLIENT_OPCODE_CREATE_BUCKET:
3306 case CLIENT_OPCODE_DELETE_BUCKET:
3307 case CLIENT_OPCODE_SELECT_BUCKET:
3308 return false;
3310 default:
3311 return true;
3316 * Each frame header consist of 24 bytes in two slightly different formats
3317 * (byte 6 and 7 is vbucket id in a request and status in a response).
3319 * This method dissect the frame header. Please refer to
3320 * https://github.com/couchbase/kv_engine/blob/master/docs/BinaryProtocol.md#request-header
3321 * for the layout of the frame header.
3323 static void dissect_frame_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *couchbase_tree, proto_item *couchbase_item) {
3324 uint8_t magic = get_magic(tvb);
3325 proto_item *ti = proto_tree_add_item(couchbase_tree, hf_magic, tvb, 0, 1, ENC_BIG_ENDIAN);
3326 if (try_val_to_str(magic, magic_vals) == NULL) {
3327 expert_add_info_format(pinfo, ti, &ei_warn_unknown_magic_byte, "Unknown magic byte: 0x%x", magic);
3330 uint8_t opcode = get_opcode(tvb);
3332 const char *opcode_name;
3333 if (is_server_magic(magic)) {
3334 ti = proto_tree_add_item(couchbase_tree, hf_server_opcode, tvb, 1, 1, ENC_BIG_ENDIAN);
3335 opcode_name = try_val_to_str_ext(opcode, &server_opcode_vals_ext);
3336 } else {
3337 ti = proto_tree_add_item(couchbase_tree, hf_opcode, tvb, 1, 1, ENC_BIG_ENDIAN);
3338 opcode_name = try_val_to_str_ext(opcode, &client_opcode_vals_ext);
3341 if (opcode_name == NULL) {
3342 expert_add_info_format(pinfo, ti, &ei_warn_unknown_opcode, "Unknown opcode: 0x%x", opcode);
3343 opcode_name = "Unknown opcode";
3345 proto_item_append_text(couchbase_item, ", %s %s, Opcode: 0x%x",
3346 opcode_name,
3347 val_to_str(magic, magic_vals, "Unknown magic (0x%x)"),
3348 opcode);
3349 col_append_fstr(pinfo->cinfo, COL_INFO, "%s %s, Opcode: 0x%x",
3350 opcode_name,
3351 val_to_str(magic, magic_vals, "Unknown magic (0x%x)"),
3352 opcode);
3354 /* Check for flex magic, which changes the header format */
3355 uint16_t keylen;
3356 uint8_t flex_frame_extras = get_flex_framing_extras_length(tvb);
3357 if (is_flex_encoded(magic)) {
3358 /* 2 separate bytes for the flex_extras and keylen */
3359 proto_tree_add_item(couchbase_tree, hf_flex_extras_length, tvb, 2, 1, ENC_BIG_ENDIAN);
3360 proto_tree_add_item(couchbase_tree, hf_flex_keylength, tvb, 3, 1, ENC_BIG_ENDIAN);
3361 } else {
3362 /* 2 bytes for the key */
3363 proto_tree_add_item(couchbase_tree, hf_keylength, tvb, 2, 2, ENC_BIG_ENDIAN);
3365 keylen = get_key_length(tvb);
3367 uint8_t extlen = get_extras_length(tvb);
3368 proto_tree_add_item(couchbase_tree, hf_extlength, tvb, 4, 1, ENC_BIG_ENDIAN);
3370 proto_tree_add_bitmask(couchbase_tree, tvb, 5, hf_datatype, ett_datatype, datatype_vals, ENC_BIG_ENDIAN);
3372 if (is_request_magic(magic)) {
3373 uint16_t vbucket = tvb_get_ntohs(tvb, 6);
3374 proto_tree_add_item(couchbase_tree, hf_vbucket, tvb, 6, 2, ENC_BIG_ENDIAN);
3375 if (opcode_use_vbucket(magic, opcode)) {
3376 proto_item_append_text(couchbase_item, ", vb:%d", vbucket);
3377 col_append_fstr(pinfo->cinfo, COL_INFO, ", vb:%d", vbucket);
3379 } else {
3380 /* This is a response or invalid magic... */
3381 uint16_t status = get_status(tvb);
3382 ti = proto_tree_add_item(couchbase_tree, hf_status, tvb, 6, 2, ENC_BIG_ENDIAN);
3383 if (status != 0) {
3384 expert_add_info_format(pinfo, ti, &ei_warn_unknown_opcode, "%s: %s",
3385 val_to_str_ext(opcode, &client_opcode_vals_ext, "Unknown opcode (0x%x)"),
3386 val_to_str_ext(status, &status_vals_ext, "Status: 0x%x"));
3390 uint32_t bodylen = get_body_length(tvb);
3391 uint32_t value_len = bodylen - extlen - keylen - flex_frame_extras;
3392 ti = proto_tree_add_uint(couchbase_tree, hf_value_length, tvb, 8, 0, value_len);
3393 proto_item_set_generated(ti);
3395 proto_tree_add_item(couchbase_tree, hf_total_bodylength, tvb, 8, 4, ENC_BIG_ENDIAN);
3398 * use little endian (network) encoding for the opaque as this is an opaque
3399 * field the client could use for whatever they want
3401 proto_tree_add_item(couchbase_tree, hf_opaque, tvb, 12, 4, ENC_LITTLE_ENDIAN);
3403 // Finally we've got the CAS (which observe has a special use for)
3404 if (opcode == CLIENT_OPCODE_OBSERVE) {
3405 proto_tree_add_item(couchbase_tree, hf_ttp, tvb, 16, 4, ENC_BIG_ENDIAN);
3406 proto_tree_add_item(couchbase_tree, hf_ttr, tvb, 20, 4, ENC_BIG_ENDIAN);
3407 } else {
3408 proto_tree_add_item(couchbase_tree, hf_cas, tvb, 16, 8, ENC_BIG_ENDIAN);
3413 * Dissect the flexible frame info's encoded in the packet
3415 static void dissect_frame_flex_info_section(tvbuff_t *tvb,
3416 packet_info *pinfo,
3417 proto_tree *tree,
3418 int offset,
3419 uint8_t size,
3420 uint8_t magic) {
3421 if (size == 0) {
3422 return;
3425 switch (magic) {
3426 case MAGIC_SERVER_RESPONSE:
3427 case MAGIC_SERVER_REQUEST:
3428 // None of the server initiated messages use flex frame encoding!
3429 proto_tree_add_item(tree, hf_flex_extras, tvb, offset, size, ENC_UTF_8|ENC_STR_HEX);
3430 proto_tree_add_expert_format(tree,
3431 pinfo,
3432 &ei_warn_unknown_flex_unsupported,
3433 tvb,
3434 offset,
3435 size,
3436 "Server initiated messages don't use flex framing");
3437 break;
3439 case MAGIC_CLIENT_REQUEST_FLEX:
3440 case MAGIC_CLIENT_RESPONSE_FLEX:
3441 dissect_flexible_framing_extras(tvb,
3442 pinfo,
3443 tree,
3444 offset,
3445 size,
3446 is_request_magic(magic));
3447 break;
3448 default:
3449 proto_tree_add_item(tree, hf_flex_extras, tvb, offset, size, ENC_UTF_8|ENC_STR_HEX);
3450 proto_tree_add_expert_format(tree,
3451 pinfo,
3452 &ei_warn_unknown_flex_unsupported,
3453 tvb,
3454 offset,
3455 size,
3456 "According to the magic we should not have flex encoding");
3461 * Dissect the extras section in the frame
3463 static void dissect_frame_extras(tvbuff_t *tvb,
3464 packet_info *pinfo,
3465 proto_tree *tree,
3466 int offset,
3467 uint8_t size,
3468 uint8_t magic,
3469 uint8_t opcode,
3470 uint16_t *subdoc_path_len) {
3471 switch (magic) {
3472 case MAGIC_SERVER_RESPONSE:
3473 dissect_server_response_extras(tvb, pinfo, tree, offset, size, opcode);
3474 break;
3475 case MAGIC_SERVER_REQUEST:
3476 dissect_server_request_extras(tvb, pinfo, tree, offset, size, opcode);
3477 break;
3478 case MAGIC_CLIENT_REQUEST_FLEX:
3479 case MAGIC_CLIENT_RESPONSE_FLEX:
3480 case MAGIC_CLIENT_REQUEST:
3481 case MAGIC_CLIENT_RESPONSE:
3482 dissect_client_extras(tvb, pinfo, tree, offset, size,
3483 opcode, is_request_magic(magic), subdoc_path_len);
3484 break;
3485 default:
3486 proto_tree_add_item(tree, hf_extras, tvb, offset, size, ENC_UTF_8|ENC_STR_HEX);
3487 proto_tree_add_expert_format(tree,
3488 pinfo,
3489 &ei_warn_unknown_extras,
3490 tvb,
3491 offset,
3492 size,
3493 "Invalid magic so we can't interpret extras");
3498 * Dissect the key section in the frame
3500 static void dissect_frame_key(tvbuff_t *tvb,
3501 packet_info *pinfo,
3502 proto_tree *tree,
3503 int offset,
3504 uint16_t size,
3505 uint8_t magic,
3506 uint8_t opcode) {
3507 if (is_server_magic(magic)) {
3508 dissect_server_key(tvb, pinfo, tree, offset, size, opcode,
3509 is_request_magic(magic));
3510 } else {
3511 dissect_client_key(tvb, pinfo, tree, offset, size, opcode,
3512 is_request_magic(magic));
3516 static void dissect_client_value(tvbuff_t *tvb,
3517 packet_info *pinfo,
3518 proto_tree *tree,
3519 int offset,
3520 uint32_t size,
3521 uint8_t magic,
3522 uint8_t opcode,
3523 uint16_t subdoc_path_len) {
3524 uint8_t datatype = get_datatype(tvb);
3525 if (is_request_magic(magic)) {
3526 dissect_value(tvb, pinfo, tree, offset, size, subdoc_path_len, opcode, true, datatype);
3527 } else {
3528 uint16_t status = get_status(tvb);
3529 if (status == 0) {
3530 dissect_value(tvb, pinfo, tree, offset, size, subdoc_path_len, opcode, false, datatype);
3531 } else if (size) {
3532 proto_tree_add_item(tree, hf_value, tvb, offset, size, ENC_ASCII | ENC_NA);
3533 if (status == STATUS_NOT_MY_VBUCKET || is_xerror(datatype, status)) {
3534 tvbuff_t *json_tvb;
3535 json_tvb = tvb_new_subset_length(tvb, offset, size);
3536 call_dissector(json_handle, json_tvb, pinfo, tree);
3537 } else if (opcode == CLIENT_OPCODE_SUBDOC_MULTI_LOOKUP) {
3538 dissect_multipath_lookup_response(tvb, pinfo, tree, offset, size);
3539 } else if (opcode == CLIENT_OPCODE_SUBDOC_MULTI_MUTATION) {
3540 dissect_multipath_mutation_response(tvb, pinfo, tree, offset, size);
3542 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
3543 val_to_str_ext(status, &status_vals_ext,
3544 "Unknown status: 0x%x"));
3545 } else {
3546 /* Newer opcodes do not include a value in non-SUCCESS responses. */
3547 proto_tree *ti;
3548 switch (opcode) {
3549 case CLIENT_OPCODE_SUBDOC_GET:
3550 case CLIENT_OPCODE_SUBDOC_EXISTS:
3551 case CLIENT_OPCODE_SUBDOC_DICT_ADD:
3552 case CLIENT_OPCODE_SUBDOC_DICT_UPSERT:
3553 case CLIENT_OPCODE_SUBDOC_DELETE:
3554 case CLIENT_OPCODE_SUBDOC_REPLACE:
3555 case CLIENT_OPCODE_SUBDOC_ARRAY_PUSH_LAST:
3556 case CLIENT_OPCODE_SUBDOC_ARRAY_PUSH_FIRST:
3557 case CLIENT_OPCODE_SUBDOC_ARRAY_INSERT:
3558 case CLIENT_OPCODE_SUBDOC_ARRAY_ADD_UNIQUE:
3559 case CLIENT_OPCODE_SUBDOC_COUNTER:
3560 case CLIENT_OPCODE_SUBDOC_MULTI_LOOKUP:
3561 case CLIENT_OPCODE_SUBDOC_MULTI_MUTATION:
3562 break;
3564 default:
3565 ti = proto_tree_add_item(tree, hf_value, tvb, offset, 0,
3566 ENC_ASCII | ENC_NA);
3567 expert_add_info_format(pinfo, ti, &ei_value_missing,
3568 "%s with status %s (0x%x) must have Value",
3569 val_to_str_ext(opcode,
3570 &client_opcode_vals_ext,
3571 "Opcode 0x%x"),
3572 val_to_str_ext(status,
3573 &status_vals_ext,
3574 "Unknown"),
3575 status);
3581 static void dissect_server_request_value(tvbuff_t *tvb,
3582 packet_info *pinfo,
3583 proto_tree *tree,
3584 int offset,
3585 int size) {
3586 switch (get_opcode(tvb)) {
3587 case SERVER_OPCODE_CLUSTERMAP_CHANGE_NOTIFICATION:
3588 d_s_o_clustermap_change_notification_req(tvb, pinfo, tree, offset, size);
3589 return;
3590 case SERVER_OPCODE_AUTHENTICATE:
3591 d_s_o_authenticate_req(tvb, pinfo, tree, offset, size);
3592 return;
3593 case SERVER_OPCODE_ACTIVE_EXTERNAL_USERS:
3594 d_s_o_active_external_users_req(tvb, pinfo, tree, offset, size);
3595 return;
3596 case SERVER_OPCODE_GET_AUTHORIZATION:
3597 d_s_o_get_authorization_req(tvb, pinfo, tree, offset, size);
3598 return;
3599 default:
3600 // Unknown packet type.. just dump the data
3601 if (size > 0) {
3602 proto_tree_add_item(tree, hf_value, tvb, offset, size,
3603 ENC_ASCII | ENC_NA);
3605 return;
3609 static void dissect_server_response_value(tvbuff_t *tvb,
3610 packet_info *pinfo,
3611 proto_tree *tree,
3612 int offset,
3613 int size) {
3614 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
3615 val_to_str_ext(get_status(tvb), &status_vals_ext,
3616 "Unknown status: 0x%x"));
3618 switch (get_opcode(tvb)) {
3619 case SERVER_OPCODE_CLUSTERMAP_CHANGE_NOTIFICATION:
3620 d_s_o_server_ignored_response(tvb, pinfo, tree, offset, size);
3621 return;
3622 case SERVER_OPCODE_AUTHENTICATE:
3623 d_s_o_authenticate_res(tvb, pinfo, tree, offset, size);
3624 return;
3625 case SERVER_OPCODE_ACTIVE_EXTERNAL_USERS:
3626 d_s_o_server_ignored_response(tvb, pinfo, tree, offset, size);
3627 return;
3628 case SERVER_OPCODE_GET_AUTHORIZATION:
3629 d_s_o_get_authorization_res(tvb, pinfo, tree, offset, size);
3630 return;
3631 default:
3632 // Unknown packet type.. just dump the data
3633 if (size > 0) {
3634 proto_tree_add_item(tree, hf_value, tvb, offset, size,
3635 ENC_ASCII | ENC_NA);
3637 return;
3642 * Dissect the value section in the frame
3644 static void dissect_frame_value(tvbuff_t *tvb,
3645 packet_info *pinfo,
3646 proto_tree *tree,
3647 int offset,
3648 uint32_t size,
3649 uint8_t magic,
3650 uint8_t opcode,
3651 uint16_t subdoc_path_len) {
3652 if (size > INT32_MAX) {
3653 // The packet size isn't supported
3656 switch (magic) {
3657 case MAGIC_CLIENT_REQUEST:
3658 case MAGIC_CLIENT_RESPONSE:
3659 case MAGIC_CLIENT_REQUEST_FLEX:
3660 case MAGIC_CLIENT_RESPONSE_FLEX:
3661 dissect_client_value(tvb, pinfo, tree, offset, size, magic, opcode, subdoc_path_len);
3662 return;
3663 case MAGIC_SERVER_REQUEST:
3664 dissect_server_request_value(tvb, pinfo, tree, offset, (int)size);
3665 return;
3666 case MAGIC_SERVER_RESPONSE:
3667 dissect_server_response_value(tvb, pinfo, tree, offset, (int)size);
3668 return;
3669 default:
3670 // Unknown magic... just dump the data
3671 if (size > 0) {
3672 proto_tree_add_item(tree, hf_value, tvb, offset, (int)size, ENC_ASCII | ENC_NA);
3674 return;
3679 * Each frame in the protocol consists of a 24 byte header, followed by
3680 * a variable number of sections (all of the sizes is located in the
3681 * first 24 byte header):
3683 * |---------------------------------------|
3684 * | Fixed 24 byte frame header |
3685 * |---------------------------------------|
3686 * | n bytes flex frame info |
3687 * |---------------------------------------|
3688 * | n bytes extras |
3689 * |---------------------------------------|
3690 * | n bytes key |
3691 * |---------------------------------------|
3692 * | n bytes value |
3693 * |---------------------------------------|
3695 * Call each function responsible for printing the segment
3697 static int
3698 dissect_couchbase(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
3700 col_set_str(pinfo->cinfo, COL_PROTOCOL, PSNAME);
3701 col_clear(pinfo->cinfo, COL_INFO);
3703 proto_item *couchbase_item = proto_tree_add_item(tree, proto_couchbase, tvb, 0, -1, ENC_NA);
3704 proto_tree *couchbase_tree = proto_item_add_subtree(couchbase_item, ett_couchbase);
3706 dissect_frame_header(tvb, pinfo, couchbase_tree, couchbase_item);
3707 uint8_t magic = get_magic(tvb);
3708 int offset = 24;
3710 uint8_t flex_frame_extra_len = get_flex_framing_extras_length(tvb);
3711 uint8_t opcode = get_opcode(tvb);
3712 uint8_t extras_length = get_extras_length(tvb);
3713 uint16_t key_length = get_key_length(tvb);
3714 uint32_t body_length = get_body_length(tvb);
3715 uint32_t value_len = body_length - key_length - extras_length - flex_frame_extra_len;
3717 dissect_frame_flex_info_section(tvb, pinfo, couchbase_tree, offset, flex_frame_extra_len, magic);
3718 offset += flex_frame_extra_len;
3720 uint16_t subdoc_path_len = 0;
3721 // Dissect the extras section
3722 dissect_frame_extras(tvb, pinfo, couchbase_tree, offset, extras_length, magic, opcode, &subdoc_path_len);
3723 offset += extras_length;
3725 // dissect the key
3726 dissect_frame_key(tvb, pinfo, couchbase_tree, offset, key_length, magic, opcode);
3727 offset += key_length;
3729 dissect_frame_value(tvb, pinfo, couchbase_tree, offset, value_len, magic, opcode, subdoc_path_len);
3730 return tvb_reported_length(tvb);
3733 static unsigned
3734 get_couchbase_pdu_length(packet_info *pinfo _U_, tvbuff_t *tvb, int offset,
3735 void *data _U_) {
3736 // See https://github.com/couchbase/kv_engine/blob/master/docs/BinaryProtocol.md#packet-structure
3737 // for a description of each packet.
3738 // The "length" field is located at offset 8 within the frame and does
3739 // not include the fixed header.
3740 return tvb_get_ntohl(tvb, offset + 8) + COUCHBASE_HEADER_LEN;
3743 /* Dissect the couchbase packet */
3744 static int
3745 dissect_couchbase_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
3746 void *data) {
3747 if (try_val_to_str(tvb_get_uint8(tvb, 0), magic_vals) == NULL) {
3748 // Magic isn't one of the know magics used by the Couchbase dissector
3749 return 0;
3752 tcp_dissect_pdus(tvb, pinfo, tree, couchbase_desegment_body,
3753 COUCHBASE_HEADER_LEN,
3754 get_couchbase_pdu_length, dissect_couchbase, data);
3755 return tvb_captured_length(tvb);
3759 /* Registration functions; register couchbase protocol,
3760 * its configuration options and also register the tcp dissectors.
3762 void
3763 proto_register_couchbase(void)
3765 static hf_register_info hf[] = {
3766 { &hf_magic, { "Magic", "couchbase.magic", FT_UINT8, BASE_HEX, VALS(magic_vals), 0x0, "Magic number", HFILL } },
3767 { &hf_opcode, { "Opcode", "couchbase.opcode", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &client_opcode_vals_ext, 0x0, "Command code", HFILL } },
3768 { &hf_server_opcode, { "Server Opcode", "couchbase.server.opcode", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &server_opcode_vals_ext, 0x0, "Command code", HFILL } },
3769 { &hf_extlength, { "Extras Length", "couchbase.extras.length", FT_UINT8, BASE_DEC, NULL, 0x0, "Length in bytes of the command extras", HFILL } },
3770 { &hf_keylength, { "Key Length", "couchbase.key.length", FT_UINT16, BASE_DEC, NULL, 0x0, "Length in bytes of the text key that follows the command extras", HFILL } },
3771 { &hf_value_length, { "Value Length", "couchbase.value.length", FT_UINT32, BASE_DEC, NULL, 0x0, "Length in bytes of the value that follows the key", HFILL } },
3772 { &hf_datatype, { "Data Type", "couchbase.datatype", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3773 { &hf_datatype_json, { "JSON", "couchbase.datatype.json", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DT_JSON, "JSON datatype", HFILL} },
3774 { &hf_datatype_snappy, { "Snappy", "couchbase.datatype.snappy", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DT_SNAPPY, "Snappy Compressed", HFILL} },
3775 { &hf_datatype_xattr, { "XATTR", "couchbase.datatype.xattr", FT_BOOLEAN, 8, TFS(&tfs_set_notset), DT_XATTR, "Xattrs included", HFILL} },
3776 { &hf_vbucket, { "VBucket", "couchbase.vbucket", FT_UINT16, BASE_DEC_HEX, NULL, 0x0, "VBucket ID", HFILL } },
3777 { &hf_status, { "Status", "couchbase.status", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &status_vals_ext, 0x0, "Status of the response", HFILL } },
3778 { &hf_total_bodylength, { "Total Body Length", "couchbase.total_bodylength", FT_UINT32, BASE_DEC, NULL, 0x0, "Length in bytes of extra + key + value", HFILL } },
3779 { &hf_opaque, { "Opaque", "couchbase.opaque", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
3780 { &hf_cas, { "CAS", "couchbase.cas", FT_UINT64, BASE_HEX, NULL, 0x0, "Data version check", HFILL } },
3781 { &hf_ttp, { "Time to Persist", "couchbase.ttp", FT_UINT32, BASE_DEC, NULL, 0x0, "Approximate time needed to persist the key (milliseconds)", HFILL } },
3782 { &hf_ttr, { "Time to Replicate", "couchbase.ttr", FT_UINT32, BASE_DEC, NULL, 0x0, "Approximate time needed to replicate the key (milliseconds)", HFILL } },
3784 { &hf_collection_key_id, { "Collection ID", "couchbase.key.collection_id", FT_UINT32, BASE_HEX, NULL, 0x0, "If this a collection stream, this is the collection-ID", HFILL } },
3785 { &hf_collection_key_logical, { "Collection Logical Key", "couchbase.key.logical_key", FT_STRING, BASE_NONE, NULL, 0x0, "If this a collection stream, this is the key in the collection", HFILL } },
3786 { &hf_collection_manifest_id, { "Collections Manifest ID", "couchbase.key.collection_manifest_id", FT_UINT64, BASE_HEX, NULL, 0x0, "The collections manifest id", HFILL } },
3788 { &hf_flex_keylength, { "Key Length", "couchbase.key.length", FT_UINT8, BASE_DEC, NULL, 0x0, "Length in bytes of the text key that follows the command extras", HFILL } },
3789 { &hf_flex_extras_length, { "Flexible Framing Extras Length", "couchbase.flex_extras", FT_UINT8, BASE_DEC, NULL, 0x0, "Length in bytes of the flexible framing extras that follows the response header", HFILL } },
3790 { &hf_flex_extras, {"Flexible Framing Extras", "couchbase.flex_frame_extras", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3791 { &hf_flex_extras_n, {"Flexible Framing Extras", "couchbase.flex_frame_extras.string", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3793 { &hf_flex_frame_id_byte0, {"Flexible Frame Byte0", "couchbase.flex_frame.byte0", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3795 { &hf_flex_frame_id_req, {"Flexible Frame ID (request)", "couchbase.flex_frame.frame.id", FT_UINT8, BASE_DEC, VALS(flex_frame_request_ids), 0x0, NULL, HFILL } },
3796 { &hf_flex_frame_id_res, {"Flexible Frame ID (response)", "couchbase.flex_frame.frame.id", FT_UINT8, BASE_DEC, VALS(flex_frame_response_ids), 0x0, NULL, HFILL } },
3797 { &hf_flex_frame_id_req_esc, {"Flexible Frame ID esc (request)", "couchbase.flex_frame.frame.id", FT_UINT16, BASE_DEC, VALS(flex_frame_request_ids), 0x0, NULL, HFILL } },
3798 { &hf_flex_frame_id_res_esc, {"Flexible Frame ID esc (response)", "couchbase.flex_frame.frame.id", FT_UINT16, BASE_DEC, VALS(flex_frame_response_ids), 0x0, NULL, HFILL } },
3801 { &hf_flex_frame_len, {"Flexible Frame Len", "couchbase.flex_frame.frame.len", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3802 { &hf_flex_frame_len_esc, {"Flexible Frame Len (esc)", "couchbase.flex_frame.frame.len", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3804 { &hf_flex_frame_tracing_duration, {"Server Recv->Send duration", "couchbase.flex_frame.frame.duration", FT_DOUBLE, BASE_NONE|BASE_UNIT_STRING, UNS(&units_microseconds), 0, NULL, HFILL } },
3805 { &hf_flex_frame_ru_count, {"Read unit count", "couchbase.flex_frame.frame.ru_count", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
3806 { &hf_flex_frame_wu_count, {"Write unit count", "couchbase.flex_frame.frame.wu_count", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
3807 { &hf_flex_frame_durability_req, {"Durability Requirement", "couchbase.flex_frame.frame.durability_req", FT_UINT8, BASE_DEC, VALS(flex_frame_durability_req), 0, NULL, HFILL } },
3808 { &hf_flex_frame_dcp_stream_id, {"DCP Stream Identifier", "couchbase.flex_frame.frame.dcp_stream_id", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
3809 { &hf_flex_frame_impersonated_user, {"Impersonated User", "couchbase.flex_frame.frame.impersonated_user", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
3811 { &hf_extras, { "Extras", "couchbase.extras", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3812 { &hf_extras_flags, { "Flags", "couchbase.extras.flags", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
3813 { &hf_extras_flags_backfill, { "Backfill Age", "couchbase.extras.flags.backfill", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0001, NULL, HFILL } },
3814 { &hf_extras_flags_dump, { "Dump", "couchbase.extras.flags.dump", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0002, NULL, HFILL } },
3815 { &hf_extras_flags_list_vbuckets, { "List VBuckets", "couchbase.extras.flags.list_vbuckets", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0004, NULL, HFILL } },
3816 { &hf_extras_flags_takeover_vbuckets, { "Takeover VBuckets", "couchbase.extras.flags.takeover_vbuckets", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0008, NULL, HFILL } },
3817 { &hf_extras_flags_support_ack, { "Support ACK", "couchbase.extras.flags.support_ack", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0010, NULL, HFILL } },
3818 { &hf_extras_flags_request_keys_only, { "Request Keys Only", "couchbase.extras.flags.request_keys_only", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0020, NULL, HFILL } },
3819 { &hf_extras_flags_checkpoint, { "Checkpoint", "couchbase.extras.flags.checkpoint", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0040, NULL, HFILL } },
3821 /* Sub-document */
3822 { &hf_subdoc_flags, { "Subdoc flags", "couchbase.extras.subdoc.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3823 { &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} },
3824 { &hf_subdoc_flags_xattrpath, { "XATTR_PATH", "couchbase.extras.subdoc.flags.xattr_path", FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x04, "If set path refers to extended attribute (XATTR)", HFILL} },
3825 { &hf_subdoc_flags_expandmacros, { "EXPAND_MACROS", "couchbase.extras.subdoc.flags.expand_macros", FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x10, "Expand macro values inside XATTRs", HFILL} },
3826 { &hf_subdoc_flags_reserved, {"Reserved fields", "couchbase.extras.subdoc.flags.reserved", FT_UINT8, BASE_HEX, NULL, 0xEA, "A reserved field", HFILL} },
3827 { &hf_subdoc_doc_flags, { "Subdoc Doc flags", "couchbase.extras.subdoc.doc_flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3828 { &hf_subdoc_doc_flags_mkdoc, { "MKDOC", "couchbase.extras.subdoc.doc_flags.mkdoc", FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x01, "Create document if it does not exist, implies mkdir_p", HFILL} },
3829 { &hf_subdoc_doc_flags_add, { "ADD", "couchbase.extras.subdoc.doc_flags.add", FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x02, "Fail if doc already exists", HFILL} },
3830 { &hf_subdoc_doc_flags_accessdeleted, { "ACCESS_DELETED", "couchbase.extras.subdoc.doc_flags.access_deleted", FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x04, "Allow access to XATTRs for deleted documents", HFILL} },
3831 { &hf_subdoc_doc_flags_createasdeleted, { "CREATE_AS_DELETED", "couchbase.extras.subdoc.doc_flags.create_as_deleted", FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x08, "If the document does not exist then create it in the Deleted state, instead of the normal Alive state", HFILL} },
3832 { &hf_subdoc_doc_flags_revivedocument, { "REVIVE_DOCUMENT", "couchbase.extras.subdoc.doc_flags.revive_document", FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x10, "If the document exists in the Deleted state, revive it to the normal Alive state", HFILL} },
3833 { &hf_subdoc_doc_flags_replicaread, { "REPLICA_READ", "couchbase.extras.subdoc.doc_flags.replica_read", FT_BOOLEAN, 8, TFS(&tfs_set_notset), 0x20, "Operate on a replica vbucket instead of an active one", HFILL} },
3834 { &hf_subdoc_doc_flags_reserved, {"Reserved fields", "couchbase.extras.subdoc.doc_flags.reserved", FT_UINT8, BASE_HEX, NULL, 0xC0, "A reserved field", HFILL} },
3835 { &hf_extras_pathlen, { "Path Length", "couchbase.extras.pathlen", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3837 /* DCP flags */
3838 { &hf_extras_flags_dcp_connection_type, {"Connection Type", "couchbase.extras.flags.dcp_connection_type", FT_UINT32, BASE_HEX, VALS(dcp_connection_type_vals), 0x00000003, NULL, HFILL } },
3839 { &hf_extras_flags_dcp_add_stream_takeover, {"Take Over", "couchbase.extras.flags.dcp_add_stream_takeover", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0001, NULL, HFILL } },
3840 { &hf_extras_flags_dcp_add_stream_diskonly, {"Disk Only", "couchbase.extras.flags.dcp_add_stream_diskonly", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0002, NULL, HFILL } },
3841 { &hf_extras_flags_dcp_add_stream_latest, {"Latest", "couchbase.extras.flags.dcp_add_stream_latest", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0004, NULL, HFILL } },
3842 { &hf_extras_flags_dcp_snapshot_marker_memory, {"Memory", "couchbase.extras.flags.dcp_snapshot_marker_memory", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0001, NULL, HFILL } },
3843 { &hf_extras_flags_dcp_snapshot_marker_disk, {"Disk", "couchbase.extras.flags.dcp_snapshot_marker_disk", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0002, NULL, HFILL } },
3844 { &hf_extras_flags_dcp_snapshot_marker_chk, {"Chk", "couchbase.extras.flags.dcp_snapshot_marker_chk", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0004, NULL, HFILL } },
3845 { &hf_extras_flags_dcp_snapshot_marker_ack, {"Ack", "couchbase.extras.flags.dcp_snapshot_marker_ack", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0008, NULL, HFILL } },
3846 { &hf_extras_flags_dcp_snapshot_marker_history, {"History", "couchbase.extras.flags.dcp_snapshot_marker_history", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0010, NULL, HFILL } },
3847 { &hf_extras_flags_dcp_snapshot_marker_may_contain_dups, {"May Contain Duplicates", "couchbase.extras.flags.dcp_snapshot_marker_may_contain_duplicates", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0020, NULL, HFILL } },
3848 { &hf_extras_flags_dcp_include_xattrs, {"Include XATTRs", "couchbase.extras.flags.dcp_include_xattrs", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0004, "Indicates the server should include documents XATTRs", HFILL} },
3849 { &hf_extras_flags_dcp_no_value, {"No Value", "couchbase.extras.flags.dcp_no_value", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0008, "Indicates the server should strip off values", HFILL} },
3850 { &hf_extras_flags_dcp_collections, {"Enable Collections", "couchbase.extras.flags.dcp_collections", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0010, "Indicates the server should stream collections", HFILL} },
3851 { &hf_extras_flags_dcp_include_delete_times, {"Include Delete Times", "couchbase.extras.flags.dcp_include_delete_times", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0020, "Indicates the server should include delete timestamps", HFILL} },
3852 { &hf_extras_flags_dcp_oso_snapshot_begin, {"OSO Begin", "couchbase.extras.flags.dcp_oso_snapshot_begin", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0001, "The start of an OSO snapshot", HFILL} },
3853 { &hf_extras_flags_dcp_oso_snapshot_end, {"OSO End", "couchbase.extras.flags.dcp_oso_snapshot_end", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0002, "The end of an OSO snapshot", HFILL} },
3855 { &hf_extras_seqno, { "Sequence number", "couchbase.extras.seqno", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3856 { &hf_extras_mutation_seqno, { "Mutation Sequence Number", "couchbase.extras.mutation_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3857 { &hf_extras_opaque, { "Opaque (vBucket identifier)", "couchbase.extras.opaque", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
3858 { &hf_extras_reserved, { "Reserved", "couchbase.extras.reserved", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
3859 { &hf_extras_start_seqno, { "Start Sequence Number", "couchbase.extras.start_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3860 { &hf_extras_end_seqno, { "End Sequence Number", "couchbase.extras.end_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3861 { &hf_extras_high_completed_seqno, { "High Completed Sequence Number", "couchbase.extras.high_completed_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3862 { &hf_extras_max_visible_seqno, { "Max Visible Seqno", "couchbase.extras.max_visible_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3863 { &hf_extras_timestamp, { "PiTR timestamp", "couchbase.extras.timestamp", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3864 { &hf_extras_marker_version, { "Snapshot Marker Version", "couchbase.extras.marker_version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3865 { &hf_extras_vbucket_uuid, { "VBucket UUID", "couchbase.extras.vbucket_uuid", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL } },
3866 { &hf_extras_snap_start_seqno, { "Snapshot Start Sequence Number", "couchbase.extras.snap_start_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3867 { &hf_extras_snap_end_seqno, { "Snapshot End Sequence Number", "couchbase.extras.snap_end_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3868 { &hf_extras_by_seqno, { "by_seqno", "couchbase.extras.by_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3869 { &hf_extras_prepared_seqno, { "by_seqno (prepared)", "couchbase.extras.by_seqno_prepared", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3870 { &hf_extras_commit_seqno, { "by_seqno (commit)", "couchbase.extras.by_seqno_commit", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3871 { &hf_extras_abort_seqno, { "by_seqno (abort)", "couchbase.extras.by_seqno_abort", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3872 { &hf_extras_rev_seqno, { "rev_seqno", "couchbase.extras.rev_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3873 { &hf_extras_lock_time, { "lock_time", "couchbase.extras.lock_time", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3874 { &hf_extras_nmeta, { "nmeta", "couchbase.extras.nmeta", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3875 { &hf_extras_nru, { "nru", "couchbase.extras.nru", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
3876 { &hf_extras_deleted, { "deleted", "couchbase.extras.deleted", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3877 { &hf_extras_bytes_to_ack, { "bytes_to_ack", "couchbase.extras.bytes_to_ack", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3878 { &hf_extras_delete_time, { "delete_time", "couchbase.extras.delete_time", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3879 { &hf_extras_delete_unused, { "unused", "couchbase.extras.delete_unused", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3880 { &hf_extras_system_event_id, { "system_event_id", "couchbase.extras.system_event_id", FT_UINT32, BASE_DEC, VALS(dcp_system_event_id_vals), 0x0, NULL, HFILL } },
3881 { &hf_extras_system_event_version, { "system_event_version", "couchbase.extras.system_event_version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3882 { &hf_extras_dcp_oso_snapshot_flags, { "OSO snapshot flags", "couchbase.extras.dcp_oso_snapshot_flags", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3884 { &hf_failover_log, { "Failover Log", "couchbase.dcp.failover_log", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3885 { &hf_failover_log_size, { "Size", "couchbase.dcp.failover_log.size", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3886 { &hf_failover_log_vbucket_uuid, { "VBucket UUID", "couchbase.dcp.failover_log.vbucket_uuid", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL } },
3887 { &hf_failover_log_vbucket_seqno, { "Sequence Number", "couchbase.dcp.failover_log.seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3889 { &hf_vbucket_states, { "VBucket States", "couchbase.vbucket_states", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3890 { &hf_vbucket_states_state, { "State", "couchbase.vbucket_states.state", FT_UINT32, BASE_HEX, VALS(vbucket_states_vals), 0x0, NULL, HFILL } },
3891 { &hf_vbucket_states_size, { "Size", "couchbase.vbucket_states.size", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3892 { &hf_vbucket_states_id, { "VBucket", "couchbase.vbucket_states.id", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3893 { &hf_vbucket_states_seqno, { "Sequence Number", "couchbase.vbucket_states.seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3895 { &hf_extras_expiration, { "Expiration", "couchbase.extras.expiration", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3896 { &hf_extras_delta, { "Amount to Add", "couchbase.extras.delta", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3897 { &hf_extras_initial, { "Initial Value", "couchbase.extras.initial", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3898 { &hf_extras_unknown, { "Unknown", "couchbase.extras.unknown", FT_BYTES, BASE_NONE, NULL, 0x0, "Unknown Extras", HFILL } },
3899 { &hf_key, { "Key", "couchbase.key", FT_STRING, BASE_NONE, NULL, 0x0, "If this is a collection stream, the key is formed of a leb128 prefix and then the key", HFILL } },
3900 { &hf_path, { "Path", "couchbase.path", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3901 { &hf_value, { "Value", "couchbase.value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3902 { &hf_uint64_response, { "Response", "couchbase.extras.response", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3903 { &hf_observe, { "Observe", "couchbase.observe", FT_STRING, BASE_NONE, NULL, 0x0, "The observe properties", HFILL } },
3904 { &hf_observe_key, { "Key", "couchbase.observe.key", FT_STRING, BASE_NONE, NULL, 0x0, "The observable key", HFILL } },
3905 { &hf_observe_keylength, { "Key Length", "couchbase.observe.keylength", FT_UINT16, BASE_DEC, NULL, 0x0, "The length of the observable key", HFILL } },
3906 { &hf_observe_vbucket, { "VBucket", "couchbase.observe.vbucket", FT_UINT16, BASE_HEX, NULL, 0x0, "VBucket of the observable key", HFILL } },
3907 { &hf_observe_status, { "Status", "couchbase.observe.status", FT_UINT8, BASE_HEX, NULL, 0x0, "Status of the observable key", HFILL } },
3908 { &hf_observe_cas, { "CAS", "couchbase.observe.cas", FT_UINT64, BASE_HEX, NULL, 0x0, "CAS value of the observable key", HFILL } },
3909 { &hf_observe_vbucket_uuid, { "VBucket UUID", "couchbase.observe.vbucket_uuid", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL } },
3910 { &hf_observe_last_persisted_seqno, { "Last persisted sequence number", "couchbase.observe.last_persisted_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3911 { &hf_observe_current_seqno, { "Current sequence number", "couchbase.observe.current_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3912 { &hf_observe_old_vbucket_uuid, { "Old VBucket UUID", "couchbase.observe.old_vbucket_uuid", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL } },
3913 { &hf_observe_last_received_seqno, { "Last received sequence number", "couchbase.observe.last_received_seqno", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3914 { &hf_observe_failed_over, { "Failed over", "couchbase.observe.failed_over", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3916 { &hf_get_errmap_version, {"Version", "couchbase.geterrmap.version", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL} },
3918 { &hf_multipath_opcode, { "Opcode", "couchbase.multipath.opcode", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &client_opcode_vals_ext, 0x0, "Command code", HFILL } },
3919 { &hf_multipath_index, { "Index", "couchbase.multipath.index", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3920 { &hf_multipath_pathlen, { "Path Length", "couchbase.multipath.path.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3921 { &hf_multipath_path, { "Path", "couchbase.multipath.path", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3922 { &hf_multipath_valuelen, { "Value Length", "couchbase.multipath.value.length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3923 { &hf_multipath_value, { "Value", "couchbase.multipath.value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3925 { &hf_meta_flags, {"Flags", "couchbase.extras.flags", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3926 { &hf_meta_expiration, {"Expiration", "couchbase.extras.expiration", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3927 { &hf_meta_revseqno, {"RevSeqno", "couchbase.extras.revseqno", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3928 { &hf_meta_cas, {"CAS", "couchbase.extras.cas", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3929 { &hf_meta_options, {"Options", "couchbase.extras.options", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3930 { &hf_force_meta, {"FORCE_WITH_META_OP", "couchbase.extras.options.force_with_meta_op", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0001, NULL, HFILL} },
3931 { &hf_force_accept, {"FORCE_ACCEPT_WITH_META_OPS", "couchbase.extras.options.force_accept_with_meta_ops", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0002, NULL, HFILL} },
3932 { &hf_regenerate_cas, {"REGENERATE_CAS", "couchbase.extras.option.regenerate_cas", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0004, NULL, HFILL} },
3933 { &hf_skip_conflict, {"SKIP_CONFLICT_RESOLUTION", "couchbase.extras.options.skip_conflict_resolution", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0008, NULL, HFILL} },
3934 { &hf_is_expiration, {"IS_EXPIRATION", "couchbase.extras.options.is_expiration", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0010, NULL, HFILL} },
3935 { &hf_metalen, {"Meta Length", "couchbase.extras.meta_length", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3936 { &hf_meta_reqextmeta, {"ReqExtMeta", "couchbase.extras.reqextmeta", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3937 { &hf_meta_deleted, {"Deleted", "couchbase.extras.deleted", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3938 { &hf_exptime, {"Expiry", "couchbase.extras.expiry", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3939 { &hf_extras_meta_seqno, {"Seqno", "couchbase.extras.meta.seqno", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL} },
3940 { &hf_confres, {"ConfRes", "couchbase.extras.confres", FT_UINT8, BASE_HEX, NULL, 0x0, "Conflict Resolution Mode", HFILL} },
3942 { &hf_bucket_type, {"Bucket Type", "couchbase.bucket.type", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
3943 { &hf_bucket_config, {"Bucket Config", "couchbase.bucket.config", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
3944 { &hf_config_key, {"Key", "couchbase.bucket.config.key", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
3945 { &hf_config_value, {"Value", "couchbase.bucket.config.value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
3946 { &hf_hello_features, {"Hello Features", "couchbase.hello.features", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL} },
3947 { &hf_hello_features_feature, {"Feature", "couchbase.hello.features.feature", FT_UINT16, BASE_HEX, VALS(feature_vals), 0x0, NULL, HFILL} },
3949 { &hf_xattrs, { "XATTRs", "couchbase.xattrs", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL} },
3950 { &hf_xattr_length, { "XATTR Length", "couchbase.xattrs.length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} },
3951 { &hf_xattr_pair_length, { "XATTR Pair Length", "couchbase.xattrs.pair.length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL} },
3952 { &hf_xattr_key, { "Key", "couchbase.xattrs.pair.key", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
3953 { &hf_xattr_value, { "Value", "couchbase.xattrs.pair.value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
3956 { &hf_server_extras_cccp_epoch, { "Epoch", "couchbase.server.extras.cccp.epoch", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3957 { &hf_server_extras_cccp_revno, { "Revision", "couchbase.server.extras.cccp.revision", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3958 { &hf_server_clustermap_value, { "Clustermap", "couchbase.server.clustermap.value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3959 { &hf_server_authentication, { "Authentication", "couchbase.server.authentication", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3960 { &hf_server_external_users, { "External users", "couchbase.server.external_users", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3961 { &hf_server_get_authorization, { "Authorization", "couchbase.server.authorization", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3963 { &hf_range_scan_uuid, { "Range Scan UUID", "couchbase.range_scan.uuid", FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL } },
3964 { &hf_range_scan_item_limit, { "Range Scan item limit", "couchbase.range_scan.item_limit", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3965 { &hf_range_scan_time_limit, { "Range Scan time limit", "couchbase.range_scan.time_limit", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
3966 { &hf_range_scan_byte_limit, { "Range Scan byte limit", "couchbase.range_scan.byte_limit", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }
3969 static ei_register_info ei[] = {
3970 { &ei_value_missing, { "couchbase.value_missing", PI_PROTOCOL, PI_WARN, "Value is mandatory for this command", EXPFILL }},
3971 { &ei_warn_shall_not_have_value, { "couchbase.warn.shall_not_have_value", PI_UNDECODED, PI_WARN, "Packet shall not have value", EXPFILL }},
3972 { &ei_warn_shall_not_have_extras, { "couchbase.warn.shall_not_have_extras", PI_UNDECODED, PI_WARN, "Packet shall not have extras", EXPFILL }},
3973 { &ei_warn_shall_not_have_key, { "couchbase.warn.shall_not_have_key", PI_UNDECODED, PI_WARN, "Packet shall not have key", EXPFILL }},
3974 { &ei_warn_must_have_extras, { "couchbase.warn.must_have_extras", PI_UNDECODED, PI_WARN, "Packet must have extras", EXPFILL }},
3975 { &ei_warn_must_have_key, { "couchbase.warn.must_have_key", PI_UNDECODED, PI_WARN, "Message must have Key", EXPFILL }},
3976 { &ei_warn_illegal_extras_length, { "couchbase.warn.illegal_extras_length", PI_UNDECODED, PI_WARN, "Illegal Extras length", EXPFILL }},
3977 { &ei_warn_illegal_value_length, { "couchbase.warn.illegal_value_length", PI_UNDECODED, PI_WARN, "Illegal Value length", EXPFILL }},
3978 { &ei_warn_unknown_magic_byte, { "couchbase.warn.unknown_magic_byte", PI_UNDECODED, PI_WARN, "Unknown magic byte", EXPFILL }},
3979 { &ei_warn_unknown_opcode, { "couchbase.warn.unknown_opcode", PI_UNDECODED, PI_WARN, "Unknown opcode", EXPFILL }},
3980 { &ei_warn_unknown_extras, { "couchbase.warn.unknown_extras", PI_UNDECODED, PI_WARN, "Unknown extras", EXPFILL }},
3981 { &ei_note_status_code, { "couchbase.note.status_code", PI_RESPONSE_CODE, PI_NOTE, "Status", EXPFILL }},
3982 { &ei_separator_not_found, { "couchbase.warn.separator_not_found", PI_UNDECODED, PI_WARN, "Separator not found", EXPFILL }},
3983 { &ei_illegal_value, { "couchbase.warn.illegal_value", PI_UNDECODED, PI_WARN, "Illegal value for command", EXPFILL }},
3984 { &ei_compression_error, { "couchbase.error.compression", PI_UNDECODED, PI_WARN, "Compression error", EXPFILL }},
3985 { &ei_warn_unknown_flex_unsupported, { "couchbase.warn.unsupported_flexible_frame", PI_UNDECODED, PI_WARN, "Unsupported Flexible encoding", EXPFILL }},
3986 { &ei_warn_unknown_flex_id, { "couchbase.warn.unknown_flexible_frame_id", PI_UNDECODED, PI_WARN, "Flexible Response ID warning", EXPFILL }},
3987 { &ei_warn_unknown_flex_len, { "couchbase.warn.unknown_flexible_frame_len", PI_UNDECODED, PI_WARN, "Flexible Response Length warning", EXPFILL }}
3990 static int *ett[] = {
3991 &ett_couchbase,
3992 &ett_extras,
3993 &ett_flex_frame_extras,
3994 &ett_extras_flags,
3995 &ett_observe,
3996 &ett_failover_log,
3997 &ett_vbucket_states,
3998 &ett_multipath,
3999 &ett_config,
4000 &ett_config_key,
4001 &ett_hello_features,
4002 &ett_datatype,
4003 &ett_xattrs,
4004 &ett_xattr_pair,
4005 &ett_collection_key
4008 module_t *couchbase_module;
4009 expert_module_t* expert_couchbase;
4011 proto_couchbase = proto_register_protocol(PNAME, PSNAME, PFNAME);
4013 proto_register_field_array(proto_couchbase, hf, array_length(hf));
4014 proto_register_subtree_array(ett, array_length(ett));
4016 expert_couchbase = expert_register_protocol(proto_couchbase);
4017 expert_register_field_array(expert_couchbase, ei, array_length(ei));
4019 /* Register our configuration options */
4020 couchbase_module = prefs_register_protocol(proto_couchbase, &proto_reg_handoff_couchbase);
4022 couchbase_handle = register_dissector("couchbase", dissect_couchbase_pdu, proto_couchbase);
4024 prefs_register_bool_preference(couchbase_module, "desegment_pdus",
4025 "Reassemble PDUs spanning multiple TCP segments",
4026 "Whether the Couchbase dissector should reassemble PDUs"
4027 " spanning multiple TCP segments."
4028 " To use this option, you must also enable \"Allow subdissectors"
4029 " to reassemble TCP streams\" in the TCP protocol settings.",
4030 &couchbase_desegment_body);
4032 prefs_register_uint_preference(couchbase_module, "tls.port", "SSL/TLS Data Port",
4033 "The port used for communicating with the data service via SSL/TLS",
4034 10, &couchbase_ssl_port_pref);
4035 prefs_register_obsolete_preference(couchbase_module, "ssl_port");
4038 /* Register the tcp couchbase dissector. */
4039 void
4040 proto_reg_handoff_couchbase(void)
4042 static bool initialized = false;
4044 if (!initialized){
4045 json_handle = find_dissector_add_dependency("json", proto_couchbase);
4046 dissector_add_uint_range_with_preference("tcp.port", COUCHBASE_DEFAULT_PORT, couchbase_handle);
4047 initialized = true;
4048 } else {
4049 ssl_dissector_delete(couchbase_ssl_port, couchbase_handle);
4051 couchbase_ssl_port = couchbase_ssl_port_pref;
4052 ssl_dissector_add(couchbase_ssl_port, couchbase_handle);
4056 * Editor modelines
4058 * Local Variables:
4059 * c-basic-offset: 2
4060 * tab-width: 8
4061 * indent-tabs-mode: nil
4062 * End:
4064 * ex: set shiftwidth=2 tabstop=8 expandtab:
4065 * :indentSize=2:tabSize=8:noTabs=true: