epan/dissectors/pidl/samr/samr.cnf cnf_dissect_lsa_BinaryString => lsarpc_dissect_str...
[wireshark-sm.git] / epan / dissectors / packet-memcache.c
blobeba5aaafdac43dbba3ba75466982c1764e52e581
1 /* packet-memcache.c
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
19 #include "config.h"
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
41 /* Magic Byte */
42 #define MAGIC_REQUEST 0x80
43 #define MAGIC_RESPONSE 0x81
45 /* Response Status */
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
55 /* Command Opcodes */
56 #define OP_GET 0x00
57 #define OP_SET 0x01
58 #define OP_ADD 0x02
59 #define OP_REPLACE 0x03
60 #define OP_DELETE 0x04
61 #define OP_INCREMENT 0x05
62 #define OP_DECREMENT 0x06
63 #define OP_QUIT 0x07
64 #define OP_FLUSH 0x08
65 #define OP_GET_Q 0x09
66 #define OP_NO_OP 0x0A
67 #define OP_VERSION 0x0B
68 #define OP_GET_K 0x0C
69 #define OP_GET_K_Q 0x0D
70 #define OP_APPEND 0x0E
71 #define OP_PREPEND 0x0F
72 #define OP_STAT 0x10
73 #define OP_SET_Q 0x11
74 #define OP_ADD_Q 0x12
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 */
86 #define OP_GETS 0xF0
87 #define OP_CAS 0xF1
88 #define OP_VERBOSE 0xF2
90 /* Data Types */
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;
98 static int hf_magic;
99 static int hf_opcode;
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;
108 static int hf_cas;
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;
115 static int hf_key;
116 static int hf_value;
117 static int hf_uint64_response;
119 static int hf_command;
120 static int hf_subcommand;
121 static int hf_flags;
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;
129 static int hf_name;
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" },
150 { 0, NULL }
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" },
162 { 0, NULL }
165 static const value_string opcode_vals[] = {
166 { OP_GET, "Get" },
167 { OP_SET, "Set" },
168 { OP_ADD, "Add" },
169 { OP_REPLACE, "Replace" },
170 { OP_DELETE, "Delete" },
171 { OP_INCREMENT, "Increment" },
172 { OP_DECREMENT, "Decrement" },
173 { OP_QUIT, "Quit" },
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 */
194 { 0, NULL }
197 static const value_string data_type_vals[] = {
198 { DT_RAW_BYTES, "Raw bytes" },
199 { 0, NULL }
202 /* memcache message types. */
203 typedef enum _memcache_type {
204 MEMCACHE_REQUEST,
205 MEMCACHE_RESPONSE,
206 MEMCACHE_UNKNOWN
207 } memcache_type_t;
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.
223 static int
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);
228 static unsigned
229 get_memcache_pdu_len (packet_info *pinfo _U_, tvbuff_t *tvb,
230 int offset, void *data _U_)
232 uint32_t body_len;
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;
241 static void
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 */
251 if (extras_len) {
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);
256 switch (opcode) {
258 case OP_GET:
259 case OP_GET_Q:
260 case OP_GET_K:
261 case OP_GET_K_Q:
262 if (extras_len) {
263 if (request) {
264 /* Request shall not have extras */
265 illegal = true;
266 } else {
267 proto_tree_add_item (extras_tree, hf_extras_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
268 offset += 4;
270 } else if (!request) {
271 /* Response must have extras */
272 missing = true;
274 break;
276 case OP_SET:
277 case OP_SET_Q:
278 case OP_ADD:
279 case OP_ADD_Q:
280 case OP_REPLACE:
281 case OP_REPLACE_Q:
282 if (extras_len) {
283 if (request) {
284 proto_tree_add_item (extras_tree, hf_extras_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
285 offset += 4;
287 proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
288 offset += 4;
289 } else {
290 /* Response shall not have extras */
291 illegal = true;
293 } else if (request) {
294 /* Request must have extras */
295 missing = true;
297 break;
299 case OP_INCREMENT:
300 case OP_INCREMENT_Q:
301 case OP_DECREMENT:
302 case OP_DECREMENT_Q:
303 if (extras_len) {
304 if (request) {
305 proto_tree_add_item (extras_tree, hf_extras_delta, tvb, offset, 8, ENC_BIG_ENDIAN);
306 offset += 8;
308 proto_tree_add_item (extras_tree, hf_extras_initial, tvb, offset, 8, ENC_BIG_ENDIAN);
309 offset += 8;
311 proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
312 offset += 4;
313 } else {
314 /* Response must not have extras (response is in Value) */
315 illegal = true;
317 } else if (request) {
318 /* Request must have extras */
319 missing = true;
321 break;
323 case OP_FLUSH:
324 case OP_FLUSH_Q:
325 if (extras_len) {
326 proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
327 offset += 4;
329 break;
331 case OP_DELETE:
332 case OP_DELETE_Q:
333 case OP_QUIT:
334 case OP_QUIT_Q:
335 case OP_VERSION:
336 case OP_APPEND:
337 case OP_APPEND_Q:
338 case OP_PREPEND:
339 case OP_PREPEND_Q:
340 case OP_STAT:
341 /* Must not have extras */
342 if (extras_len) {
343 illegal = true;
345 break;
347 default:
348 if (extras_len) {
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;
353 break;
356 if (illegal) {
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);
373 static void
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 */
381 if (key_len) {
382 ti = proto_tree_add_item (tree, hf_key, tvb, offset, key_len, ENC_ASCII);
383 offset += key_len;
385 if ((opcode == OP_QUIT) || (opcode == OP_QUIT_Q) || (opcode == OP_NO_OP) || (opcode == OP_VERSION)) {
386 /* Request and Response must not have key */
387 illegal = true;
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 */
395 if (!request) {
396 illegal = true;
399 } else {
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 */
406 if (request) {
407 missing = true;
412 if (illegal) {
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"));
422 static void
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 */
430 if (value_len > 0) {
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");
436 } else {
437 ti = proto_tree_add_item (tree, hf_value, tvb, offset, value_len, ENC_ASCII);
439 offset += value_len;
442 /* Sanity check */
443 if (value_len) {
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 */
449 if (request) {
450 illegal = true;
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 */
457 illegal = true;
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 */
464 if (!request) {
465 illegal = true;
468 } else {
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 */
474 if (request) {
475 missing = true;
480 if (illegal) {
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");
491 static int
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;
496 int offset = 0;
497 uint8_t magic, opcode, extras_len;
498 uint16_t key_len, status = 0;
499 uint32_t body_len, value_len;
500 bool request;
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);
510 offset += 1;
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);
518 offset += 1;
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);
533 offset += 2;
535 extras_len = tvb_get_uint8 (tvb, offset);
536 proto_tree_add_item (memcache_tree, hf_extras_length, tvb, offset, 1, ENC_BIG_ENDIAN);
537 offset += 1;
539 proto_tree_add_item (memcache_tree, hf_data_type, tvb, offset, 1, ENC_BIG_ENDIAN);
540 offset += 1;
542 status = tvb_get_ntohs (tvb, offset);
543 if (magic & 0x01) { /* We suppose this is a response, even when unknown magic byte */
544 request = false;
545 ti = proto_tree_add_item (memcache_tree, hf_status, tvb, offset, 2, ENC_BIG_ENDIAN);
546 if (status != 0) {
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"));
551 } else {
552 request = true;
553 ti = proto_tree_add_item (memcache_tree, hf_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
554 if (status != 0) {
555 expert_add_info_format(pinfo, ti, &ei_reserved_value, "Reserved value: %d", status);
558 offset += 2;
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);
566 offset += 4;
568 proto_tree_add_item (memcache_tree, hf_opaque, tvb, offset, 4, ENC_BIG_ENDIAN);
569 offset += 4;
571 proto_tree_add_item (memcache_tree, hf_cas, tvb, offset, 8, ENC_BIG_ENDIAN);
572 offset += 8;
574 if (status == 0) {
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);
579 offset += key_len;
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"));
589 } else {
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.
600 static bool
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;
608 int next_offset;
610 /* get the header line. */
611 linelen = tvb_find_line_end (tvb, offset, -1, &next_offset,
612 false);
613 if (linelen < 0) {
614 return false;
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);
622 if (tokenlen == 0) {
623 return false;
625 offset += (int) (next_token - line);
626 line = next_token;
629 /* line or the next_token has the value we want. */
630 tokenlen = get_token_len (line, lineend, &next_token);
631 if (tokenlen == 0) {
632 return false;
635 bytes_val = tvb_get_string_enc(pinfo->pool, tvb, offset, tokenlen, ENC_ASCII);
636 if (bytes_val) {
637 if (sscanf (bytes_val, "%u", bytes) == 1) {
638 *content_length_found = true;
639 } else {
640 return false;
642 } else {
643 return false;
646 /* reached this far, we got what we want. */
647 return true;
650 /* check if a PDU needs to be desegmented. */
651 static bool
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.
667 return false;
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 */
677 return true;
679 return false;
683 * Optionally do reassembly of the requests, responses and data.
685 static bool
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)
691 int linelen;
692 int next_offset;
693 int length_remaining;
694 int reported_length_remaining;
695 uint32_t content_length = 0;
696 bool content_length_found = false;
697 bool ret = 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;
719 return false;
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;
732 return false;
735 /* Browse through the header to find the content length.
737 * request:
738 * <command name> <key> <flags> <exptime> <bytes> [noreply]\r\n
739 * cas <key> <flags> <exptime> <bytes> <cas unqiue> [noreply]\r\n
741 * response:
742 * VALUE <key> <flags> <bytes> [<cas unique>]\r\n
743 * <data block>\r\n
745 if (expect_content_length == true) {
746 switch (type) {
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);
751 if (!ret) {
752 return false;
754 break;
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);
759 if (!ret) {
760 return false;
762 break;
764 default:
765 /* Unrecognized message type. */
766 return false;
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. */
781 return true;
784 /* Dissect a memcache message. */
785 static int
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;
790 int orig_offset;
791 int first_linelen;
792 int datalen;
793 bool expect_content_length = false;
794 int next_offset;
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,
809 false);
810 if (first_linelen < 0) {
811 return -1;
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. */
836 return -1;
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));
851 } else {
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
863 * response.
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. */
872 return -1;
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);
885 if (datalen > 0) {
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.
891 offset += datalen;
894 return offset - orig_offset;
897 /* Payload dissector
898 * <data block>\r\n
900 static int
901 content_data_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
902 int content_length, uint8_t opcode)
904 int datalen;
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;
918 } else {
919 short_pkt = true;
923 /* dissect the data block. */
924 dissect_value (tvb, pinfo, tree, offset, datalen, opcode, true);
925 if (datalen > 0) {
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.
931 if (!short_pkt) {
932 offset += (datalen + 2); /* go past /r/n*/
933 } else {
934 offset += datalen; /* short packet; no /r/n*/
939 return offset;
942 /* Find the occurrences of a ':' in a stat response. */
943 static unsigned
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;
949 unsigned char c;
951 linep = line;
952 while (linep < lineend) {
953 temp = linep;
954 c = *linep++;
956 switch (c) {
957 case ':':
958 occurrences++;
959 if (occurrences == 1) {
960 *first_colon = temp;
961 } else if (occurrences == 2) {
962 *last_colon = temp;
963 } else {
964 /* anything other than 1 or 2;
965 * return immediately
967 return occurrences;
969 break;
970 default:
971 break;
975 return occurrences;
978 /* incr/decr response dissector */
979 static int
980 incr_dissector (tvbuff_t *tvb, proto_tree *tree, int offset)
982 int next_offset;
983 int linelen;
984 const unsigned char *line, *lineend;
986 const unsigned char *next_token;
987 int tokenlen;
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);
993 if (linelen < 0) {
994 /* header is out of the packet limits. */
995 return -1;
999 * Get a buffer that refers to the line.
1000 * in other words, the unstructured portion
1001 * of memcache.
1003 line = tvb_get_ptr (tvb, offset, linelen);
1004 lineend = line + linelen;
1006 /* 64 bit value */
1007 tokenlen = get_token_len (line, lineend, &next_token);
1008 if (tokenlen == 0) {
1009 return -1;
1012 proto_tree_add_item (tree, hf_uint64_response, tvb, offset, tokenlen, ENC_BIG_ENDIAN);
1014 /* CRLF */
1015 tokenlen = get_token_len (line, lineend, &next_token);
1016 if (tokenlen == 0) {
1017 return next_offset;
1018 } else {
1019 return -1; /* invalid token */
1023 return offset;
1026 /* stats response dissector */
1027 static int
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;
1033 int next_offset;
1034 const unsigned char *next_token;
1035 const unsigned char *line, *lineend;
1036 uint32_t slabclass;
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,
1042 false);
1043 if (linelen < 0) {
1044 return -1;
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);
1057 line = next_token;
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);
1062 return offset;
1063 } else {
1064 /* invalid token */
1065 return -1;
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;
1075 /* slabclass */
1076 tokenlen = (int) (last_colon - first_colon - 1);
1077 if (tokenlen > 10 || tokenlen <= 0) {
1078 return -1;
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;
1087 break;
1089 case 1: /* stats slabs: 1 colon */
1090 tokenlen = (int) (first_colon - line);
1091 if (tokenlen > 10 || tokenlen <= 0) {
1092 return -1;
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;
1102 break;
1104 case 0: /* stats: 0 colons */
1105 break;
1107 default:
1108 /* invalid token. */
1109 return -1;
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);
1120 line = next_token;
1122 /* value */
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;
1132 return offset;
1135 /* get/gets response dissector */
1136 static int
1137 get_response_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset)
1139 int next_offset;
1140 int linelen;
1141 const unsigned char *line, *lineend;
1142 const unsigned char *next_token;
1143 int tokenlen;
1144 uint16_t flags;
1145 uint32_t bytes;
1146 uint64_t cas;
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,
1154 false);
1155 if (linelen < 0) {
1156 /* header is out of the packet limits. */
1157 return -1;
1161 * Get a buffer that refers to the line.
1162 * in other words, the unstructured portion
1163 * of memcache.
1165 line = tvb_get_ptr (tvb, offset, linelen);
1166 lineend = line + linelen;
1168 /* VALUE token */
1169 tokenlen = get_token_len (line, lineend, &next_token);
1170 if (tokenlen == 0) {
1171 /* error */
1172 return -1;
1175 if ((tokenlen == 5) && strncmp (line, "VALUE", tokenlen) == 0) {
1176 /* proceed */
1177 } else if ((tokenlen == 3) && strncmp (line, "END", tokenlen) == 0) {
1178 /* done. reached an end of response. */
1179 offset += (int) (next_token - line);
1180 return offset;
1181 } else {
1182 /* invalid token */
1183 return -1;
1186 offset += (int) (next_token - line);
1187 line = next_token;
1189 /* key */
1190 tokenlen = get_token_len (line, lineend, &next_token);
1191 if (tokenlen == 0) {
1192 return -1;
1194 dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, true);
1195 offset += (int) (next_token - line);
1196 line = next_token;
1198 /* flags */
1199 tokenlen = get_token_len (line, lineend, &next_token);
1200 if (tokenlen == 0 || tokenlen > 5) {
1201 return -1;
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);
1210 line = next_token;
1212 /* bytes */
1213 tokenlen = get_token_len (line, lineend, &next_token);
1214 if (tokenlen == 0 || tokenlen > 10) {
1215 return -1;
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);
1224 line = next_token;
1226 /* check if cas id is present */
1227 tokenlen = get_token_len (line, lineend, &next_token);
1228 if (tokenlen > 20) {
1229 return -1;
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);
1239 /* CRLF */
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);
1249 if (offset == -1) {
1250 return offset;
1254 return offset;
1257 /* Basic memcache response dissector. */
1258 static int
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;
1263 int tokenlen;
1265 switch (opcode) {
1267 case OP_GET:
1268 case OP_GETS:
1269 return get_response_dissector (tvb, pinfo, tree, offset);
1271 case OP_VERSION:
1272 /* response code. */
1273 tokenlen = get_token_len (line, lineend, &next_token);
1274 if (tokenlen == 0) {
1275 return -1;
1277 if ((tokenlen == 7) && strncmp (line, "VERSION", tokenlen) == 0) {
1278 offset += (int) (next_token - line);
1279 line = next_token;
1280 } else {
1281 return -1;
1284 /* version string */
1285 tokenlen = get_token_len (line, lineend, &next_token);
1286 if (tokenlen == 0) {
1287 /* expecting version string. */
1288 return -1;
1291 proto_tree_add_item (tree, hf_version, tvb, offset, tokenlen, ENC_ASCII);
1292 offset += (int) (next_token - line);
1293 line = next_token;
1295 /* CRLF */
1296 tokenlen = get_token_len (line, lineend, &next_token);
1297 if (tokenlen != 0) {
1298 /* invalid token */
1299 return -1;
1302 return offset;
1304 case OP_STAT:
1305 return stat_dissector (tvb, tree, offset);
1307 default:
1308 break;
1311 /* response code. */
1312 tokenlen = get_token_len (line, lineend, &next_token);
1313 if (tokenlen == 0) {
1314 return -1;
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);
1332 return offset;
1335 /* if we have reached this point:
1336 * it is either an incr/decr response of the format
1337 * <value>\r\n.
1338 * or
1339 * "stats sizes" response of the format:
1340 * <size> <count> \r\n
1342 if (opcode == OP_INCREMENT) {
1343 return incr_dissector (tvb, tree, offset);
1346 return offset;
1349 /* Basic memcache request dissector. */
1350 static int
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;
1355 int tokenlen;
1357 uint16_t flags;
1358 uint32_t expiration;
1359 uint32_t bytes;
1360 uint64_t cas;
1361 char response_chars[21]; /* cover uint64 (20 + 1) bytes*/
1363 /* command. */
1364 tokenlen = get_token_len (line, lineend, &next_token);
1365 if (tokenlen == 0) {
1366 return -1;
1368 proto_tree_add_item (tree, hf_command, tvb, offset, tokenlen, ENC_ASCII);
1369 offset += (int) (next_token - line);
1370 line = next_token;
1372 switch (opcode) {
1374 case OP_SET:
1375 case OP_ADD:
1376 case OP_REPLACE:
1377 case OP_APPEND:
1378 case OP_PREPEND:
1379 case OP_CAS:
1381 /* key */
1382 tokenlen = get_token_len (line, lineend, &next_token);
1383 if (tokenlen == 0) {
1384 return -1;
1387 dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, true);
1388 offset += (int) (next_token - line);
1389 line = next_token;
1391 /* flags */
1392 tokenlen = get_token_len (line, lineend, &next_token);
1393 if (tokenlen == 0 || tokenlen > 5) {
1394 return -1;
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);
1403 line = next_token;
1405 /* expiration */
1406 tokenlen = get_token_len (line, lineend, &next_token);
1407 if (tokenlen == 0 || tokenlen > 10) {
1408 return -1;
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);
1417 line = next_token;
1419 /* bytes */
1420 tokenlen = get_token_len (line, lineend, &next_token);
1421 if (tokenlen == 0 || tokenlen > 10) {
1422 return -1;
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);
1431 line = next_token;
1433 /* cas id. */
1434 if (opcode == OP_CAS) {
1435 tokenlen = get_token_len (line, lineend, &next_token);
1436 if (tokenlen == 0 || tokenlen > 20) {
1437 return -1;
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);
1446 line = next_token;
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);
1463 if (offset == -1) {
1464 return offset;
1466 break;
1468 case OP_INCREMENT:
1469 case OP_DECREMENT:
1470 /* key */
1471 tokenlen = get_token_len (line, lineend, &next_token);
1472 if (tokenlen == 0) {
1473 return -1;
1475 dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, true);
1476 offset += (int) (next_token - line);
1477 line = next_token;
1479 /* value */
1480 tokenlen = get_token_len (line, lineend, &next_token);
1481 if (tokenlen == 0) {
1482 return -1;
1484 proto_tree_add_item (tree, hf_value, tvb, offset, tokenlen, ENC_ASCII);
1485 offset += (int) (next_token - line);
1486 line = next_token;
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);
1496 line = next_token;
1497 } else {
1498 return -1; /* should have been noreply or CRLF. */
1501 /* CRLF */
1502 tokenlen = get_token_len (line, lineend, &next_token);
1503 if (tokenlen == 0) {
1504 return offset; /* CRLF */
1505 } else {
1506 /*something's wrong; invalid command maybe. */
1507 return -1;
1509 break;
1511 case OP_DELETE:
1512 /* key */
1513 tokenlen = get_token_len (line, lineend, &next_token);
1514 if (tokenlen == 0) {
1515 return -1;
1517 /* dissect key. */
1518 dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, true);
1519 offset += (int) (next_token - line);
1520 line = next_token;
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) {
1529 /* noreply */
1530 proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, ENC_ASCII);
1531 } else {
1532 /* expiration */
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);
1540 line = next_token;
1541 } else {
1542 return -1;
1545 /* CRLF */
1546 tokenlen = get_token_len (line, lineend, &next_token);
1547 if (tokenlen == 0) {
1548 return offset;
1549 } else {
1550 /*something's wrong; invalid command maybe. */
1551 return -1;
1553 break;
1555 case OP_GET:
1556 case OP_GETS:
1557 /* could be followed by any number of keys, add
1558 * them one by one. tokenlen cannot be 0 to begin
1559 * with.
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);
1568 line = next_token;
1570 break;
1572 case OP_STAT:
1573 tokenlen = get_token_len (line, lineend, &next_token);
1574 if (tokenlen == 0) { /* just the 'stats' command;*/
1575 return offset;
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);
1579 line = next_token;
1582 /* CRLF */
1583 tokenlen = get_token_len (line, lineend, &next_token);
1584 if (tokenlen == 0) {
1585 return offset;
1586 } else {
1587 /* something's wrong; invalid command maybe. */
1588 return -1;
1590 break;
1592 case OP_FLUSH:
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) {
1600 /* noreply */
1601 proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, ENC_ASCII);
1602 } else {
1603 /* expiration */
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);
1611 line = next_token;
1612 } else {
1613 return -1;
1616 /* maybe noreply now? */
1617 tokenlen = get_token_len (line, lineend, &next_token);
1618 if (tokenlen == 0) {
1619 return offset;
1621 if (tokenlen == 7 && strncmp (line, "noreply", 7) == 0) {
1622 /* noreply */
1623 proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, ENC_ASCII);
1624 offset += (int) (next_token - line);
1625 } else {
1626 return -1; /* expecting CRLF and if not noreply*/
1628 break;
1630 case OP_VERBOSE:
1631 /* not implemented for now.*/
1632 break;
1634 case OP_VERSION:
1635 case OP_QUIT:
1636 /* CRLF */
1637 tokenlen = get_token_len (line, lineend, &next_token);
1638 if (tokenlen == 0) {
1639 return offset;
1640 } else {
1641 /*something's wrong; invalid command maybe. */
1642 return -1;
1645 default:
1646 /* invalid command maybe; break out. */
1647 break;
1650 return offset;
1654 * any message that is not starting with the following keywords
1655 * is a response.
1657 static int
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;
1664 int indx = 0;
1666 /* look for a space */
1667 while (indx < linelen) {
1668 if (*ptr == ' ')
1669 break;
1671 ptr++;
1672 indx++;
1675 /* is it a response? */
1676 switch (indx) {
1677 case 2:
1678 if (strncmp (data, "OK", indx) == 0) {
1679 *type = MEMCACHE_RESPONSE;
1680 is_request_or_response = true;
1682 break;
1684 case 3:
1685 if (strncmp (data, "END", indx) == 0) {
1686 *type = MEMCACHE_RESPONSE;
1687 is_request_or_response = true;
1689 break;
1691 case 4:
1692 if (strncmp (data, "STAT", indx) == 0) {
1693 *opcode = OP_STAT;
1694 *type = MEMCACHE_RESPONSE;
1695 is_request_or_response = true;
1697 break;
1699 case 5:
1700 if (strncmp (data, "VALUE", indx) == 0) {
1701 *opcode = OP_GET;
1702 *type = MEMCACHE_RESPONSE;
1703 *expect_content_length = true;
1704 is_request_or_response = true;
1706 break;
1708 case 6:
1709 if (strncmp (data, "EXISTS", indx) == 0 ||
1710 strncmp (data, "STORED", indx) == 0) {
1711 *type = MEMCACHE_RESPONSE;
1712 is_request_or_response = true;
1714 break;
1716 case 7:
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;
1726 break;
1728 case 9:
1729 if (strncmp (data, "NOT_FOUND", indx) == 0) {
1730 *type = MEMCACHE_RESPONSE;
1731 is_request_or_response = true;
1733 break;
1735 case 10:
1736 if (strncmp (data, "NOT_STORED", indx) == 0) {
1737 *type = MEMCACHE_RESPONSE;
1738 is_request_or_response = true;
1740 break;
1742 default:
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? */
1752 switch (indx) {
1753 case 3:
1754 if (strncmp (data, "get", indx) == 0) {
1755 *opcode = OP_GET;
1756 *type = MEMCACHE_REQUEST;
1757 is_request_or_response = true;
1758 } else if (strncmp (data, "set", indx) == 0) {
1759 *opcode = OP_SET;
1760 *type = MEMCACHE_REQUEST;
1761 *expect_content_length = true;
1762 is_request_or_response = true;
1763 } else if (strncmp (data, "add", indx) == 0) {
1764 *opcode = OP_ADD;
1765 *type = MEMCACHE_REQUEST;
1766 *expect_content_length = true;
1767 is_request_or_response = true;
1768 } else if (strncmp (data, "cas", indx) == 0) {
1769 *opcode = OP_CAS;
1770 *type = MEMCACHE_REQUEST;
1771 *expect_content_length = true;
1772 is_request_or_response = true;
1774 break;
1776 case 4:
1777 if (strncmp (data, "gets", indx) == 0) {
1778 *opcode = OP_GETS;
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) {
1790 *opcode = OP_QUIT;
1791 *type = MEMCACHE_REQUEST;
1792 is_request_or_response = true;
1794 break;
1796 case 5:
1797 if (strncmp (data, "stats", indx) == 0) {
1798 *opcode = OP_STAT;
1799 *type = MEMCACHE_REQUEST;
1800 is_request_or_response = true;
1802 break;
1804 case 6:
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;
1815 break;
1817 case 7:
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;
1833 break;
1835 case 9:
1836 if (strncmp (data, "flush_all", indx) == 0) {
1837 *opcode = OP_FLUSH;
1838 *type = MEMCACHE_REQUEST;
1839 is_request_or_response = true;
1841 break;
1843 default:
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;
1852 /* XXX:
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. */
1860 static void
1861 dissect_memcache_text (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1863 int offset = 0;
1864 int len;
1866 while (tvb_reported_length_remaining (tvb, offset) != 0) {
1868 /* dissect the memcache packet. */
1869 len = dissect_memcache_message (tvb, offset, pinfo, tree);
1870 if (len == -1)
1871 break;
1872 offset += len;
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) */
1884 static int
1885 dissect_memcache_tcp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
1887 int offset = 0;
1888 uint8_t magic;
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);
1895 } else {
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) */
1903 static int
1904 dissect_memcache_udp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
1906 int offset = 0;
1907 uint8_t magic;
1909 magic = tvb_get_uint8 (tvb, offset);
1911 if (try_val_to_str (magic, magic_vals) != NULL) {
1912 dissect_memcache (tvb, pinfo, tree, data);
1913 } else {
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
1922 * dissectors.
1924 void
1925 proto_register_memcache (void)
1927 static hf_register_info hf[] = {
1928 { &hf_magic,
1929 { "Magic", "memcache.magic",
1930 FT_UINT8, BASE_DEC, VALS (magic_vals), 0x0,
1931 "Magic number", HFILL } },
1933 { &hf_opcode,
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 } },
1943 { &hf_key_length,
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 } },
1948 { &hf_value_length,
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 } },
1953 { &hf_data_type,
1954 { "Data type", "memcache.data_type",
1955 FT_UINT8, BASE_DEC, VALS (data_type_vals), 0x0,
1956 NULL, HFILL } },
1958 { &hf_reserved,
1959 { "Reserved", "memcache.reserved",
1960 FT_UINT16, BASE_DEC, NULL, 0x0,
1961 "Reserved for future use", HFILL } },
1963 { &hf_status,
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 } },
1973 { &hf_opaque,
1974 { "Opaque", "memcache.opaque",
1975 FT_UINT32, BASE_DEC, NULL, 0x0,
1976 NULL, HFILL } },
1978 { &hf_cas,
1979 { "CAS", "memcache.cas",
1980 FT_UINT64, BASE_DEC, NULL, 0x0,
1981 "Data version check", HFILL } },
1983 { &hf_extras,
1984 { "Extras", "memcache.extras",
1985 FT_NONE, BASE_NONE, NULL, 0x0,
1986 NULL, HFILL } },
1988 { &hf_extras_flags,
1989 { "Flags", "memcache.extras.flags",
1990 FT_UINT32, BASE_HEX, NULL, 0x0,
1991 NULL, HFILL } },
1993 { &hf_extras_expiration,
1994 { "Expiration", "memcache.extras.expiration",
1995 FT_UINT32, BASE_DEC, NULL, 0x0,
1996 NULL, HFILL } },
1998 { &hf_extras_delta,
1999 { "Amount to add", "memcache.extras.delta",
2000 FT_UINT64, BASE_DEC, NULL, 0x0,
2001 NULL, HFILL } },
2003 { &hf_extras_initial,
2004 { "Initial value", "memcache.extras.initial",
2005 FT_UINT64, BASE_DEC, NULL, 0x0,
2006 NULL, HFILL } },
2008 { &hf_extras_unknown,
2009 { "Unknown", "memcache.extras.unknown",
2010 FT_BYTES, BASE_NONE, NULL, 0x0,
2011 "Unknown Extras", HFILL } },
2013 { &hf_key,
2014 { "Key", "memcache.key",
2015 FT_STRING, BASE_NONE, NULL, 0x0,
2016 NULL, HFILL } },
2018 { &hf_value,
2019 { "Value", "memcache.value",
2020 FT_STRING, BASE_NONE, NULL, 0x0,
2021 NULL, HFILL } },
2023 { &hf_uint64_response,
2024 { "Response", "memcache.extras.response",
2025 FT_UINT64, BASE_DEC, NULL, 0x0,
2026 NULL, HFILL } },
2028 { &hf_command,
2029 { "Command", "memcache.command",
2030 FT_STRING, BASE_NONE , NULL, 0x0,
2031 NULL, HFILL } },
2033 { &hf_subcommand,
2034 { "Sub command", "memcache.subcommand",
2035 FT_STRING, BASE_NONE, NULL, 0x0,
2036 "Sub command if any", HFILL } },
2038 { &hf_flags,
2039 { "Flags", "memcache.flags",
2040 FT_UINT16, BASE_DEC, NULL, 0x0,
2041 NULL, HFILL } },
2043 { &hf_expiration,
2044 { "Expiration", "memcache.expiration",
2045 FT_UINT32, BASE_DEC, NULL, 0x0,
2046 NULL, HFILL } },
2048 { &hf_noreply,
2049 { "Noreply", "memcache.noreply",
2050 FT_STRING, BASE_NONE, NULL, 0x0,
2051 "Client does not expect a reply", HFILL } },
2053 { &hf_response,
2054 { "Response", "memcache.response",
2055 FT_STRING, BASE_NONE, NULL, 0x0,
2056 "Response command", HFILL } },
2058 { &hf_version,
2059 { "Version", "memcache.version",
2060 FT_STRING, BASE_NONE, NULL, 0x0,
2061 "Version of running memcache", HFILL } },
2063 { &hf_slabclass,
2064 { "Slab class", "memcache.slabclass",
2065 FT_UINT32, BASE_DEC, NULL, 0x0,
2066 "Slab class of a stat", HFILL } },
2068 { &hf_name,
2069 { "Stat name", "memcache.name",
2070 FT_STRING, BASE_NONE, NULL, 0x0,
2071 "Name of a stat", HFILL } },
2073 { &hf_name_value,
2074 { "Stat value", "memcache.name_value",
2075 FT_STRING, BASE_NONE, NULL, 0x0,
2076 "Value of a stat", HFILL } },
2079 static int *ett[] = {
2080 &ett_memcache,
2081 &ett_extras
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. */
2131 void
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);
2139 * Editor modelines
2141 * Local Variables:
2142 * c-basic-offset: 2
2143 * tab-width: 8
2144 * indent-tabs-mode: nil
2145 * End:
2147 * ex: set shiftwidth=2 tabstop=8 expandtab:
2148 * :indentSize=2:tabSize=8:noTabs=true: