2 * Routines for Memcache Binary Protocol
3 * http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol
5 * Copyright 2009, Stig Bjorlykke <stig@bjorlykke.org>
7 * Routines for Memcache Textual Protocol
8 * http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
10 * Copyright 2009, Rama Chitta <rama@gear6.com>
12 * Wireshark - Network traffic analyzer
13 * By Gerald Combs <gerald@wireshark.org>
14 * Copyright 1998 Gerald Combs
16 * SPDX-License-Identifier: GPL-2.0-or-later
21 #include <stdio.h> /* for sscanf() */
22 #include <stdlib.h> /* for strtoul() */
24 #include <epan/packet.h>
25 #include <epan/strutil.h>
26 #include <epan/prefs.h>
27 #include <epan/expert.h>
29 #include "packet-tcp.h"
31 void proto_register_memcache (void);
32 void proto_reg_handoff_memcache(void);
34 #define PNAME "Memcache Protocol"
35 #define PSNAME "MEMCACHE"
36 #define PFNAME "memcache"
38 #define MEMCACHE_DEFAULT_RANGE "11211"
39 #define MEMCACHE_HEADER_LEN 24
42 #define MAGIC_REQUEST 0x80
43 #define MAGIC_RESPONSE 0x81
46 #define RS_NO_ERROR 0x0000
47 #define RS_KEY_NOT_FOUND 0x0001
48 #define RS_KEY_EXISTS 0x0002
49 #define RS_VALUE_TOO_BIG 0x0003
50 #define RS_INVALID_ARGUMENTS 0x0004
51 #define RS_ITEM_NOT_STORED 0x0005
52 #define RS_UNKNOWN_COMMAND 0x0081
53 #define RS_OUT_OF_MEMORY 0x0082
59 #define OP_REPLACE 0x03
60 #define OP_DELETE 0x04
61 #define OP_INCREMENT 0x05
62 #define OP_DECREMENT 0x06
67 #define OP_VERSION 0x0B
69 #define OP_GET_K_Q 0x0D
70 #define OP_APPEND 0x0E
71 #define OP_PREPEND 0x0F
75 #define OP_REPLACE_Q 0x13
76 #define OP_DELETE_Q 0x14
77 #define OP_INCREMENT_Q 0x15
78 #define OP_DECREMENT_Q 0x16
79 #define OP_QUIT_Q 0x17
80 #define OP_FLUSH_Q 0x18
81 #define OP_APPEND_Q 0x19
82 #define OP_PREPEND_Q 0x1A
84 /* Internally defined command opcodes used in the textual dissector only */
85 /* This values are not defined in any standard and can be redefined here */
88 #define OP_VERBOSE 0xF2
91 #define DT_RAW_BYTES 0x00
93 static int proto_memcache
;
95 static dissector_handle_t memcache_tcp_handle
;
96 static dissector_handle_t memcache_udp_handle
;
100 static int hf_extras_length
;
101 static int hf_key_length
;
102 static int hf_value_length
;
103 static int hf_data_type
;
104 static int hf_reserved
;
105 static int hf_status
;
106 static int hf_total_body_length
;
107 static int hf_opaque
;
109 static int hf_extras
;
110 static int hf_extras_flags
;
111 static int hf_extras_expiration
;
112 static int hf_extras_delta
;
113 static int hf_extras_initial
;
114 static int hf_extras_unknown
;
117 static int hf_uint64_response
;
119 static int hf_command
;
120 static int hf_subcommand
;
122 static int hf_expiration
;
123 static int hf_noreply
;
125 static int hf_response
;
127 static int hf_version
;
128 static int hf_slabclass
;
130 static int hf_name_value
;
132 static int ett_memcache
;
133 static int ett_extras
;
135 static expert_field ei_value_missing
;
136 static expert_field ei_extras_missing
;
137 static expert_field ei_value_length
;
138 static expert_field ei_key_missing
;
139 static expert_field ei_key_unknown
;
140 static expert_field ei_extras_unknown
;
141 static expert_field ei_value_unknown
;
142 static expert_field ei_status_response
;
143 static expert_field ei_opcode_unknown
;
144 static expert_field ei_reserved_value
;
145 static expert_field ei_magic_unknown
;
147 static const value_string magic_vals
[] = {
148 { MAGIC_REQUEST
, "Request" },
149 { MAGIC_RESPONSE
, "Response" },
153 static const value_string status_vals
[] = {
154 { RS_NO_ERROR
, "No error" },
155 { RS_KEY_NOT_FOUND
, "Key not found" },
156 { RS_KEY_EXISTS
, "Key exists" },
157 { RS_VALUE_TOO_BIG
, "Value too big" },
158 { RS_INVALID_ARGUMENTS
, "Invalid arguments" },
159 { RS_ITEM_NOT_STORED
, "Item not stored" },
160 { RS_UNKNOWN_COMMAND
, "Unknown command" },
161 { RS_OUT_OF_MEMORY
, "Out of memory" },
165 static const value_string opcode_vals
[] = {
169 { OP_REPLACE
, "Replace" },
170 { OP_DELETE
, "Delete" },
171 { OP_INCREMENT
, "Increment" },
172 { OP_DECREMENT
, "Decrement" },
174 { OP_FLUSH
, "Flush" },
175 { OP_GET_Q
, "Get Quietly" },
176 { OP_NO_OP
, "No-op" },
177 { OP_VERSION
, "Version" },
178 { OP_GET_K
, "Get Key" },
179 { OP_GET_K_Q
, "Get Key Quietly" },
180 { OP_APPEND
, "Append" },
181 { OP_PREPEND
, "Prepend" },
182 { OP_STAT
, "Statistics" },
183 { OP_SET_Q
, "Set Quietly" },
184 { OP_ADD_Q
, "Add Quietly" },
185 { OP_REPLACE_Q
, "Replace Quietly" },
186 { OP_DELETE_Q
, "Delete Quietly" },
187 { OP_INCREMENT_Q
, "Increment Quietly" },
188 { OP_DECREMENT_Q
, "Decrement Quietly" },
189 { OP_QUIT_Q
, "Quit Quietly" },
190 { OP_FLUSH_Q
, "Flush Quietly" },
191 { OP_APPEND_Q
, "Append Quietly" },
192 { OP_PREPEND_Q
, "Prepend Quietly" },
193 /* Internally defined values not valid here */
197 static const value_string data_type_vals
[] = {
198 { DT_RAW_BYTES
, "Raw bytes" },
202 /* memcache message types. */
203 typedef enum _memcache_type
{
209 /* desegmentation of MEMCACHE header */
210 static bool memcache_desegment_headers
= true;
212 /* desegmentation of MEMCACHE payload */
213 static bool memcache_desegment_body
= true;
215 /* should refer to either the request or the response dissector.
217 typedef int (*ReqRespDissector
)(tvbuff_t
*, packet_info
*, proto_tree
*,
218 int, const unsigned char*, const unsigned char*, uint8_t);
220 /* determines if a packet contains a memcache
221 * request or reply by looking at its first token.
224 is_memcache_request_or_reply(const char *data
, int linelen
, uint8_t *opcode
,
225 memcache_type_t
*type
, bool *expect_content_length
,
226 ReqRespDissector
*reqresp_dissector
);
229 get_memcache_pdu_len (packet_info
*pinfo _U_
, tvbuff_t
*tvb
,
230 int offset
, void *data _U_
)
234 /* Get the length of the memcache body */
235 body_len
= tvb_get_ntohl(tvb
, offset
+8);
237 /* That length doesn't include the header; add that in */
238 return body_len
+ MEMCACHE_HEADER_LEN
;
242 dissect_extras (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
243 int offset
, uint8_t extras_len
, uint8_t opcode
, bool request
)
245 proto_tree
*extras_tree
= NULL
;
246 proto_item
*extras_item
= NULL
, *ti
;
247 int save_offset
= offset
;
248 bool illegal
= false; /* Set when extras shall not be present */
249 bool missing
= false; /* Set when extras is missing */
252 extras_item
= proto_tree_add_item (tree
, hf_extras
, tvb
, offset
, extras_len
, ENC_NA
);
253 extras_tree
= proto_item_add_subtree (extras_item
, ett_extras
);
264 /* Request shall not have extras */
267 proto_tree_add_item (extras_tree
, hf_extras_flags
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
270 } else if (!request
) {
271 /* Response must have extras */
284 proto_tree_add_item (extras_tree
, hf_extras_flags
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
287 proto_tree_add_item (extras_tree
, hf_extras_expiration
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
290 /* Response shall not have extras */
293 } else if (request
) {
294 /* Request must have extras */
305 proto_tree_add_item (extras_tree
, hf_extras_delta
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
308 proto_tree_add_item (extras_tree
, hf_extras_initial
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
311 proto_tree_add_item (extras_tree
, hf_extras_expiration
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
314 /* Response must not have extras (response is in Value) */
317 } else if (request
) {
318 /* Request must have extras */
326 proto_tree_add_item (extras_tree
, hf_extras_expiration
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
341 /* Must not have extras */
349 /* Decode as unknown extras */
350 proto_tree_add_item (extras_tree
, hf_extras_unknown
, tvb
, offset
, extras_len
, ENC_NA
);
351 offset
+= extras_len
;
357 ti
= proto_tree_add_item (extras_tree
, hf_extras_unknown
, tvb
, offset
, extras_len
, ENC_NA
);
358 expert_add_info_format(pinfo
, ti
, &ei_extras_unknown
, "%s %s shall not have Extras",
359 val_to_str (opcode
, opcode_vals
, "Opcode %d"),
360 request
? "Request" : "Response");
361 offset
+= extras_len
;
362 } else if (missing
) {
363 proto_tree_add_expert_format(tree
, pinfo
, &ei_extras_missing
, tvb
, offset
, 0, "%s %s must have Extras",
364 val_to_str (opcode
, opcode_vals
, "Opcode %d"),
365 request
? "Request" : "Response");
368 if ((offset
- save_offset
) != extras_len
) {
369 expert_add_info_format(pinfo
, extras_item
, &ei_extras_unknown
, "Illegal Extras length, should be %d", offset
- save_offset
);
374 dissect_key (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
375 int offset
, int key_len
, uint8_t opcode
, bool request
)
377 proto_item
*ti
= NULL
;
378 bool illegal
= false; /* Set when key shall not be present */
379 bool missing
= false; /* Set when key is missing */
382 ti
= proto_tree_add_item (tree
, hf_key
, tvb
, offset
, key_len
, ENC_ASCII
);
385 if ((opcode
== OP_QUIT
) || (opcode
== OP_QUIT_Q
) || (opcode
== OP_NO_OP
) || (opcode
== OP_VERSION
)) {
386 /* Request and Response must not have key */
389 if ((opcode
== OP_SET
) || (opcode
== OP_ADD
) || (opcode
== OP_REPLACE
) || (opcode
== OP_DELETE
) ||
390 (opcode
== OP_SET_Q
) || (opcode
== OP_ADD_Q
) || (opcode
== OP_REPLACE_Q
) || (opcode
== OP_DELETE_Q
) ||
391 (opcode
== OP_FLUSH
) || (opcode
== OP_APPEND
) || (opcode
== OP_PREPEND
) ||
392 (opcode
== OP_FLUSH_Q
) || (opcode
== OP_APPEND_Q
) || (opcode
== OP_PREPEND_Q
))
394 /* Response must not have a key */
400 if ((opcode
== OP_GET
) || (opcode
== OP_GET_Q
) || (opcode
== OP_GET_K
) || (opcode
== OP_GET_K_Q
) ||
401 (opcode
== OP_SET
) || (opcode
== OP_ADD
) || (opcode
== OP_REPLACE
) || (opcode
== OP_DELETE
) ||
402 (opcode
== OP_SET_Q
) || (opcode
== OP_ADD_Q
) || (opcode
== OP_REPLACE_Q
) || (opcode
== OP_DELETE_Q
) ||
403 (opcode
== OP_INCREMENT
) || (opcode
== OP_DECREMENT
) || (opcode
== OP_INCREMENT_Q
) || (opcode
== OP_DECREMENT_Q
))
405 /* Request must have key */
413 expert_add_info_format(pinfo
, ti
, &ei_key_unknown
, "%s %s shall not have Key",
414 val_to_str (opcode
, opcode_vals
, "Opcode %d"),
415 request
? "Request" : "Response");
416 } else if (missing
) {
417 proto_tree_add_expert_format(tree
, pinfo
, &ei_key_missing
, tvb
, offset
, 0, "%s Request must have Key",
418 val_to_str (opcode
, opcode_vals
, "Opcode %d"));
423 dissect_value (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
424 int offset
, uint32_t value_len
, uint8_t opcode
, bool request
)
426 proto_item
*ti
= NULL
;
427 bool illegal
= false; /* Set when value shall not be present */
428 bool missing
= false; /* Set when value is missing */
431 if (!request
&& ((opcode
== OP_INCREMENT
) || (opcode
== OP_DECREMENT
))) {
432 ti
= proto_tree_add_item (tree
, hf_uint64_response
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
433 if (value_len
!= 8) {
434 expert_add_info_format(pinfo
, ti
, &ei_value_length
, "Illegal Value length, should be 8");
437 ti
= proto_tree_add_item (tree
, hf_value
, tvb
, offset
, value_len
, ENC_ASCII
);
444 if ((opcode
== OP_GET
) || (opcode
== OP_GET_Q
) || (opcode
== OP_GET_K
) || (opcode
== OP_GET_K_Q
) ||
445 (opcode
== OP_INCREMENT
) || (opcode
== OP_DECREMENT
) || (opcode
== OP_VERSION
) ||
446 (opcode
== OP_INCREMENT_Q
) || (opcode
== OP_DECREMENT_Q
))
448 /* Request must not have value */
453 if ((opcode
== OP_DELETE
) || (opcode
== OP_QUIT
) || (opcode
== OP_FLUSH
) || (opcode
== OP_NO_OP
) ||
454 (opcode
== OP_DELETE_Q
) || (opcode
== OP_QUIT_Q
) || (opcode
== OP_FLUSH_Q
))
456 /* Request and Response must not have value */
459 if ((opcode
== OP_SET
) || (opcode
== OP_ADD
) || (opcode
== OP_REPLACE
) ||
460 (opcode
== OP_SET_Q
) || (opcode
== OP_ADD_Q
) || (opcode
== OP_REPLACE_Q
) ||
461 (opcode
== OP_APPEND
) || (opcode
== OP_PREPEND
) || (opcode
== OP_APPEND_Q
) || (opcode
== OP_PREPEND_Q
))
463 /* Response must not have value */
469 if ((opcode
== OP_SET
) || (opcode
== OP_ADD
) || (opcode
== OP_REPLACE
) ||
470 (opcode
== OP_SET_Q
) || (opcode
== OP_ADD_Q
) || (opcode
== OP_REPLACE_Q
) ||
471 (opcode
== OP_APPEND
) || (opcode
== OP_PREPEND
) || (opcode
== OP_APPEND_Q
) || (opcode
== OP_PREPEND_Q
))
473 /* Request must have a value */
481 expert_add_info_format(pinfo
, ti
, &ei_value_unknown
, "%s %s shall not have Value",
482 val_to_str (opcode
, opcode_vals
, "Opcode %d"),
483 request
? "Request" : "Response");
484 } else if (missing
) {
485 proto_tree_add_expert_format(tree
, pinfo
, &ei_value_missing
, tvb
, offset
, 0, "%s %s must have Value",
486 val_to_str (opcode
, opcode_vals
, "Opcode %d"),
487 request
? "Request" : "Response");
492 dissect_memcache (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
494 proto_tree
*memcache_tree
;
495 proto_item
*memcache_item
, *ti
;
497 uint8_t magic
, opcode
, extras_len
;
498 uint16_t key_len
, status
= 0;
499 uint32_t body_len
, value_len
;
502 col_set_str (pinfo
->cinfo
, COL_PROTOCOL
, PSNAME
);
503 col_clear (pinfo
->cinfo
, COL_INFO
);
505 memcache_item
= proto_tree_add_item (tree
, proto_memcache
, tvb
, offset
, -1, ENC_NA
);
506 memcache_tree
= proto_item_add_subtree (memcache_item
, ett_memcache
);
508 magic
= tvb_get_uint8 (tvb
, offset
);
509 ti
= proto_tree_add_item (memcache_tree
, hf_magic
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
512 if (try_val_to_str (magic
, magic_vals
) == NULL
) {
513 expert_add_info_format(pinfo
, ti
, &ei_magic_unknown
, "Unknown magic byte: %d", magic
);
516 opcode
= tvb_get_uint8 (tvb
, offset
);
517 ti
= proto_tree_add_item (memcache_tree
, hf_opcode
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
520 if (try_val_to_str (opcode
, opcode_vals
) == NULL
) {
521 expert_add_info_format(pinfo
, ti
, &ei_opcode_unknown
, "Unknown opcode: %d", opcode
);
524 proto_item_append_text (memcache_item
, ", %s %s", val_to_str (opcode
, opcode_vals
, "Unknown opcode (%d)"),
525 val_to_str (magic
, magic_vals
, "Unknown magic (%d)"));
527 col_append_fstr (pinfo
->cinfo
, COL_INFO
, "%s %s",
528 val_to_str (opcode
, opcode_vals
, "Unknown opcode (%d)"),
529 val_to_str (magic
, magic_vals
, "Unknown magic (%d)"));
531 key_len
= tvb_get_ntohs (tvb
, offset
);
532 proto_tree_add_item (memcache_tree
, hf_key_length
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
535 extras_len
= tvb_get_uint8 (tvb
, offset
);
536 proto_tree_add_item (memcache_tree
, hf_extras_length
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
539 proto_tree_add_item (memcache_tree
, hf_data_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
542 status
= tvb_get_ntohs (tvb
, offset
);
543 if (magic
& 0x01) { /* We suppose this is a response, even when unknown magic byte */
545 ti
= proto_tree_add_item (memcache_tree
, hf_status
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
547 expert_add_info_format(pinfo
, ti
, &ei_status_response
, "%s: %s",
548 val_to_str (opcode
, opcode_vals
, "Unknown opcode (%d)"),
549 val_to_str (status
, status_vals
, "Status: %d"));
553 ti
= proto_tree_add_item (memcache_tree
, hf_reserved
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
555 expert_add_info_format(pinfo
, ti
, &ei_reserved_value
, "Reserved value: %d", status
);
560 body_len
= tvb_get_ntohl (tvb
, offset
);
561 value_len
= body_len
- extras_len
- key_len
;
562 ti
= proto_tree_add_uint (memcache_tree
, hf_value_length
, tvb
, offset
, 0, value_len
);
563 proto_item_set_generated (ti
);
565 proto_tree_add_item (memcache_tree
, hf_total_body_length
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
568 proto_tree_add_item (memcache_tree
, hf_opaque
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
571 proto_tree_add_item (memcache_tree
, hf_cas
, tvb
, offset
, 8, ENC_BIG_ENDIAN
);
575 dissect_extras (tvb
, pinfo
, memcache_tree
, offset
, extras_len
, opcode
, request
);
576 offset
+= extras_len
;
578 dissect_key (tvb
, pinfo
, memcache_tree
, offset
, key_len
, opcode
, request
);
581 dissect_value (tvb
, pinfo
, memcache_tree
, offset
, value_len
, opcode
, request
);
582 /*offset += value_len;*/
583 } else if (body_len
) {
584 proto_tree_add_item (memcache_tree
, hf_value
, tvb
, offset
, body_len
, ENC_ASCII
);
585 /*offset += body_len;*/
587 col_append_fstr (pinfo
->cinfo
, COL_INFO
, " (%s)",
588 val_to_str (status
, status_vals
, "Unknown status: %d"));
590 proto_tree_add_expert_format(memcache_tree
, pinfo
, &ei_value_missing
, tvb
, offset
, 0, "%s with status %s (%d) must have Value",
591 val_to_str (opcode
, opcode_vals
, "Opcode %d"),
592 val_to_str_const (status
, status_vals
, "Unknown"), status
);
595 return tvb_captured_length(tvb
);
598 /* Obtain the content length by peeping into the header.
601 get_payload_length (tvbuff_t
*tvb
, packet_info
*pinfo
, const int token_number
, int offset
,
602 uint32_t *bytes
, bool *content_length_found
)
604 const unsigned char *next_token
;
605 const unsigned char *line
, *lineend
;
606 unsigned char *bytes_val
;
607 int tokenlen
, i
= 0, linelen
;
610 /* get the header line. */
611 linelen
= tvb_find_line_end (tvb
, offset
, -1, &next_offset
,
617 line
= tvb_get_ptr (tvb
, offset
, linelen
);
618 lineend
= line
+ linelen
;
620 while (++i
< token_number
) {
621 tokenlen
= get_token_len (line
, lineend
, &next_token
);
625 offset
+= (int) (next_token
- line
);
629 /* line or the next_token has the value we want. */
630 tokenlen
= get_token_len (line
, lineend
, &next_token
);
635 bytes_val
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, tokenlen
, ENC_ASCII
);
637 if (sscanf (bytes_val
, "%u", bytes
) == 1) {
638 *content_length_found
= true;
646 /* reached this far, we got what we want. */
650 /* check if a PDU needs to be desegmented. */
652 desegment_pdus (tvbuff_t
*tvb
, packet_info
*pinfo
, const int offset
,
653 const int data_offset
, uint32_t content_length
)
655 int length_remaining
, reported_length_remaining
;
657 /* data_offset has been set to start of the data block. */
658 if (!tvb_bytes_exist (tvb
, data_offset
, content_length
)) {
660 length_remaining
= tvb_captured_length_remaining (tvb
, data_offset
);
661 reported_length_remaining
= tvb_reported_length_remaining (tvb
, data_offset
);
663 if (length_remaining
< reported_length_remaining
) {
664 /* It's a waste of time asking for more
665 * data, because that data wasn't captured.
670 if (length_remaining
== -1) {
671 length_remaining
= 0;
674 pinfo
->desegment_offset
= offset
; /* start of the packet. */
675 pinfo
->desegment_len
= (content_length
+ 2) - length_remaining
; /* add 2 for /r/n */
683 * Optionally do reassembly of the requests, responses and data.
686 memcache_req_resp_hdrs_do_reassembly (
687 tvbuff_t
*tvb
, const int offset
, packet_info
*pinfo
,
688 const bool desegment_headers
, const bool desegment_body
,
689 const memcache_type_t type
, const bool expect_content_length
)
693 int length_remaining
;
694 int reported_length_remaining
;
695 uint32_t content_length
= 0;
696 bool content_length_found
= false;
700 * If header desegmentation is activated, check the
701 * header in this tvbuff.
702 * request one more byte (we don't know how many bytes
703 * we'll need, so we just ask for one).
705 if (desegment_headers
&& pinfo
->can_desegment
) {
706 next_offset
= offset
;
708 reported_length_remaining
= tvb_reported_length_remaining (tvb
, next_offset
);
710 * Request one more byte if there're no
711 * bytes left in the reported data (if there're
712 * bytes left in the reported data, but not in
713 * the available data, requesting more bytes
714 * won't help, as those bytes weren't captured).
716 if (reported_length_remaining
< 1) {
717 pinfo
->desegment_offset
= offset
;
718 pinfo
->desegment_len
= DESEGMENT_ONE_MORE_SEGMENT
;
722 length_remaining
= tvb_captured_length_remaining (tvb
, next_offset
);
724 /* Request one more byte if we cannot find a
725 * header (i.e. a line end).
727 linelen
= tvb_find_line_end (tvb
, next_offset
, -1, &next_offset
, true);
728 if (linelen
== -1 && length_remaining
>= reported_length_remaining
) {
729 /* Not enough data; ask for one more byte. */
730 pinfo
->desegment_offset
= offset
;
731 pinfo
->desegment_len
= DESEGMENT_ONE_MORE_SEGMENT
;
735 /* Browse through the header to find the content length.
738 * <command name> <key> <flags> <exptime> <bytes> [noreply]\r\n
739 * cas <key> <flags> <exptime> <bytes> <cas unqiue> [noreply]\r\n
742 * VALUE <key> <flags> <bytes> [<cas unique>]\r\n
745 if (expect_content_length
== true) {
748 case MEMCACHE_REQUEST
:
749 /* Get the fifth token in the header.*/
750 ret
= get_payload_length (tvb
, pinfo
, 5 , offset
, &content_length
, &content_length_found
);
756 case MEMCACHE_RESPONSE
:
757 /* Get the fourth token in the header.*/
758 ret
= get_payload_length (tvb
, pinfo
, 4 , offset
, &content_length
, &content_length_found
);
765 /* Unrecognized message type. */
771 /* We have reached the end of a header, so there
772 * should be 'content_length' bytes after this
773 * followed by CRLF. The next_offset points to the
774 * start of the data bytes.
776 if (desegment_body
&& content_length_found
) {
777 return !desegment_pdus (tvb
, pinfo
, offset
, next_offset
, content_length
);
780 /* No further desegmentation needed. */
784 /* Dissect a memcache message. */
786 dissect_memcache_message (tvbuff_t
*tvb
, int offset
, packet_info
*pinfo
, proto_tree
*tree
)
788 const unsigned char *line
;
789 const unsigned char *lineend
;
793 bool expect_content_length
= false;
796 bool is_request_or_reply
;
797 memcache_type_t memcache_type
;
798 ReqRespDissector reqresp_dissector
= NULL
;
799 proto_tree
*memcache_tree
= NULL
;
800 proto_item
*memcache_item
= NULL
;
801 uint8_t opcode
= 0xff; /* set to something that is not in the list. */
803 /* Find a line end in the packet.
804 * Note that "tvb_find_line_end ()" will return a value that
805 * is not longer than what's in the buffer, so the
806 * "tvb_get_ptr ()" call won't throw an exception.
808 first_linelen
= tvb_find_line_end (tvb
, offset
, -1, &next_offset
,
810 if (first_linelen
< 0) {
814 line
= tvb_get_ptr (tvb
, offset
, first_linelen
);
815 lineend
= line
+ first_linelen
;
817 memcache_type
= MEMCACHE_UNKNOWN
; /* packet type not known yet */
819 /* Look at the first token of the first line to
820 * determine if it is a request or a response?
822 is_request_or_reply
=
823 is_memcache_request_or_reply ((const char *)line
,
824 first_linelen
, &opcode
, &memcache_type
,
825 &expect_content_length
, &reqresp_dissector
);
826 if (is_request_or_reply
) {
828 /* Yes, it is a request or a response.
829 * Do header and body desegmentation if we've been told to.
831 if (!memcache_req_resp_hdrs_do_reassembly (tvb
, offset
, pinfo
, memcache_desegment_headers
,
832 memcache_desegment_body
, memcache_type
,
833 expect_content_length
))
835 /* More data needed for desegmentation. */
840 /* Columns and summary display. */
841 col_set_str (pinfo
->cinfo
, COL_PROTOCOL
, PSNAME
);
843 /* If the packet is a memcache request or reply,
844 * put the first line from the buffer into the summary
845 * Otherwise, just call it a continuation.
847 if (is_request_or_reply
) {
848 line
= tvb_get_ptr (tvb
, offset
, first_linelen
);
849 col_add_fstr (pinfo
->cinfo
, COL_INFO
, "%s ",
850 format_text(pinfo
->pool
, line
, first_linelen
));
852 col_set_str (pinfo
->cinfo
, COL_INFO
, "MEMCACHE Continuation");
855 orig_offset
= offset
;
857 memcache_item
= proto_tree_add_item (tree
, proto_memcache
, tvb
, offset
, -1, ENC_NA
);
858 memcache_tree
= proto_item_add_subtree (memcache_item
, ett_memcache
);
860 /* Process the packet data. The first line is expected to be a
861 * header. If it's not a header then we don't dissect.
862 * At this point, we already know if it is a request or a
865 if (tvb_reported_length_remaining (tvb
, offset
) != 0) {
866 /* Dissect a request or a response. */
867 if (is_request_or_reply
&& reqresp_dissector
) {
868 next_offset
= reqresp_dissector (tvb
, pinfo
, memcache_tree
,
869 offset
, line
, lineend
, opcode
);
870 if (next_offset
== -1) {
871 /* Error in dissecting. */
874 offset
= next_offset
;
879 * If a 'bytes' value was supplied, the amount of data to be
880 * processed as MEMCACHE payload is the minimum of the 'bytes'
881 * value and the amount of data remaining in the frame.
884 datalen
= tvb_captured_length_remaining (tvb
, offset
);
887 * We've processed "datalen" bytes worth of data
888 * (which may be no data at all); advance the
889 * offset past whatever data we've processed.
894 return offset
- orig_offset
;
901 content_data_dissector (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
,
902 int content_length
, uint8_t opcode
)
905 bool short_pkt
= false;
908 * Expecting to read 'content_length' number of bytes from
909 * the buffer. It is not necessary that we have all the
910 * content_length bytes available to read.
912 if (tvb_reported_length_remaining (tvb
, offset
) != 0) {
913 /* bytes actually remaining in this tvbuff. */
914 datalen
= tvb_captured_length_remaining (tvb
, offset
);
915 if (content_length
>= 0) {
916 if (datalen
>= (content_length
+ 2)) { /* also consider \r\n*/
917 datalen
= content_length
;
923 /* dissect the data block. */
924 dissect_value (tvb
, pinfo
, tree
, offset
, datalen
, opcode
, true);
927 * We've processed "datalen" bytes worth of data
928 * (which may be no data at all); advance the
929 * offset past whatever data we've processed.
932 offset
+= (datalen
+ 2); /* go past /r/n*/
934 offset
+= datalen
; /* short packet; no /r/n*/
942 /* Find the occurrences of a ':' in a stat response. */
944 find_stat_colon (const unsigned char *line
, const unsigned char *lineend
,
945 const unsigned char **first_colon
, const unsigned char **last_colon
)
947 const unsigned char *linep
, *temp
;
948 unsigned occurrences
= 0;
952 while (linep
< lineend
) {
959 if (occurrences
== 1) {
961 } else if (occurrences
== 2) {
964 /* anything other than 1 or 2;
978 /* incr/decr response dissector */
980 incr_dissector (tvbuff_t
*tvb
, proto_tree
*tree
, int offset
)
984 const unsigned char *line
, *lineend
;
986 const unsigned char *next_token
;
989 /* expecting to read 'bytes' number of bytes from the buffer. */
990 if (tvb_offset_exists (tvb
, offset
)) {
991 /* Find the end of the line. */
992 linelen
= tvb_find_line_end (tvb
, offset
, -1, &next_offset
, false);
994 /* header is out of the packet limits. */
999 * Get a buffer that refers to the line.
1000 * in other words, the unstructured portion
1003 line
= tvb_get_ptr (tvb
, offset
, linelen
);
1004 lineend
= line
+ linelen
;
1007 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1008 if (tokenlen
== 0) {
1012 proto_tree_add_item (tree
, hf_uint64_response
, tvb
, offset
, tokenlen
, ENC_BIG_ENDIAN
);
1015 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1016 if (tokenlen
== 0) {
1019 return -1; /* invalid token */
1026 /* stats response dissector */
1028 stat_dissector (tvbuff_t
*tvb
, proto_tree
*tree
, int offset
)
1030 unsigned occurrences
= 0;
1031 const unsigned char *first_colon
= NULL
, *last_colon
= NULL
;
1032 int tokenlen
, linelen
;
1034 const unsigned char *next_token
;
1035 const unsigned char *line
, *lineend
;
1037 unsigned char response_chars
[21];
1039 while (tvb_offset_exists (tvb
, offset
)) {
1040 /* Find the end of the line. */
1041 linelen
= tvb_find_line_end (tvb
, offset
, -1, &next_offset
,
1048 * Get a buffer that refers to the line.
1050 line
= tvb_get_ptr (tvb
, offset
, linelen
);
1051 lineend
= line
+ linelen
;
1053 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1054 if ((tokenlen
== 4) && strncmp (line
, "STAT", tokenlen
) == 0) {
1055 proto_tree_add_item (tree
, hf_command
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1056 offset
+= (int) (next_token
- line
);
1058 occurrences
= find_stat_colon (line
, lineend
, &first_colon
, &last_colon
);
1059 } else if ((tokenlen
== 3) && strncmp (line
, "END", tokenlen
) == 0) {
1060 /* done. reached an end of response. */
1061 offset
+= (int) (next_token
- line
);
1068 switch (occurrences
) {
1069 case 2: /* stats items: 2 colons */
1070 /* subcommand 'items' */
1071 tokenlen
= (int) (first_colon
- line
);
1072 proto_tree_add_item (tree
, hf_subcommand
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1073 offset
+= tokenlen
+ 1;
1076 tokenlen
= (int) (last_colon
- first_colon
- 1);
1077 if (tokenlen
> 10 || tokenlen
<= 0) {
1080 memcpy (response_chars
, first_colon
+ 1, tokenlen
);
1081 response_chars
[tokenlen
] = '\0';
1083 slabclass
= (uint32_t) strtoul (response_chars
, NULL
, 10);
1084 proto_tree_add_uint (tree
, hf_slabclass
, tvb
, offset
, tokenlen
, slabclass
);
1085 offset
+= tokenlen
+ 1;
1086 line
= last_colon
+ 1;
1089 case 1: /* stats slabs: 1 colon */
1090 tokenlen
= (int) (first_colon
- line
);
1091 if (tokenlen
> 10 || tokenlen
<= 0) {
1094 memcpy (response_chars
, line
, tokenlen
);
1095 response_chars
[tokenlen
] = '\0';
1097 slabclass
= (uint32_t) strtoul (response_chars
, NULL
, 10);
1098 proto_tree_add_uint (tree
, hf_slabclass
, tvb
, offset
, tokenlen
, slabclass
);
1100 offset
+= (int) (tokenlen
+ 1);
1101 line
= first_colon
+ 1;
1104 case 0: /* stats: 0 colons */
1108 /* invalid token. */
1112 /* <hf_name> <hf_name_value>\r\n */
1113 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1114 if (tokenlen
== 0) {
1115 return -1; /* invalid token */
1118 proto_tree_add_item (tree
, hf_name
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1119 offset
+= (int) (next_token
- line
);
1123 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1124 if (tokenlen
== 0) {
1125 return -1; /* invalid token */
1127 proto_tree_add_item (tree
, hf_name_value
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1129 offset
= next_offset
;
1135 /* get/gets response dissector */
1137 get_response_dissector (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
)
1141 const unsigned char *line
, *lineend
;
1142 const unsigned char *next_token
;
1147 uint8_t opcode
= 0xff;
1148 char response_chars
[21]; /* cover uint64 (20 + 1) bytes*/
1150 /* expecting to read 'bytes' number of bytes from the buffer. */
1151 while (tvb_offset_exists (tvb
, offset
)) {
1152 /* Find the end of the line. */
1153 linelen
= tvb_find_line_end (tvb
, offset
, -1, &next_offset
,
1156 /* header is out of the packet limits. */
1161 * Get a buffer that refers to the line.
1162 * in other words, the unstructured portion
1165 line
= tvb_get_ptr (tvb
, offset
, linelen
);
1166 lineend
= line
+ linelen
;
1169 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1170 if (tokenlen
== 0) {
1175 if ((tokenlen
== 5) && strncmp (line
, "VALUE", tokenlen
) == 0) {
1177 } else if ((tokenlen
== 3) && strncmp (line
, "END", tokenlen
) == 0) {
1178 /* done. reached an end of response. */
1179 offset
+= (int) (next_token
- line
);
1186 offset
+= (int) (next_token
- line
);
1190 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1191 if (tokenlen
== 0) {
1194 dissect_key (tvb
, pinfo
, tree
, offset
, tokenlen
, opcode
, true);
1195 offset
+= (int) (next_token
- line
);
1199 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1200 if (tokenlen
== 0 || tokenlen
> 5) {
1203 memcpy (response_chars
, line
, tokenlen
);
1204 response_chars
[tokenlen
] = '\0';
1206 flags
= (uint16_t) strtoul (response_chars
, NULL
, 10);
1207 proto_tree_add_uint (tree
, hf_flags
, tvb
, offset
, tokenlen
, flags
);
1209 offset
+= (int) (next_token
- line
);
1213 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1214 if (tokenlen
== 0 || tokenlen
> 10) {
1217 memcpy (response_chars
, line
, tokenlen
);
1218 response_chars
[tokenlen
] = '\0';
1220 bytes
= (uint32_t) strtoul (response_chars
, NULL
, 10);
1221 proto_tree_add_uint (tree
, hf_value_length
, tvb
, offset
, tokenlen
, bytes
);
1223 offset
+= (int) (next_token
- line
);
1226 /* check if cas id is present */
1227 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1228 if (tokenlen
> 20) {
1232 if (tokenlen
!= 0) { /* reached the end of line; CRLF */
1233 memcpy (response_chars
, line
, tokenlen
);
1234 response_chars
[tokenlen
] = '\0';
1236 cas
= (uint64_t) strtoul (response_chars
, NULL
, 10);
1237 proto_tree_add_uint64 (tree
, hf_cas
, tvb
, offset
, tokenlen
, cas
);
1240 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1241 if (tokenlen
!= 0) {
1242 return -1; /* invalid token */
1246 offset
= next_offset
;
1247 /* <datablock>\r\n */
1248 offset
= content_data_dissector (tvb
, pinfo
, tree
, offset
, bytes
, opcode
);
1257 /* Basic memcache response dissector. */
1259 memcache_response_dissector (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
,
1260 const unsigned char *line
, const unsigned char *lineend
, uint8_t opcode
)
1262 const unsigned char *next_token
;
1269 return get_response_dissector (tvb
, pinfo
, tree
, offset
);
1272 /* response code. */
1273 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1274 if (tokenlen
== 0) {
1277 if ((tokenlen
== 7) && strncmp (line
, "VERSION", tokenlen
) == 0) {
1278 offset
+= (int) (next_token
- line
);
1284 /* version string */
1285 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1286 if (tokenlen
== 0) {
1287 /* expecting version string. */
1291 proto_tree_add_item (tree
, hf_version
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1292 offset
+= (int) (next_token
- line
);
1296 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1297 if (tokenlen
!= 0) {
1305 return stat_dissector (tvb
, tree
, offset
);
1311 /* response code. */
1312 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1313 if (tokenlen
== 0) {
1317 /* all the following mark an end of a response.
1318 * should take care of set, add, cas, append, replace
1319 * prepend, flush_all, verbosity, delete and to an extent
1320 * incr, decr and stat commands.
1322 if ((tokenlen
== 6 && strncmp (line
, "STORED", tokenlen
) == 0) ||
1323 (tokenlen
== 10 && strncmp (line
, "NOT_STORED", tokenlen
) == 0) ||
1324 (tokenlen
== 6 && strncmp (line
, "EXISTS", tokenlen
) == 0) ||
1325 (tokenlen
== 9 && strncmp (line
, "NOT_FOUND", tokenlen
) == 0) ||
1326 (tokenlen
== 7 && strncmp (line
, "DELETED", tokenlen
) == 0) ||
1327 (tokenlen
== 2 && strncmp (line
, "OK", tokenlen
) == 0) ||
1328 (tokenlen
== 3 && strncmp (line
, "END", tokenlen
) == 0))
1330 proto_tree_add_item (tree
, hf_response
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1331 offset
+= (int) (next_token
- line
);
1335 /* if we have reached this point:
1336 * it is either an incr/decr response of the format
1339 * "stats sizes" response of the format:
1340 * <size> <count> \r\n
1342 if (opcode
== OP_INCREMENT
) {
1343 return incr_dissector (tvb
, tree
, offset
);
1349 /* Basic memcache request dissector. */
1351 memcache_request_dissector (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, int offset
,
1352 const unsigned char *line
, const unsigned char *lineend
, uint8_t opcode
)
1354 const unsigned char *next_token
;
1358 uint32_t expiration
;
1361 char response_chars
[21]; /* cover uint64 (20 + 1) bytes*/
1364 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1365 if (tokenlen
== 0) {
1368 proto_tree_add_item (tree
, hf_command
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1369 offset
+= (int) (next_token
- line
);
1382 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1383 if (tokenlen
== 0) {
1387 dissect_key (tvb
, pinfo
, tree
, offset
, tokenlen
, opcode
, true);
1388 offset
+= (int) (next_token
- line
);
1392 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1393 if (tokenlen
== 0 || tokenlen
> 5) {
1396 memcpy (response_chars
, line
, tokenlen
);
1397 response_chars
[tokenlen
] = '\0';
1399 flags
= (uint16_t) strtoul (response_chars
, NULL
, 10);
1400 proto_tree_add_uint (tree
, hf_flags
, tvb
, offset
, tokenlen
, flags
);
1402 offset
+= (int) (next_token
- line
);
1406 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1407 if (tokenlen
== 0 || tokenlen
> 10) {
1410 memcpy (response_chars
, line
, tokenlen
);
1411 response_chars
[tokenlen
] = '\0';
1413 expiration
= (uint32_t) strtoul (response_chars
, NULL
, 10);
1414 proto_tree_add_uint (tree
, hf_expiration
, tvb
, offset
, tokenlen
, expiration
);
1416 offset
+= (int) (next_token
- line
);
1420 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1421 if (tokenlen
== 0 || tokenlen
> 10) {
1424 memcpy (response_chars
, line
, tokenlen
);
1425 response_chars
[tokenlen
] = '\0';
1427 bytes
= (uint32_t) strtoul (response_chars
, NULL
, 10);
1428 proto_tree_add_uint (tree
, hf_value_length
, tvb
, offset
, tokenlen
, bytes
);
1430 offset
+= (int) (next_token
- line
);
1434 if (opcode
== OP_CAS
) {
1435 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1436 if (tokenlen
== 0 || tokenlen
> 20) {
1439 memcpy (response_chars
, line
, tokenlen
);
1440 response_chars
[tokenlen
] = '\0';
1442 cas
= (uint64_t) strtoul (response_chars
, NULL
, 10);
1443 proto_tree_add_uint64 (tree
, hf_cas
, tvb
, offset
, tokenlen
, cas
);
1445 offset
+= (int) (next_token
- line
);
1449 /* check if the following bit is "noreply" or
1450 * the actual data block.
1452 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1453 if (tokenlen
!= 0) {
1454 if (tokenlen
== 7 && strncmp (line
, "noreply", 7) == 0) {
1455 proto_tree_add_item (tree
, hf_noreply
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1457 offset
+= (int) (next_token
- line
);
1460 offset
+= 2 ; /* go past /r/n*/
1461 /* <datablock>\r\n */
1462 offset
= content_data_dissector (tvb
, pinfo
, tree
, offset
, bytes
, opcode
);
1471 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1472 if (tokenlen
== 0) {
1475 dissect_key (tvb
, pinfo
, tree
, offset
, tokenlen
, opcode
, true);
1476 offset
+= (int) (next_token
- line
);
1480 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1481 if (tokenlen
== 0) {
1484 proto_tree_add_item (tree
, hf_value
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1485 offset
+= (int) (next_token
- line
);
1488 /* check for "noreply" */
1489 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1490 if (tokenlen
== 0) {
1491 return offset
; /* reached CRLF */
1493 if (tokenlen
== 7 && strncmp (line
, "noreply", 7) == 0) {
1494 proto_tree_add_item (tree
, hf_noreply
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1495 offset
+= (int) (next_token
- line
);
1498 return -1; /* should have been noreply or CRLF. */
1502 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1503 if (tokenlen
== 0) {
1504 return offset
; /* CRLF */
1506 /*something's wrong; invalid command maybe. */
1513 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1514 if (tokenlen
== 0) {
1518 dissect_key (tvb
, pinfo
, tree
, offset
, tokenlen
, opcode
, true);
1519 offset
+= (int) (next_token
- line
);
1522 /* check if it's expiration or noreply */
1523 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1524 if (tokenlen
== 0) {
1525 return offset
; /* neither expiration nor noreply; CRLF */
1527 if (tokenlen
<= 10) {
1528 if (tokenlen
== 7 && strncmp (line
, "noreply", 7) == 0) {
1530 proto_tree_add_item (tree
, hf_noreply
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1533 memcpy (response_chars
, line
, tokenlen
);
1534 response_chars
[tokenlen
] = '\0';
1536 expiration
= (uint32_t) strtoul (response_chars
, NULL
, 10);
1537 proto_tree_add_uint (tree
, hf_expiration
, tvb
, offset
, tokenlen
, expiration
);
1539 offset
+= (int) (next_token
- line
);
1546 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1547 if (tokenlen
== 0) {
1550 /*something's wrong; invalid command maybe. */
1557 /* could be followed by any number of keys, add
1558 * them one by one. tokenlen cannot be 0 to begin
1561 while (tokenlen
!= 0) {
1562 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1563 if (tokenlen
== 0) {
1564 return offset
; /* CRLF */
1566 dissect_key (tvb
, pinfo
, tree
, offset
, tokenlen
, opcode
, true);
1567 offset
+= (int) (next_token
- line
);
1573 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1574 if (tokenlen
== 0) { /* just the 'stats' command;*/
1576 } else { /* there is a sub command; record it*/
1577 proto_tree_add_item (tree
, hf_subcommand
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1578 offset
+= (int) (next_token
- line
);
1583 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1584 if (tokenlen
== 0) {
1587 /* something's wrong; invalid command maybe. */
1593 /* check if it's expiration or noreply */
1594 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1595 if (tokenlen
== 0) {
1596 return offset
; /* neither expiration nor noreply; CRLF */
1598 if (tokenlen
<= 10) {
1599 if (tokenlen
== 7 && strncmp (line
, "noreply", 7) == 0) {
1601 proto_tree_add_item (tree
, hf_noreply
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1604 memcpy (response_chars
, line
, tokenlen
);
1605 response_chars
[tokenlen
] = '\0';
1607 expiration
= (uint32_t) strtoul (response_chars
, NULL
, 10);
1608 proto_tree_add_uint (tree
, hf_expiration
, tvb
, offset
, tokenlen
, expiration
);
1610 offset
+= (int) (next_token
- line
);
1616 /* maybe noreply now? */
1617 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1618 if (tokenlen
== 0) {
1621 if (tokenlen
== 7 && strncmp (line
, "noreply", 7) == 0) {
1623 proto_tree_add_item (tree
, hf_noreply
, tvb
, offset
, tokenlen
, ENC_ASCII
);
1624 offset
+= (int) (next_token
- line
);
1626 return -1; /* expecting CRLF and if not noreply*/
1631 /* not implemented for now.*/
1637 tokenlen
= get_token_len (line
, lineend
, &next_token
);
1638 if (tokenlen
== 0) {
1641 /*something's wrong; invalid command maybe. */
1646 /* invalid command maybe; break out. */
1654 * any message that is not starting with the following keywords
1658 is_memcache_request_or_reply (const char *data
, int linelen
, uint8_t *opcode
,
1659 memcache_type_t
*type
, bool *expect_content_length
,
1660 ReqRespDissector
*reqresp_dissector
)
1662 const unsigned char *ptr
= (const unsigned char *)data
;
1663 bool is_request_or_response
= false;
1666 /* look for a space */
1667 while (indx
< linelen
) {
1675 /* is it a response? */
1678 if (strncmp (data
, "OK", indx
) == 0) {
1679 *type
= MEMCACHE_RESPONSE
;
1680 is_request_or_response
= true;
1685 if (strncmp (data
, "END", indx
) == 0) {
1686 *type
= MEMCACHE_RESPONSE
;
1687 is_request_or_response
= true;
1692 if (strncmp (data
, "STAT", indx
) == 0) {
1694 *type
= MEMCACHE_RESPONSE
;
1695 is_request_or_response
= true;
1700 if (strncmp (data
, "VALUE", indx
) == 0) {
1702 *type
= MEMCACHE_RESPONSE
;
1703 *expect_content_length
= true;
1704 is_request_or_response
= true;
1709 if (strncmp (data
, "EXISTS", indx
) == 0 ||
1710 strncmp (data
, "STORED", indx
) == 0) {
1711 *type
= MEMCACHE_RESPONSE
;
1712 is_request_or_response
= true;
1717 if (strncmp (data
, "VERSION", indx
) == 0) {
1718 *opcode
= OP_VERSION
;
1719 *type
= MEMCACHE_RESPONSE
;
1720 is_request_or_response
= true;
1721 } else if (strncmp (data
, "DELETED", indx
) == 0) {
1722 *opcode
= OP_DELETE
;
1723 *type
= MEMCACHE_RESPONSE
;
1724 is_request_or_response
= true;
1729 if (strncmp (data
, "NOT_FOUND", indx
) == 0) {
1730 *type
= MEMCACHE_RESPONSE
;
1731 is_request_or_response
= true;
1736 if (strncmp (data
, "NOT_STORED", indx
) == 0) {
1737 *type
= MEMCACHE_RESPONSE
;
1738 is_request_or_response
= true;
1743 break; /* is it a request? */
1746 if (is_request_or_response
&& reqresp_dissector
) {
1747 *reqresp_dissector
= memcache_response_dissector
;
1748 return is_request_or_response
;
1751 /* is it a request? */
1754 if (strncmp (data
, "get", indx
) == 0) {
1756 *type
= MEMCACHE_REQUEST
;
1757 is_request_or_response
= true;
1758 } else if (strncmp (data
, "set", indx
) == 0) {
1760 *type
= MEMCACHE_REQUEST
;
1761 *expect_content_length
= true;
1762 is_request_or_response
= true;
1763 } else if (strncmp (data
, "add", indx
) == 0) {
1765 *type
= MEMCACHE_REQUEST
;
1766 *expect_content_length
= true;
1767 is_request_or_response
= true;
1768 } else if (strncmp (data
, "cas", indx
) == 0) {
1770 *type
= MEMCACHE_REQUEST
;
1771 *expect_content_length
= true;
1772 is_request_or_response
= true;
1777 if (strncmp (data
, "gets", indx
) == 0) {
1779 *type
= MEMCACHE_REQUEST
;
1780 is_request_or_response
= true;
1781 } else if (strncmp (data
, "incr", indx
) == 0) {
1782 *opcode
= OP_INCREMENT
;
1783 *type
= MEMCACHE_REQUEST
;
1784 is_request_or_response
= true;
1785 } else if (strncmp (data
, "decr", indx
) == 0) {
1786 *opcode
= OP_DECREMENT
;
1787 *type
= MEMCACHE_REQUEST
;
1788 is_request_or_response
= true;
1789 } else if (strncmp (data
, "quit", indx
) == 0) {
1791 *type
= MEMCACHE_REQUEST
;
1792 is_request_or_response
= true;
1797 if (strncmp (data
, "stats", indx
) == 0) {
1799 *type
= MEMCACHE_REQUEST
;
1800 is_request_or_response
= true;
1805 if (strncmp (data
, "append", indx
) == 0) {
1806 *opcode
= OP_APPEND
;
1807 *type
= MEMCACHE_REQUEST
;
1808 *expect_content_length
= true;
1809 is_request_or_response
= true;
1810 } else if (strncmp (data
, "delete", indx
) == 0) {
1811 *opcode
= OP_DELETE
;
1812 *type
= MEMCACHE_REQUEST
;
1813 is_request_or_response
= true;
1818 if (strncmp (data
, "replace", indx
) == 0) {
1819 *opcode
= OP_REPLACE
;
1820 *type
= MEMCACHE_REQUEST
;
1821 *expect_content_length
= true;
1822 is_request_or_response
= true;
1823 } else if (strncmp (data
, "prepend", indx
) == 0) {
1824 *opcode
= OP_PREPEND
;
1825 *type
= MEMCACHE_REQUEST
;
1826 *expect_content_length
= true;
1827 is_request_or_response
= true;
1828 } else if (strncmp (data
, "version", indx
) == 0) {
1829 *opcode
= OP_VERSION
;
1830 *type
= MEMCACHE_REQUEST
;
1831 is_request_or_response
= true;
1836 if (strncmp (data
, "flush_all", indx
) == 0) {
1838 *type
= MEMCACHE_REQUEST
;
1839 is_request_or_response
= true;
1844 break; /* check if it is an 'incr' or 'stats sizes' response. */
1847 if (is_request_or_response
&& reqresp_dissector
) {
1848 *reqresp_dissector
= memcache_request_dissector
;
1849 return is_request_or_response
;
1853 * Recognize 'incr', 'decr' and 'stats sizes' responses.
1854 * I don't have a solution for this yet.
1856 return is_request_or_response
;
1859 /* dissect memcache textual protocol PDUs. */
1861 dissect_memcache_text (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
1866 while (tvb_reported_length_remaining (tvb
, offset
) != 0) {
1868 /* dissect the memcache packet. */
1869 len
= dissect_memcache_message (tvb
, offset
, pinfo
, tree
);
1875 * OK, we've set the Protocol and Info columns for the
1876 * first MEMCACHE message; set a fence so that subsequent
1877 * MEMCACHE messages don't overwrite the Info column.
1879 col_set_fence (pinfo
->cinfo
, COL_INFO
);
1883 /* Dissect tcp packets based on the type of protocol (text/binary) */
1885 dissect_memcache_tcp (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
1890 magic
= tvb_get_uint8 (tvb
, offset
);
1892 if (try_val_to_str (magic
, magic_vals
) != NULL
) {
1893 tcp_dissect_pdus (tvb
, pinfo
, tree
, memcache_desegment_body
, 12,
1894 get_memcache_pdu_len
, dissect_memcache
, data
);
1896 dissect_memcache_text (tvb
, pinfo
, tree
);
1899 return tvb_captured_length(tvb
);
1902 /* Dissect udp packets based on the type of protocol (text/binary) */
1904 dissect_memcache_udp (tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
1909 magic
= tvb_get_uint8 (tvb
, offset
);
1911 if (try_val_to_str (magic
, magic_vals
) != NULL
) {
1912 dissect_memcache (tvb
, pinfo
, tree
, data
);
1914 dissect_memcache_message (tvb
, 0, pinfo
, tree
);
1917 return tvb_captured_length(tvb
);
1920 /* Registration functions; register memcache protocol,
1921 * its configuration options and also register the tcp and udp
1925 proto_register_memcache (void)
1927 static hf_register_info hf
[] = {
1929 { "Magic", "memcache.magic",
1930 FT_UINT8
, BASE_DEC
, VALS (magic_vals
), 0x0,
1931 "Magic number", HFILL
} },
1934 { "Opcode", "memcache.opcode",
1935 FT_UINT8
, BASE_DEC
, VALS (opcode_vals
), 0x0,
1936 "Command code", HFILL
} },
1938 { &hf_extras_length
,
1939 { "Extras length", "memcache.extras.length",
1940 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
1941 "Length in bytes of the command extras", HFILL
} },
1944 { "Key Length", "memcache.key.length",
1945 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
1946 "Length in bytes of the text key that follows the command extras", HFILL
} },
1949 { "Value length", "memcache.value.length",
1950 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
1951 "Length in bytes of the value that follows the key", HFILL
} },
1954 { "Data type", "memcache.data_type",
1955 FT_UINT8
, BASE_DEC
, VALS (data_type_vals
), 0x0,
1959 { "Reserved", "memcache.reserved",
1960 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
1961 "Reserved for future use", HFILL
} },
1964 { "Status", "memcache.status",
1965 FT_UINT16
, BASE_DEC
, VALS (status_vals
), 0x0,
1966 "Status of the response", HFILL
} },
1968 { &hf_total_body_length
,
1969 { "Total body length", "memcache.total_body_length",
1970 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
1971 "Length in bytes of extra + key + value", HFILL
} },
1974 { "Opaque", "memcache.opaque",
1975 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
1979 { "CAS", "memcache.cas",
1980 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
1981 "Data version check", HFILL
} },
1984 { "Extras", "memcache.extras",
1985 FT_NONE
, BASE_NONE
, NULL
, 0x0,
1989 { "Flags", "memcache.extras.flags",
1990 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
1993 { &hf_extras_expiration
,
1994 { "Expiration", "memcache.extras.expiration",
1995 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
1999 { "Amount to add", "memcache.extras.delta",
2000 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2003 { &hf_extras_initial
,
2004 { "Initial value", "memcache.extras.initial",
2005 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2008 { &hf_extras_unknown
,
2009 { "Unknown", "memcache.extras.unknown",
2010 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2011 "Unknown Extras", HFILL
} },
2014 { "Key", "memcache.key",
2015 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2019 { "Value", "memcache.value",
2020 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2023 { &hf_uint64_response
,
2024 { "Response", "memcache.extras.response",
2025 FT_UINT64
, BASE_DEC
, NULL
, 0x0,
2029 { "Command", "memcache.command",
2030 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2034 { "Sub command", "memcache.subcommand",
2035 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2036 "Sub command if any", HFILL
} },
2039 { "Flags", "memcache.flags",
2040 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
2044 { "Expiration", "memcache.expiration",
2045 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2049 { "Noreply", "memcache.noreply",
2050 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2051 "Client does not expect a reply", HFILL
} },
2054 { "Response", "memcache.response",
2055 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2056 "Response command", HFILL
} },
2059 { "Version", "memcache.version",
2060 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2061 "Version of running memcache", HFILL
} },
2064 { "Slab class", "memcache.slabclass",
2065 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2066 "Slab class of a stat", HFILL
} },
2069 { "Stat name", "memcache.name",
2070 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2071 "Name of a stat", HFILL
} },
2074 { "Stat value", "memcache.name_value",
2075 FT_STRING
, BASE_NONE
, NULL
, 0x0,
2076 "Value of a stat", HFILL
} },
2079 static int *ett
[] = {
2084 static ei_register_info ei
[] = {
2085 { &ei_extras_unknown
, { "memcache.extras.notexpected", PI_UNDECODED
, PI_WARN
, "shall not have Extras", EXPFILL
}},
2086 { &ei_extras_missing
, { "memcache.extras.missing", PI_UNDECODED
, PI_WARN
, "must have Extras", EXPFILL
}},
2087 { &ei_key_unknown
, { "memcache.key.notexpected", PI_UNDECODED
, PI_WARN
, "shall not have Key", EXPFILL
}},
2088 { &ei_key_missing
, { "memcache.key.missing", PI_UNDECODED
, PI_WARN
, "must have Key", EXPFILL
}},
2089 { &ei_value_length
, { "memcache.value.invalid", PI_UNDECODED
, PI_WARN
, "Illegal Value length, should be 8", EXPFILL
}},
2090 { &ei_value_unknown
, { "memcache.value.notexpected", PI_UNDECODED
, PI_WARN
, "shall not have Value", EXPFILL
}},
2091 { &ei_value_missing
, { "memcache.value.missing", PI_UNDECODED
, PI_WARN
, "must have Value", EXPFILL
}},
2092 { &ei_magic_unknown
, { "memcache.magic.unknown", PI_UNDECODED
, PI_WARN
, "Unknown magic byte", EXPFILL
}},
2093 { &ei_opcode_unknown
, { "memcache.opcode.unknown", PI_UNDECODED
, PI_WARN
, "Unknown opcode", EXPFILL
}},
2094 { &ei_status_response
, { "memcache.status.response", PI_RESPONSE_CODE
, PI_NOTE
, "Error response", EXPFILL
}},
2095 { &ei_reserved_value
, { "memcache.reserved.expert", PI_UNDECODED
, PI_WARN
, "Reserved value", EXPFILL
}},
2098 module_t
*memcache_module
;
2099 expert_module_t
*expert_memcache
;
2101 proto_memcache
= proto_register_protocol (PNAME
, PSNAME
, PFNAME
);
2102 memcache_tcp_handle
= register_dissector ("memcache.tcp", dissect_memcache_tcp
, proto_memcache
);
2103 memcache_udp_handle
= register_dissector ("memcache.udp", dissect_memcache_udp
, proto_memcache
);
2105 proto_register_field_array (proto_memcache
, hf
, array_length (hf
));
2106 proto_register_subtree_array (ett
, array_length (ett
));
2107 expert_memcache
= expert_register_protocol(proto_memcache
);
2108 expert_register_field_array(expert_memcache
, ei
, array_length(ei
));
2110 /* Register our configuration options */
2111 memcache_module
= prefs_register_protocol (proto_memcache
, NULL
);
2113 prefs_register_bool_preference (memcache_module
, "desegment_headers",
2114 "Reassemble MEMCACHE headers spanning multiple TCP segments",
2115 "Whether the MEMCACHE dissector should reassemble headers "
2116 "of a request spanning multiple TCP segments. "
2117 "To use this option, you must also enable "
2118 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
2119 &memcache_desegment_headers
);
2121 prefs_register_bool_preference (memcache_module
, "desegment_pdus",
2122 "Reassemble PDUs spanning multiple TCP segments",
2123 "Whether the memcache dissector should reassemble PDUs"
2124 " spanning multiple TCP segments."
2125 " To use this option, you must also enable \"Allow subdissectors"
2126 " to reassemble TCP streams\" in the TCP protocol settings.",
2127 &memcache_desegment_body
);
2130 /* Register the tcp and udp memcache dissectors. */
2132 proto_reg_handoff_memcache (void)
2134 dissector_add_uint_range_with_preference("tcp.port", MEMCACHE_DEFAULT_RANGE
, memcache_tcp_handle
);
2135 dissector_add_uint_range_with_preference("udp.port", MEMCACHE_DEFAULT_RANGE
, memcache_udp_handle
);
2144 * indent-tabs-mode: nil
2147 * ex: set shiftwidth=2 tabstop=8 expandtab:
2148 * :indentSize=2:tabSize=8:noTabs=true: