Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-memcache.c
Line
Count
Source
1
/* packet-memcache.c
2
 * Routines for Memcache Binary Protocol
3
 * http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol
4
 *
5
 * Copyright 2009, Stig Bjorlykke <stig@bjorlykke.org>
6
 *
7
 * Routines for Memcache Textual Protocol
8
 * http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
9
 *
10
 * Copyright 2009, Rama Chitta <rama@gear6.com>
11
 *
12
 * Wireshark - Network traffic analyzer
13
 * By Gerald Combs <gerald@wireshark.org>
14
 * Copyright 1998 Gerald Combs
15
 *
16
 * SPDX-License-Identifier: GPL-2.0-or-later
17
 */
18
19
#include "config.h"
20
21
#include <stdio.h>  /* for sscanf() */
22
23
#include <epan/packet.h>
24
#include <epan/strutil.h>
25
#include <epan/prefs.h>
26
#include <epan/expert.h>
27
28
#include <wsutil/strtoi.h>
29
30
#include "packet-tcp.h"
31
32
void proto_register_memcache (void);
33
void proto_reg_handoff_memcache(void);
34
35
32
#define MEMCACHE_DEFAULT_RANGE  "11211"
36
286
#define MEMCACHE_HEADER_LEN   24
37
38
/* Magic Byte */
39
#define MAGIC_REQUEST         0x80
40
#define MAGIC_RESPONSE        0x81
41
42
/* Response Status */
43
#define RS_NO_ERROR           0x0000
44
#define RS_KEY_NOT_FOUND      0x0001
45
#define RS_KEY_EXISTS         0x0002
46
#define RS_VALUE_TOO_BIG      0x0003
47
#define RS_INVALID_ARGUMENTS  0x0004
48
#define RS_ITEM_NOT_STORED    0x0005
49
#define RS_UNKNOWN_COMMAND    0x0081
50
#define RS_OUT_OF_MEMORY      0x0082
51
52
/* Command Opcodes */
53
260
#define OP_GET                0x00
54
196
#define OP_SET                0x01
55
158
#define OP_ADD                0x02
56
155
#define OP_REPLACE            0x03
57
38
#define OP_DELETE             0x04
58
30
#define OP_INCREMENT          0x05
59
29
#define OP_DECREMENT          0x06
60
37
#define OP_QUIT               0x07
61
22
#define OP_FLUSH              0x08
62
154
#define OP_GET_Q              0x09
63
11
#define OP_NO_OP              0x0A
64
10
#define OP_VERSION            0x0B
65
170
#define OP_GET_K              0x0C
66
156
#define OP_GET_K_Q            0x0D
67
129
#define OP_APPEND             0x0E
68
133
#define OP_PREPEND            0x0F
69
7
#define OP_STAT               0x10
70
150
#define OP_SET_Q              0x11
71
151
#define OP_ADD_Q              0x12
72
151
#define OP_REPLACE_Q          0x13
73
31
#define OP_DELETE_Q           0x14
74
21
#define OP_INCREMENT_Q        0x15
75
21
#define OP_DECREMENT_Q        0x16
76
14
#define OP_QUIT_Q             0x17
77
19
#define OP_FLUSH_Q            0x18
78
131
#define OP_APPEND_Q           0x19
79
131
#define OP_PREPEND_Q          0x1A
80
81
/* Internally defined command opcodes used in the textual dissector only */
82
/* This values are not defined in any standard and can be redefined here */
83
0
#define OP_GETS               0xF0
84
0
#define OP_CAS                0xF1
85
0
#define OP_VERBOSE            0xF2
86
87
/* Data Types */
88
#define DT_RAW_BYTES          0x00
89
90
static int proto_memcache;
91
92
static dissector_handle_t memcache_tcp_handle;
93
static dissector_handle_t memcache_udp_handle;
94
95
static int hf_magic;
96
static int hf_opcode;
97
static int hf_extras_length;
98
static int hf_key_length;
99
static int hf_value_length;
100
static int hf_data_type;
101
static int hf_reserved;
102
static int hf_status;
103
static int hf_total_body_length;
104
static int hf_opaque;
105
static int hf_cas;
106
static int hf_extras;
107
static int hf_extras_flags;
108
static int hf_extras_expiration;
109
static int hf_extras_delta;
110
static int hf_extras_initial;
111
static int hf_extras_unknown;
112
static int hf_key;
113
static int hf_value;
114
static int hf_uint64_response;
115
116
static int hf_command;
117
static int hf_subcommand;
118
static int hf_flags;
119
static int hf_expiration;
120
static int hf_noreply;
121
122
static int hf_response;
123
124
static int hf_version;
125
static int hf_slabclass;
126
static int hf_name;
127
static int hf_name_value;
128
129
static int ett_memcache;
130
static int ett_extras;
131
132
static expert_field ei_value_missing;
133
static expert_field ei_extras_missing;
134
static expert_field ei_value_length;
135
static expert_field ei_key_missing;
136
static expert_field ei_key_unknown;
137
static expert_field ei_extras_unknown;
138
static expert_field ei_value_unknown;
139
static expert_field ei_status_response;
140
static expert_field ei_opcode_unknown;
141
static expert_field ei_reserved_value;
142
static expert_field ei_magic_unknown;
143
144
static const value_string magic_vals[] = {
145
  { MAGIC_REQUEST,         "Request"            },
146
  { MAGIC_RESPONSE,        "Response"           },
147
  { 0, NULL }
148
};
149
150
static const value_string status_vals[] = {
151
  { RS_NO_ERROR,           "No error"           },
152
  { RS_KEY_NOT_FOUND,      "Key not found"      },
153
  { RS_KEY_EXISTS,         "Key exists"         },
154
  { RS_VALUE_TOO_BIG,      "Value too big"      },
155
  { RS_INVALID_ARGUMENTS,  "Invalid arguments"  },
156
  { RS_ITEM_NOT_STORED,    "Item not stored"    },
157
  { RS_UNKNOWN_COMMAND,    "Unknown command"    },
158
  { RS_OUT_OF_MEMORY,      "Out of memory"      },
159
  { 0, NULL }
160
};
161
162
static const value_string opcode_vals[] = {
163
  { OP_GET,                "Get"                },
164
  { OP_SET,                "Set"                },
165
  { OP_ADD,                "Add"                },
166
  { OP_REPLACE,            "Replace"            },
167
  { OP_DELETE,             "Delete"             },
168
  { OP_INCREMENT,          "Increment"          },
169
  { OP_DECREMENT,          "Decrement"          },
170
  { OP_QUIT,               "Quit"               },
171
  { OP_FLUSH,              "Flush"              },
172
  { OP_GET_Q,              "Get Quietly"        },
173
  { OP_NO_OP,              "No-op"              },
174
  { OP_VERSION,            "Version"            },
175
  { OP_GET_K,              "Get Key"            },
176
  { OP_GET_K_Q,            "Get Key Quietly"    },
177
  { OP_APPEND,             "Append"             },
178
  { OP_PREPEND,            "Prepend"            },
179
  { OP_STAT,               "Statistics"         },
180
  { OP_SET_Q,              "Set Quietly"        },
181
  { OP_ADD_Q,              "Add Quietly"        },
182
  { OP_REPLACE_Q,          "Replace Quietly"    },
183
  { OP_DELETE_Q,           "Delete Quietly"     },
184
  { OP_INCREMENT_Q,        "Increment Quietly"  },
185
  { OP_DECREMENT_Q,        "Decrement Quietly"  },
186
  { OP_QUIT_Q,             "Quit Quietly"       },
187
  { OP_FLUSH_Q,            "Flush Quietly"      },
188
  { OP_APPEND_Q,           "Append Quietly"     },
189
  { OP_PREPEND_Q,          "Prepend Quietly"    },
190
  /* Internally defined values not valid here */
191
  { 0, NULL }
192
};
193
194
static const value_string data_type_vals[] = {
195
  { DT_RAW_BYTES,          "Raw bytes"          },
196
  { 0, NULL }
197
};
198
199
/* memcache message types. */
200
typedef enum _memcache_type {
201
  MEMCACHE_REQUEST,
202
  MEMCACHE_RESPONSE,
203
  MEMCACHE_UNKNOWN
204
} memcache_type_t;
205
206
/* desegmentation of MEMCACHE header */
207
static bool memcache_desegment_headers = true;
208
209
/* desegmentation of MEMCACHE payload */
210
static bool memcache_desegment_body = true;
211
212
/* should refer to either the request or the response dissector.
213
 */
214
typedef int (*ReqRespDissector)(tvbuff_t*, packet_info *, proto_tree *,
215
                                unsigned, const unsigned char*, const unsigned char*, uint8_t);
216
217
/* determines if a packet contains a memcache
218
 * request or reply by looking at its first token.
219
 */
220
static int
221
is_memcache_request_or_reply(const char *data, int linelen, uint8_t *opcode,
222
                             memcache_type_t *type, bool *expect_content_length,
223
                             ReqRespDissector *reqresp_dissector);
224
225
static unsigned
226
get_memcache_pdu_len (packet_info *pinfo _U_, tvbuff_t *tvb,
227
                      int offset, void *data _U_)
228
286
{
229
286
  uint32_t body_len;
230
231
  /* Get the length of the memcache body */
232
286
  body_len = tvb_get_ntohl(tvb, offset+8);
233
234
  /* That length doesn't include the header; add that in */
235
286
  return body_len + MEMCACHE_HEADER_LEN;
236
286
}
237
238
static void
239
dissect_extras (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
240
                unsigned offset, uint8_t extras_len, uint8_t opcode, bool request)
241
177
{
242
177
  proto_tree *extras_tree = NULL;
243
177
  proto_item *extras_item = NULL, *ti;
244
177
  int         save_offset = offset;
245
177
  bool        illegal = false;  /* Set when extras shall not be present */
246
177
  bool        missing = false;  /* Set when extras is missing */
247
248
177
  if (extras_len) {
249
19
    extras_item = proto_tree_add_item (tree, hf_extras, tvb, offset, extras_len, ENC_NA);
250
19
    extras_tree = proto_item_add_subtree (extras_item, ett_extras);
251
19
  }
252
253
177
  switch (opcode) {
254
255
109
  case OP_GET:
256
109
  case OP_GET_Q:
257
125
  case OP_GET_K:
258
126
  case OP_GET_K_Q:
259
126
    if (extras_len) {
260
4
      if (request) {
261
        /* Request shall not have extras */
262
2
        illegal = true;
263
2
      } else {
264
2
        proto_tree_add_item (extras_tree, hf_extras_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
265
2
        offset += 4;
266
2
      }
267
122
    } else if (!request) {
268
      /* Response must have extras */
269
4
      missing = true;
270
4
    }
271
126
    break;
272
273
4
  case OP_SET:
274
5
  case OP_SET_Q:
275
7
  case OP_ADD:
276
7
  case OP_ADD_Q:
277
7
  case OP_REPLACE:
278
7
  case OP_REPLACE_Q:
279
7
    if (extras_len) {
280
0
      if (request) {
281
0
        proto_tree_add_item (extras_tree, hf_extras_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
282
0
        offset += 4;
283
284
0
        proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
285
0
        offset += 4;
286
0
      } else {
287
        /* Response shall not have extras */
288
0
        illegal = true;
289
0
      }
290
7
    } else if (request) {
291
      /* Request must have extras */
292
1
      missing = true;
293
1
    }
294
7
    break;
295
296
1
  case OP_INCREMENT:
297
1
  case OP_INCREMENT_Q:
298
1
  case OP_DECREMENT:
299
1
  case OP_DECREMENT_Q:
300
1
    if (extras_len) {
301
0
      if (request) {
302
0
        proto_tree_add_item (extras_tree, hf_extras_delta, tvb, offset, 8, ENC_BIG_ENDIAN);
303
0
        offset += 8;
304
305
0
        proto_tree_add_item (extras_tree, hf_extras_initial, tvb, offset, 8, ENC_BIG_ENDIAN);
306
0
        offset += 8;
307
308
0
        proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
309
0
        offset += 4;
310
0
      } else {
311
        /* Response must not have extras (response is in Value) */
312
0
        illegal = true;
313
0
      }
314
1
    } else if (request) {
315
      /* Request must have extras */
316
1
      missing = true;
317
1
    }
318
1
    break;
319
320
9
  case OP_FLUSH:
321
9
  case OP_FLUSH_Q:
322
9
    if (extras_len) {
323
0
      proto_tree_add_item (extras_tree, hf_extras_expiration, tvb, offset, 4, ENC_BIG_ENDIAN);
324
0
      offset += 4;
325
0
    }
326
9
    break;
327
328
3
  case OP_DELETE:
329
3
  case OP_DELETE_Q:
330
3
  case OP_QUIT:
331
3
  case OP_QUIT_Q:
332
3
  case OP_VERSION:
333
3
  case OP_APPEND:
334
6
  case OP_APPEND_Q:
335
7
  case OP_PREPEND:
336
7
  case OP_PREPEND_Q:
337
7
  case OP_STAT:
338
    /* Must not have extras */
339
7
    if (extras_len) {
340
2
      illegal = true;
341
2
    }
342
7
    break;
343
344
18
  default:
345
18
    if (extras_len) {
346
      /* Decode as unknown extras */
347
4
      proto_tree_add_item (extras_tree, hf_extras_unknown, tvb, offset, extras_len, ENC_NA);
348
4
      offset += extras_len;
349
4
    }
350
18
    break;
351
177
  }
352
353
164
  if (illegal) {
354
4
    ti = proto_tree_add_item (extras_tree, hf_extras_unknown, tvb, offset, extras_len, ENC_NA);
355
4
    expert_add_info_format(pinfo, ti, &ei_extras_unknown, "%s %s shall not have Extras",
356
4
                    val_to_str(pinfo->pool, opcode, opcode_vals, "Opcode %d"),
357
4
                    request ? "Request" : "Response");
358
4
    offset += extras_len;
359
160
  } else if (missing) {
360
6
    proto_tree_add_expert_format(tree, pinfo, &ei_extras_missing, tvb, offset, 0, "%s %s must have Extras",
361
6
                            val_to_str(pinfo->pool, opcode, opcode_vals, "Opcode %d"),
362
6
                            request ? "Request" : "Response");
363
6
  }
364
365
164
  if ((offset - save_offset) != extras_len) {
366
2
    expert_add_info_format(pinfo, extras_item, &ei_extras_unknown, "Illegal Extras length, should be %d", offset - save_offset);
367
2
  }
368
164
}
369
370
static void
371
dissect_key (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
372
             unsigned offset, int key_len, uint8_t opcode, bool request)
373
164
{
374
164
  proto_item *ti = NULL;
375
164
  bool        illegal = false;  /* Set when key shall not be present */
376
164
  bool        missing = false;  /* Set when key is missing */
377
378
164
  if (key_len) {
379
24
    ti = proto_tree_add_item (tree, hf_key, tvb, offset, key_len, ENC_ASCII);
380
24
    offset += key_len;
381
382
24
    if ((opcode == OP_QUIT) || (opcode == OP_QUIT_Q) || (opcode == OP_NO_OP) || (opcode == OP_VERSION)) {
383
      /* Request and Response must not have key */
384
0
      illegal = true;
385
0
    }
386
24
    if ((opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) || (opcode == OP_DELETE) ||
387
3
        (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) || (opcode == OP_DELETE_Q) ||
388
3
        (opcode == OP_FLUSH) || (opcode == OP_APPEND) || (opcode == OP_PREPEND) ||
389
2
        (opcode == OP_FLUSH_Q) || (opcode == OP_APPEND_Q) || (opcode == OP_PREPEND_Q))
390
1
    {
391
      /* Response must not have a key */
392
1
      if (!request) {
393
0
        illegal = true;
394
0
      }
395
1
    }
396
140
  } else {
397
140
    if ((opcode == OP_GET) || (opcode == OP_GET_Q) || (opcode == OP_GET_K) || (opcode == OP_GET_K_Q) ||
398
25
        (opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) || (opcode == OP_DELETE) ||
399
18
        (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) || (opcode == OP_DELETE_Q) ||
400
17
        (opcode == OP_INCREMENT) || (opcode == OP_DECREMENT) || (opcode == OP_INCREMENT_Q) || (opcode == OP_DECREMENT_Q))
401
124
    {
402
      /* Request must have key */
403
124
      if (request) {
404
116
        missing = true;
405
116
      }
406
124
    }
407
140
  }
408
409
164
  if (illegal) {
410
0
    expert_add_info_format(pinfo, ti, &ei_key_unknown, "%s %s shall not have Key",
411
0
            val_to_str(pinfo->pool, opcode, opcode_vals, "Opcode %d"),
412
0
            request ? "Request" : "Response");
413
164
  } else if (missing) {
414
116
    proto_tree_add_expert_format(tree, pinfo, &ei_key_missing, tvb, offset, 0, "%s Request must have Key",
415
116
                            val_to_str(pinfo->pool, opcode, opcode_vals, "Opcode %d"));
416
116
  }
417
164
}
418
419
static void
420
dissect_value (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
421
               unsigned offset, uint32_t value_len, uint8_t opcode, bool request)
422
143
{
423
143
  proto_item *ti = NULL;
424
143
  bool        illegal = false;  /* Set when value shall not be present */
425
143
  bool        missing = false;  /* Set when value is missing */
426
427
143
  if (value_len > 0) {
428
27
    if (!request && ((opcode == OP_INCREMENT) || (opcode == OP_DECREMENT))) {
429
0
      ti = proto_tree_add_item (tree, hf_uint64_response, tvb, offset, 8, ENC_BIG_ENDIAN);
430
0
      if (value_len != 8) {
431
0
        expert_add_info(pinfo, ti, &ei_value_length);
432
0
      }
433
27
    } else {
434
27
      ti = proto_tree_add_item (tree, hf_value, tvb, offset, value_len, ENC_ASCII);
435
27
    }
436
27
    offset += value_len;
437
27
  }
438
439
  /* Sanity check */
440
143
  if (value_len) {
441
11
    if ((opcode == OP_GET) || (opcode == OP_GET_Q) || (opcode == OP_GET_K) || (opcode == OP_GET_K_Q) ||
442
4
        (opcode == OP_INCREMENT) || (opcode == OP_DECREMENT) || (opcode == OP_VERSION) ||
443
4
        (opcode == OP_INCREMENT_Q) || (opcode == OP_DECREMENT_Q))
444
7
    {
445
      /* Request must not have value */
446
7
      if (request) {
447
6
        illegal = true;
448
6
      }
449
7
    }
450
11
    if ((opcode == OP_DELETE) ||  (opcode == OP_QUIT) || (opcode == OP_FLUSH) || (opcode == OP_NO_OP) ||
451
8
        (opcode == OP_DELETE_Q) ||  (opcode == OP_QUIT_Q) || (opcode == OP_FLUSH_Q))
452
3
    {
453
      /* Request and Response must not have value */
454
3
      illegal = true;
455
3
    }
456
11
    if ((opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) ||
457
11
        (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) ||
458
11
        (opcode == OP_APPEND) || (opcode == OP_PREPEND) || (opcode == OP_APPEND_Q) || (opcode == OP_PREPEND_Q))
459
0
    {
460
      /* Response must not have value */
461
0
      if (!request) {
462
0
        illegal = true;
463
0
      }
464
0
    }
465
132
  } else {
466
132
    if ((opcode == OP_SET) || (opcode == OP_ADD) || (opcode == OP_REPLACE) ||
467
113
        (opcode == OP_SET_Q) || (opcode == OP_ADD_Q) || (opcode == OP_REPLACE_Q) ||
468
113
        (opcode == OP_APPEND) || (opcode == OP_PREPEND) || (opcode == OP_APPEND_Q) || (opcode == OP_PREPEND_Q))
469
5
    {
470
      /* Request must have a value */
471
5
      if (request) {
472
2
        missing = true;
473
2
      }
474
5
    }
475
132
  }
476
477
143
  if (illegal) {
478
9
    expert_add_info_format(pinfo, ti, &ei_value_unknown, "%s %s shall not have Value",
479
9
            val_to_str(pinfo->pool, opcode, opcode_vals, "Opcode %d"),
480
9
            request ? "Request" : "Response");
481
134
  } else if (missing) {
482
2
    proto_tree_add_expert_format(tree, pinfo, &ei_value_missing, tvb, offset, 0, "%s %s must have Value",
483
2
                            val_to_str(pinfo->pool, opcode, opcode_vals, "Opcode %d"),
484
2
                            request ? "Request" : "Response");
485
2
  }
486
143
}
487
488
static int
489
dissect_memcache (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
490
297
{
491
297
  proto_tree *memcache_tree;
492
297
  proto_item *memcache_item, *ti;
493
297
  int         offset = 0;
494
297
  uint8_t     magic, opcode, extras_len;
495
297
  uint16_t    key_len, status = 0;
496
297
  uint32_t    body_len, value_len;
497
297
  bool        request;
498
499
297
  col_set_str (pinfo->cinfo, COL_PROTOCOL, "MEMCACHE");
500
297
  col_clear (pinfo->cinfo, COL_INFO);
501
502
297
  memcache_item = proto_tree_add_item (tree, proto_memcache, tvb, offset, -1, ENC_NA);
503
297
  memcache_tree = proto_item_add_subtree (memcache_item, ett_memcache);
504
505
297
  ti = proto_tree_add_item_ret_uint8 (memcache_tree, hf_magic, tvb, offset, 1, ENC_BIG_ENDIAN, &magic);
506
297
  offset += 1;
507
508
297
  if (try_val_to_str (magic, magic_vals) == NULL) {
509
231
    expert_add_info_format(pinfo, ti, &ei_magic_unknown, "Unknown magic byte: %d", magic);
510
231
  }
511
512
297
  ti = proto_tree_add_item_ret_uint8 (memcache_tree, hf_opcode, tvb, offset, 1, ENC_BIG_ENDIAN, &opcode);
513
297
  offset += 1;
514
515
297
  if (try_val_to_str (opcode, opcode_vals) == NULL) {
516
99
    expert_add_info_format(pinfo, ti, &ei_opcode_unknown, "Unknown opcode: %d", opcode);
517
99
  }
518
519
297
  proto_item_append_text (memcache_item, ", %s %s", val_to_str(pinfo->pool, opcode, opcode_vals, "Unknown opcode (%d)"),
520
297
                          val_to_str(pinfo->pool, magic, magic_vals, "Unknown magic (%d)"));
521
522
297
  col_append_fstr (pinfo->cinfo, COL_INFO, "%s %s",
523
297
                   val_to_str(pinfo->pool, opcode, opcode_vals, "Unknown opcode (%d)"),
524
297
                   val_to_str(pinfo->pool, magic, magic_vals, "Unknown magic (%d)"));
525
526
297
  key_len = tvb_get_ntohs (tvb, offset);
527
297
  proto_tree_add_item (memcache_tree, hf_key_length, tvb, offset, 2, ENC_BIG_ENDIAN);
528
297
  offset += 2;
529
530
297
  extras_len = tvb_get_uint8 (tvb, offset);
531
297
  proto_tree_add_item (memcache_tree, hf_extras_length, tvb, offset, 1, ENC_BIG_ENDIAN);
532
297
  offset += 1;
533
534
297
  proto_tree_add_item (memcache_tree, hf_data_type, tvb, offset, 1, ENC_BIG_ENDIAN);
535
297
  offset += 1;
536
537
297
  status = tvb_get_ntohs (tvb, offset);
538
297
  if (magic & 0x01) {    /* We suppose this is a response, even when unknown magic byte */
539
116
    request = false;
540
116
    ti = proto_tree_add_item (memcache_tree, hf_status, tvb, offset, 2, ENC_BIG_ENDIAN);
541
116
    if (status != 0) {
542
78
      expert_add_info_format(pinfo, ti, &ei_status_response, "%s: %s",
543
78
                              val_to_str(pinfo->pool, opcode, opcode_vals, "Unknown opcode (%d)"),
544
78
                              val_to_str(pinfo->pool, status, status_vals, "Status: %d"));
545
78
    }
546
181
  } else {
547
181
    request = true;
548
181
    ti = proto_tree_add_item (memcache_tree, hf_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
549
181
    if (status != 0) {
550
41
      expert_add_info_format(pinfo, ti, &ei_reserved_value, "Reserved value: %d", status);
551
41
    }
552
181
  }
553
297
  offset += 2;
554
555
297
  body_len = tvb_get_ntohl (tvb, offset);
556
297
  value_len = body_len - extras_len - key_len;
557
297
  ti = proto_tree_add_uint (memcache_tree, hf_value_length, tvb, offset, 0, value_len);
558
297
  proto_item_set_generated (ti);
559
560
297
  proto_tree_add_item (memcache_tree, hf_total_body_length, tvb, offset, 4, ENC_BIG_ENDIAN);
561
297
  offset += 4;
562
563
297
  proto_tree_add_item (memcache_tree, hf_opaque, tvb, offset, 4, ENC_BIG_ENDIAN);
564
297
  offset += 4;
565
566
297
  proto_tree_add_item (memcache_tree, hf_cas, tvb, offset, 8, ENC_BIG_ENDIAN);
567
297
  offset += 8;
568
569
297
  if (status == 0) {
570
177
    dissect_extras (tvb, pinfo, memcache_tree, offset, extras_len, opcode, request);
571
177
    offset += extras_len;
572
573
177
    dissect_key (tvb, pinfo, memcache_tree, offset, key_len, opcode, request);
574
177
    offset += key_len;
575
576
177
    dissect_value (tvb, pinfo, memcache_tree, offset, value_len, opcode, request);
577
    /*offset += value_len;*/
578
177
  } else if (body_len) {
579
26
    proto_tree_add_item (memcache_tree, hf_value, tvb, offset, body_len, ENC_ASCII);
580
    /*offset += body_len;*/
581
582
26
    col_append_fstr (pinfo->cinfo, COL_INFO, " (%s)",
583
26
                     val_to_str(pinfo->pool, status, status_vals, "Unknown status: %d"));
584
94
  } else {
585
94
    proto_tree_add_expert_format(memcache_tree, pinfo, &ei_value_missing, tvb, offset, 0, "%s with status %s (%d) must have Value",
586
94
                            val_to_str(pinfo->pool, opcode, opcode_vals, "Opcode %d"),
587
94
                            val_to_str_const (status, status_vals, "Unknown"), status);
588
94
  }
589
590
297
  return tvb_captured_length(tvb);
591
297
}
592
593
/* Obtain the content length by peeping into the header.
594
 */
595
static bool
596
get_payload_length (tvbuff_t *tvb, packet_info *pinfo, const unsigned token_number, unsigned offset,
597
                    uint32_t *bytes, bool *content_length_found)
598
0
{
599
0
  const unsigned char *next_token;
600
0
  const unsigned char *line, *lineend;
601
0
  const char          *bytes_val;
602
0
  unsigned             tokenlen, i = 0, linelen = 0;
603
0
  unsigned             next_offset;
604
605
  /* get the header line. */
606
0
  if (!tvb_find_line_end_remaining(tvb, offset, &linelen, &next_offset)) {
607
0
    return false;
608
0
  }
609
610
0
  line = tvb_get_ptr (tvb, offset, linelen);
611
0
  lineend = line + linelen;
612
613
0
  while (++i < token_number) {
614
0
    tokenlen = get_token_len (line, lineend, &next_token);
615
0
    if (tokenlen == 0) {
616
0
      return false;
617
0
    }
618
0
    offset += (unsigned) (next_token - line);
619
0
    line = next_token;
620
0
  }
621
622
  /* line or the next_token has the value we want. */
623
0
  tokenlen = get_token_len (line, lineend, &next_token);
624
0
  if (tokenlen == 0)  {
625
0
    return false;
626
0
  }
627
628
0
  bytes_val = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset, tokenlen, ENC_ASCII);
629
0
  if (bytes_val) {
630
0
    if (sscanf (bytes_val, "%u", bytes) == 1) {
631
0
      *content_length_found = true;
632
0
    } else {
633
0
      return false;
634
0
    }
635
0
  } else {
636
0
    return false;
637
0
  }
638
639
  /* reached this far, we got what we want. */
640
0
  return true;
641
0
}
642
643
/* check if a PDU needs to be desegmented. */
644
static bool
645
desegment_pdus (tvbuff_t *tvb, packet_info *pinfo, const unsigned offset,
646
                const int data_offset, uint32_t content_length)
647
0
{
648
0
  unsigned length_remaining, reported_length_remaining;
649
650
  /* data_offset has been set to start of the data block. */
651
0
  if (!tvb_bytes_exist (tvb, data_offset, content_length)) {
652
653
0
    length_remaining = tvb_captured_length_remaining (tvb, data_offset);
654
0
    reported_length_remaining = tvb_reported_length_remaining (tvb, data_offset);
655
656
0
    if (length_remaining < reported_length_remaining) {
657
      /* It's a waste of time asking for more
658
       * data, because that data wasn't captured.
659
       */
660
0
      return false;
661
0
    }
662
663
0
    pinfo->desegment_offset = offset; /* start of the packet. */
664
0
    pinfo->desegment_len = (content_length + 2) - length_remaining; /* add 2 for /r/n */
665
666
0
    return true;
667
0
  }
668
0
  return false;
669
0
}
670
671
/*
672
 * Optionally do reassembly of the requests, responses and data.
673
 */
674
static bool
675
memcache_req_resp_hdrs_do_reassembly (
676
    tvbuff_t *tvb, const unsigned offset, packet_info *pinfo,
677
    const bool desegment_headers, const bool desegment_body,
678
    const memcache_type_t type, const bool expect_content_length)
679
0
{
680
0
  unsigned  linelen;
681
0
  unsigned  next_offset;
682
0
  unsigned  length_remaining;
683
0
  unsigned  reported_length_remaining;
684
0
  uint32_t  content_length          = 0;
685
0
  bool      content_length_found    = false;
686
0
  bool      ret                     = false;
687
688
  /*
689
   * If header desegmentation is activated, check the
690
   * header in this tvbuff.
691
   * request one more byte (we don't know how many bytes
692
   * we'll need, so we just ask for one).
693
   */
694
0
  if (desegment_headers && pinfo->can_desegment) {
695
0
    next_offset = offset;
696
697
0
    reported_length_remaining = tvb_reported_length_remaining (tvb, next_offset);
698
    /*
699
     * Request one more byte if there're no
700
     * bytes left in the reported data (if there're
701
     * bytes left in the reported data, but not in
702
     * the available data, requesting more bytes
703
     * won't help, as those bytes weren't captured).
704
     */
705
0
    if (reported_length_remaining < 1) {
706
0
      pinfo->desegment_offset = offset;
707
0
      pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
708
0
      return false;
709
0
    }
710
711
0
    length_remaining = tvb_captured_length_remaining (tvb, next_offset);
712
713
    /* Request one more byte if we cannot find a
714
     * header (i.e. a line end).
715
     */
716
0
    bool found = tvb_find_line_end_remaining(tvb, next_offset,&linelen, &next_offset);
717
0
    if (!found && length_remaining >= reported_length_remaining) {
718
      /* Not enough data; ask for one more byte. */
719
0
      pinfo->desegment_offset = offset;
720
0
      pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
721
0
      return false;
722
0
    }
723
724
    /* Browse through the header to find the content length.
725
     *
726
     * request:
727
     * <command name> <key> <flags> <exptime> <bytes> [noreply]\r\n
728
     * cas <key> <flags> <exptime> <bytes> <cas unqiue> [noreply]\r\n
729
     *
730
     * response:
731
     * VALUE <key> <flags> <bytes> [<cas unique>]\r\n
732
     * <data block>\r\n
733
     */
734
0
    if (expect_content_length == true) {
735
0
      switch (type) {
736
737
0
      case MEMCACHE_REQUEST:
738
        /* Get the fifth token in the header.*/
739
0
        ret = get_payload_length (tvb, pinfo, 5 , offset, &content_length, &content_length_found);
740
0
        if (!ret) {
741
0
          return false;
742
0
        }
743
0
        break;
744
745
0
      case MEMCACHE_RESPONSE:
746
        /* Get the fourth token in the header.*/
747
0
        ret =  get_payload_length (tvb, pinfo, 4 , offset, &content_length, &content_length_found);
748
0
        if (!ret) {
749
0
          return false;
750
0
        }
751
0
        break;
752
753
0
      default:
754
        /* Unrecognized message type. */
755
0
        return false;
756
0
      }
757
0
    }
758
0
  }
759
760
  /* We have reached the end of a header, so there
761
   * should be 'content_length' bytes after this
762
   * followed by CRLF. The next_offset points to the
763
   * start of the data bytes.
764
   */
765
0
  if (desegment_body && content_length_found) {
766
0
    return !desegment_pdus (tvb, pinfo, offset, next_offset, content_length);
767
0
  }
768
769
  /* No further desegmentation needed. */
770
0
  return true;
771
0
}
772
773
/* Dissect a memcache message. */
774
static int
775
dissect_memcache_message (tvbuff_t *tvb, unsigned offset, packet_info *pinfo, proto_tree *tree)
776
36
{
777
36
  const unsigned char      *line;
778
36
  const unsigned char      *lineend;
779
36
  unsigned           orig_offset;
780
36
  unsigned           first_linelen;
781
36
  unsigned           datalen;
782
36
  bool               expect_content_length = false;
783
36
  unsigned           next_offset;
784
36
  int                toffset;
785
786
36
  bool               is_request_or_reply;
787
36
  memcache_type_t    memcache_type;
788
36
  ReqRespDissector   reqresp_dissector  = NULL;
789
36
  proto_tree        *memcache_tree      = NULL;
790
36
  proto_item        *memcache_item      = NULL;
791
36
  uint8_t            opcode = 0xff; /* set to something that is not in the list. */
792
793
  /* Find a line end in the packet.
794
   * Note that "tvb_find_line_end_remaining()" will return a value that
795
   * is not longer than what's in the buffer, so the
796
   * "tvb_get_ptr ()" call won't throw an exception.
797
   */
798
36
  if (!tvb_find_line_end_remaining(tvb, offset, &first_linelen, &next_offset)) {
799
2
    return -1;
800
2
  }
801
802
34
  line = tvb_get_ptr (tvb, offset, first_linelen);
803
34
  lineend = line + first_linelen;
804
805
34
  memcache_type = MEMCACHE_UNKNOWN; /* packet type not known yet */
806
807
  /* Look at the first token of the first line to
808
   * determine if it is a request or a response?
809
   */
810
34
  is_request_or_reply =
811
34
    is_memcache_request_or_reply ((const char *)line,
812
34
                                  first_linelen, &opcode, &memcache_type,
813
34
                                  &expect_content_length, &reqresp_dissector);
814
34
  if (is_request_or_reply) {
815
816
    /* Yes, it is a request or a response.
817
     * Do header and body desegmentation if we've been told to.
818
     */
819
0
    if (!memcache_req_resp_hdrs_do_reassembly (tvb, offset, pinfo, memcache_desegment_headers,
820
0
                                               memcache_desegment_body, memcache_type,
821
0
                                               expect_content_length))
822
0
    {
823
      /* More data needed for desegmentation. */
824
0
      return -1;
825
0
    }
826
0
  }
827
828
  /* Columns and summary display. */
829
34
  col_set_str (pinfo->cinfo, COL_PROTOCOL, "MEMCACHE");
830
831
  /* If the packet is a memcache request or reply,
832
   * put the first line from the buffer into the summary
833
   * Otherwise, just call it a continuation.
834
   */
835
34
  if (is_request_or_reply) {
836
0
    col_add_fstr (pinfo->cinfo, COL_INFO, "%s ",
837
0
                 tvb_format_text(pinfo->pool, tvb, offset, first_linelen));
838
34
  } else {
839
34
    col_set_str (pinfo->cinfo, COL_INFO, "MEMCACHE Continuation");
840
34
  }
841
842
34
  orig_offset = offset;
843
844
34
  memcache_item = proto_tree_add_item (tree, proto_memcache, tvb, offset, -1, ENC_NA);
845
34
  memcache_tree = proto_item_add_subtree (memcache_item, ett_memcache);
846
847
  /* Process the packet data. The first line is expected to be a
848
   * header. If it's not a header then we don't dissect.
849
   * At this point, we already know if it is a request or a
850
   * response.
851
   */
852
34
  if (tvb_reported_length_remaining (tvb, offset) != 0) {
853
    /* Dissect a request or a response. */
854
34
    if (is_request_or_reply && reqresp_dissector) {
855
0
      toffset = reqresp_dissector (tvb, pinfo, memcache_tree, offset, line, lineend, opcode);
856
0
      if (toffset == -1) {
857
        /* Error in dissecting. */
858
0
        return -1;
859
0
      }
860
0
      offset = toffset;
861
0
    }
862
34
  }
863
864
  /*
865
   * If a 'bytes' value was supplied, the amount of data to be
866
   * processed as MEMCACHE payload is the minimum of the 'bytes'
867
   * value and the amount of data remaining in the frame.
868
   *
869
   */
870
34
  datalen = tvb_captured_length_remaining (tvb, offset);
871
34
  if (datalen > 0) {
872
    /*
873
     * We've processed "datalen" bytes worth of data
874
     * (which may be no data at all); advance the
875
     * offset past whatever data we've processed.
876
     */
877
34
    offset += datalen;
878
34
  }
879
880
34
  return offset - orig_offset;
881
34
}
882
883
/* Payload dissector
884
 * <data block>\r\n
885
 */
886
static int
887
content_data_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset,
888
  unsigned content_length, uint8_t opcode)
889
0
{
890
0
  unsigned      datalen;
891
0
  bool          short_pkt = false;
892
893
  /*
894
   * Expecting to read 'content_length' number of bytes from
895
   * the buffer. It is not necessary that we have all the
896
   * content_length bytes available to read.
897
   */
898
0
  if (tvb_reported_length_remaining (tvb, offset) != 0) {
899
    /* bytes actually remaining in this tvbuff. */
900
0
    datalen = tvb_captured_length_remaining (tvb, offset);
901
0
    if (content_length > 0) {
902
0
      if (datalen >= (content_length + 2)) { /* also consider \r\n*/
903
0
        datalen = content_length;
904
0
      } else {
905
0
        short_pkt = true;
906
0
      }
907
0
    }
908
909
    /* dissect the data block. */
910
0
    dissect_value (tvb, pinfo, tree, offset, datalen, opcode, true);
911
0
    if (datalen > 0) {
912
      /*
913
       * We've processed "datalen" bytes worth of data
914
       * (which may be no data at all); advance the
915
       * offset past whatever data we've processed.
916
       */
917
0
      if (!short_pkt) {
918
0
        offset += (datalen + 2); /* go past /r/n*/
919
0
      } else {
920
0
        offset += datalen; /* short packet; no /r/n*/
921
0
      }
922
0
    }
923
0
  }
924
925
0
  return offset;
926
0
}
927
928
/* Find the occurrences of a ':' in a stat response. */
929
static unsigned
930
find_stat_colon (const unsigned char *line, const unsigned char *lineend,
931
                 const unsigned char **first_colon, const unsigned char **last_colon)
932
0
{
933
0
  const unsigned char *linep, *temp;
934
0
  unsigned      occurrences = 0;
935
0
  unsigned char c;
936
937
0
  linep = line;
938
0
  while (linep < lineend) {
939
0
    temp = linep;
940
0
    c = *linep++;
941
942
0
    switch (c) {
943
0
    case ':':
944
0
      occurrences++;
945
0
      if (occurrences == 1) {
946
0
        *first_colon = temp;
947
0
      } else if (occurrences == 2) {
948
0
        *last_colon = temp;
949
0
      } else {
950
        /* anything other than 1 or 2;
951
         * return immediately
952
         */
953
0
        return occurrences;
954
0
      }
955
0
      break;
956
0
    default:
957
0
      break;
958
0
    }
959
0
  }
960
961
0
  return occurrences;
962
0
}
963
964
/* incr/decr response dissector */
965
static int
966
incr_dissector (tvbuff_t *tvb, proto_tree *tree, unsigned offset)
967
0
{
968
0
  unsigned       next_offset = 0;
969
0
  unsigned       linelen;
970
0
  const unsigned char  *line, *lineend;
971
972
0
  const unsigned char  *next_token;
973
0
  unsigned       tokenlen;
974
975
  /* expecting to read 'bytes' number of bytes from the buffer. */
976
0
  if (tvb_offset_exists (tvb, offset)) {
977
    /* Find the end of the line. */
978
0
    if (!tvb_find_line_end_remaining(tvb, offset, &linelen, &next_offset)) {
979
      /* header is out of the packet limits. */
980
0
      return -1;
981
0
    }
982
983
    /*
984
     * Get a buffer that refers to the line.
985
     * in other words, the unstructured portion
986
     * of memcache.
987
     */
988
0
    line = tvb_get_ptr (tvb, offset, linelen);
989
0
    lineend = line + linelen;
990
991
    /* 64 bit value */
992
0
    tokenlen = get_token_len (line, lineend, &next_token);
993
0
    if (tokenlen == 0) {
994
0
      return -1;
995
0
    }
996
997
0
    proto_tree_add_item (tree, hf_uint64_response, tvb, offset, tokenlen, ENC_BIG_ENDIAN);
998
999
    /* CRLF */
1000
0
    tokenlen = get_token_len (line, lineend, &next_token);
1001
0
    if (tokenlen == 0) {
1002
0
      return next_offset;
1003
0
    } else {
1004
0
      return -1; /* invalid token */
1005
0
    }
1006
0
  }
1007
1008
0
  return offset;
1009
0
}
1010
1011
/* stats response dissector */
1012
static int
1013
stat_dissector (tvbuff_t *tvb, proto_tree *tree, unsigned offset)
1014
0
{
1015
0
  unsigned      occurrences = 0;
1016
0
  const unsigned char *first_colon = NULL, *last_colon = NULL, *endp;
1017
0
  unsigned      tokenlen, linelen;
1018
0
  unsigned      next_offset = 0;
1019
0
  const unsigned char *next_token;
1020
0
  const unsigned char *line, *lineend;
1021
0
  uint32_t      slabclass;
1022
1023
0
  while (tvb_offset_exists (tvb, offset)) {
1024
    /* Find the end of the line. */
1025
0
    if (!tvb_find_line_end_remaining(tvb, offset, &linelen, &next_offset)) {
1026
0
      return -1;
1027
0
    }
1028
1029
    /*
1030
     * Get a buffer that refers to the line.
1031
     */
1032
0
    line = tvb_get_ptr (tvb, offset, linelen);
1033
0
    lineend = line + linelen;
1034
1035
0
    tokenlen = get_token_len (line, lineend, &next_token);
1036
0
    if ((tokenlen == 4) && strncmp ((char*)line, "STAT", tokenlen) == 0) {
1037
0
      proto_tree_add_item (tree, hf_command, tvb, offset, tokenlen, ENC_ASCII);
1038
0
      offset += (int) (next_token - line);
1039
0
      line = next_token;
1040
0
      occurrences = find_stat_colon (line, lineend, &first_colon, &last_colon);
1041
0
    } else if ((tokenlen == 3) && strncmp ((char*)line, "END", tokenlen) == 0) {
1042
      /* done. reached an end of response. */
1043
0
      offset += (int) (next_token - line);
1044
0
      return offset;
1045
0
    } else {
1046
      /* invalid token */
1047
0
      return -1;
1048
0
    }
1049
1050
0
    switch (occurrences) {
1051
0
    case 2: /* stats items: 2 colons */
1052
      /* subcommand 'items' */
1053
0
      tokenlen = (int) (first_colon - line);
1054
0
      proto_tree_add_item (tree, hf_subcommand, tvb, offset, tokenlen, ENC_ASCII);
1055
0
      offset += tokenlen + 1;
1056
1057
      /* slabclass */
1058
0
      tokenlen = (int) (last_colon - first_colon - 1);
1059
0
      if (tokenlen > 10 || tokenlen <= 0) {
1060
0
        return -1;
1061
0
      }
1062
0
      ws_buftou32(first_colon + 1, tokenlen, &endp, &slabclass);
1063
0
      proto_tree_add_uint (tree, hf_slabclass, tvb, offset, tokenlen, slabclass);
1064
0
      offset += tokenlen + 1;
1065
0
      line = last_colon + 1;
1066
0
      break;
1067
1068
0
    case 1: /* stats slabs: 1 colon */
1069
0
      tokenlen = (int) (first_colon - line);
1070
0
      if (tokenlen > 10 || tokenlen <= 0) {
1071
0
        return -1;
1072
0
      }
1073
1074
0
      ws_buftou32(line, tokenlen, &endp, &slabclass);
1075
0
      proto_tree_add_uint (tree, hf_slabclass, tvb, offset, tokenlen, slabclass);
1076
1077
0
      offset += (int) (tokenlen + 1);
1078
0
      line = first_colon + 1;
1079
0
      break;
1080
1081
0
    case 0: /* stats: 0 colons */
1082
0
      break;
1083
1084
0
    default:
1085
      /* invalid token. */
1086
0
      return -1;
1087
0
    }
1088
1089
    /* <hf_name> <hf_name_value>\r\n */
1090
0
    tokenlen = get_token_len (line, lineend, &next_token);
1091
0
    if (tokenlen == 0) {
1092
0
      return -1; /* invalid token */
1093
0
    }
1094
1095
0
    proto_tree_add_item (tree, hf_name, tvb, offset, tokenlen, ENC_ASCII);
1096
0
    offset += (int) (next_token - line);
1097
0
    line = next_token;
1098
1099
    /* value */
1100
0
    tokenlen = get_token_len (line, lineend, &next_token);
1101
0
    if (tokenlen == 0) {
1102
0
      return -1; /* invalid token */
1103
0
    }
1104
0
    proto_tree_add_item (tree, hf_name_value, tvb, offset, tokenlen, ENC_ASCII);
1105
1106
0
    offset = next_offset;
1107
0
  }
1108
1109
0
  return offset;
1110
0
}
1111
1112
/* get/gets response dissector */
1113
static int
1114
get_response_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset)
1115
0
{
1116
0
  unsigned       next_offset;
1117
0
  unsigned       linelen;
1118
0
  const unsigned char  *line, *lineend;
1119
0
  const unsigned char  *next_token, *endp;
1120
0
  int            tokenlen;
1121
0
  uint16_t       flags;
1122
0
  uint32_t       bytes;
1123
0
  uint64_t       cas;
1124
0
  uint8_t        opcode = 0xff;
1125
1126
  /* expecting to read 'bytes' number of bytes from the buffer. */
1127
0
  while (tvb_offset_exists (tvb, offset)) {
1128
    /* Find the end of the line. */
1129
0
    if (!tvb_find_line_end_remaining(tvb, offset, &linelen, &next_offset)) {
1130
      /* header is out of the packet limits. */
1131
0
      return -1;
1132
0
    }
1133
1134
    /*
1135
     * Get a buffer that refers to the line.
1136
     * in other words, the unstructured portion
1137
     * of memcache.
1138
     */
1139
0
    line = tvb_get_ptr (tvb, offset, linelen);
1140
0
    lineend = line + linelen;
1141
1142
    /* VALUE token  */
1143
0
    tokenlen = get_token_len (line, lineend, &next_token);
1144
0
    if (tokenlen == 0) {
1145
      /* error */
1146
0
      return -1;
1147
0
    }
1148
1149
0
    if ((tokenlen == 5) && strncmp ((char*)line, "VALUE", tokenlen) == 0) {
1150
      /* proceed */
1151
0
    } else if ((tokenlen == 3) && strncmp ((char*)line, "END", tokenlen) == 0) {
1152
      /* done. reached an end of response. */
1153
0
      offset += (int) (next_token - line);
1154
0
      return offset;
1155
0
    } else {
1156
      /* invalid token */
1157
0
      return -1;
1158
0
    }
1159
1160
0
    offset += (int) (next_token - line);
1161
0
    line = next_token;
1162
1163
    /* key */
1164
0
    tokenlen = get_token_len (line, lineend, &next_token);
1165
0
    if (tokenlen == 0) {
1166
0
      return -1;
1167
0
    }
1168
0
    dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, true);
1169
0
    offset += (int) (next_token - line);
1170
0
    line = next_token;
1171
1172
    /* flags */
1173
0
    tokenlen = get_token_len (line, lineend, &next_token);
1174
0
    if (tokenlen == 0 || tokenlen > 5) {
1175
0
      return -1;
1176
0
    }
1177
1178
0
    ws_buftou16(line, tokenlen, &endp, &flags);
1179
0
    proto_tree_add_uint (tree, hf_flags, tvb, offset, tokenlen, flags);
1180
1181
0
    offset += (int) (next_token - line);
1182
0
    line = next_token;
1183
1184
    /* bytes */
1185
0
    tokenlen = get_token_len (line, lineend, &next_token);
1186
0
    if (tokenlen == 0 || tokenlen > 10) {
1187
0
      return -1;
1188
0
    }
1189
1190
0
    ws_buftou32(line, tokenlen, &endp, &bytes);
1191
0
    proto_tree_add_uint (tree, hf_value_length, tvb, offset, tokenlen, bytes);
1192
1193
0
    offset += (int) (next_token - line);
1194
0
    line = next_token;
1195
1196
    /* check if cas id is present */
1197
0
    tokenlen = get_token_len (line, lineend, &next_token);
1198
0
    if (tokenlen > 20) {
1199
0
      return -1;
1200
0
    }
1201
1202
0
    if (tokenlen != 0) {  /* reached the end of line; CRLF */
1203
1204
0
      ws_buftou64(line, tokenlen, &endp, &cas);
1205
0
      proto_tree_add_uint64 (tree, hf_cas, tvb, offset, tokenlen, cas);
1206
1207
      /* CRLF */
1208
0
      tokenlen = get_token_len (line, lineend, &next_token);
1209
0
      if (tokenlen != 0) {
1210
0
        return -1; /* invalid token */
1211
0
      }
1212
0
    }
1213
1214
0
    offset = next_offset;
1215
    /* <datablock>\r\n */
1216
0
    offset = content_data_dissector (tvb, pinfo, tree, offset, bytes, opcode);
1217
0
  }
1218
1219
0
  return offset;
1220
0
}
1221
1222
/* Basic memcache response dissector. */
1223
static int
1224
memcache_response_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset,
1225
                             const unsigned char *line, const unsigned char *lineend, uint8_t opcode)
1226
0
{
1227
0
  const unsigned char *next_token;
1228
0
  unsigned     tokenlen;
1229
1230
0
  switch (opcode) {
1231
1232
0
  case OP_GET:
1233
0
  case OP_GETS:
1234
0
    return get_response_dissector (tvb, pinfo, tree, offset);
1235
1236
0
  case OP_VERSION:
1237
    /* response code.  */
1238
0
    tokenlen = get_token_len (line, lineend, &next_token);
1239
0
    if (tokenlen == 0) {
1240
0
      return -1;
1241
0
    }
1242
0
    if ((tokenlen == 7) && strncmp ((char*)line, "VERSION", tokenlen) == 0) {
1243
0
      offset += (int) (next_token - line);
1244
0
      line = next_token;
1245
0
    } else {
1246
0
      return -1;
1247
0
    }
1248
1249
    /* version string */
1250
0
    tokenlen = get_token_len (line, lineend, &next_token);
1251
0
    if (tokenlen == 0) {
1252
      /* expecting version string. */
1253
0
      return -1;
1254
0
    }
1255
1256
0
    proto_tree_add_item (tree, hf_version, tvb, offset, tokenlen, ENC_ASCII);
1257
0
    offset += (int) (next_token - line);
1258
0
    line = next_token;
1259
1260
    /* CRLF */
1261
0
    tokenlen = get_token_len (line, lineend, &next_token);
1262
0
    if (tokenlen != 0) {
1263
      /* invalid token */
1264
0
      return -1;
1265
0
    }
1266
1267
0
    return offset;
1268
1269
0
  case OP_STAT:
1270
0
    return stat_dissector (tvb, tree, offset);
1271
1272
0
  default:
1273
0
    break;
1274
0
  }
1275
1276
  /* response code.  */
1277
0
  tokenlen = get_token_len (line, lineend, &next_token);
1278
0
  if (tokenlen == 0) {
1279
0
    return -1;
1280
0
  }
1281
1282
  /* all the following mark an end of a response.
1283
   * should take care of set, add, cas, append, replace
1284
   * prepend, flush_all, verbosity, delete and to an extent
1285
   * incr, decr and stat commands.
1286
   */
1287
0
  if ((tokenlen == 6 && strncmp ((char*)line, "STORED", tokenlen) == 0) ||
1288
0
      (tokenlen == 10 && strncmp((char*)line, "NOT_STORED", tokenlen) == 0) ||
1289
0
      (tokenlen == 6 && strncmp ((char*)line, "EXISTS", tokenlen) == 0) ||
1290
0
      (tokenlen == 9 && strncmp ((char*)line, "NOT_FOUND", tokenlen) == 0) ||
1291
0
      (tokenlen == 7 && strncmp ((char*)line, "DELETED", tokenlen) == 0) ||
1292
0
      (tokenlen == 2 && strncmp ((char*)line, "OK", tokenlen) == 0) ||
1293
0
      (tokenlen == 3 && strncmp ((char*)line, "END", tokenlen) == 0))
1294
0
  {
1295
0
    proto_tree_add_item (tree, hf_response, tvb, offset, tokenlen, ENC_ASCII);
1296
0
    offset += (int) (next_token - line);
1297
0
    return offset;
1298
0
  }
1299
1300
  /* if we have reached this point:
1301
   * it is either an incr/decr response of the format
1302
   *  <value>\r\n.
1303
   *  or
1304
   *  "stats sizes" response of the format:
1305
   *  <size> <count> \r\n
1306
   */
1307
0
  if (opcode == OP_INCREMENT) {
1308
0
    return incr_dissector (tvb, tree, offset);
1309
0
  }
1310
1311
0
  return offset;
1312
0
}
1313
1314
/* Basic memcache request dissector. */
1315
static int
1316
memcache_request_dissector (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, unsigned offset,
1317
                            const unsigned char *line, const unsigned char *lineend, uint8_t opcode)
1318
0
{
1319
0
  const unsigned char *next_token, *endp;
1320
0
  unsigned      tokenlen;
1321
1322
0
  uint16_t      flags;
1323
0
  uint32_t      expiration;
1324
0
  uint32_t      bytes;
1325
0
  uint64_t      cas;
1326
1327
  /* command. */
1328
0
  tokenlen = get_token_len (line, lineend, &next_token);
1329
0
  if (tokenlen == 0) {
1330
0
    return -1;
1331
0
  }
1332
0
  proto_tree_add_item (tree, hf_command, tvb, offset, tokenlen, ENC_ASCII);
1333
0
  offset += (int) (next_token - line);
1334
0
  line = next_token;
1335
1336
0
  switch (opcode) {
1337
1338
0
  case OP_SET:
1339
0
  case OP_ADD:
1340
0
  case OP_REPLACE:
1341
0
  case OP_APPEND:
1342
0
  case OP_PREPEND:
1343
0
  case OP_CAS:
1344
1345
    /* key */
1346
0
    tokenlen = get_token_len (line, lineend, &next_token);
1347
0
    if (tokenlen == 0) {
1348
0
      return -1;
1349
0
    }
1350
1351
0
    dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, true);
1352
0
    offset += (int) (next_token - line);
1353
0
    line = next_token;
1354
1355
    /* flags */
1356
0
    tokenlen = get_token_len (line, lineend, &next_token);
1357
0
    if (tokenlen == 0 || tokenlen > 5) {
1358
0
      return -1;
1359
0
    }
1360
1361
0
    ws_buftou16(line, tokenlen, &endp, &flags);
1362
0
    proto_tree_add_uint (tree, hf_flags, tvb, offset, tokenlen, flags);
1363
1364
0
    offset += (int) (next_token - line);
1365
0
    line = next_token;
1366
1367
    /* expiration */
1368
0
    tokenlen = get_token_len (line, lineend, &next_token);
1369
0
    if (tokenlen == 0 || tokenlen > 10) {
1370
0
      return -1;
1371
0
    }
1372
1373
0
    ws_buftou32(line, tokenlen, &endp, &expiration);
1374
0
    proto_tree_add_uint (tree, hf_expiration, tvb, offset, tokenlen, expiration);
1375
1376
0
    offset += (int) (next_token - line);
1377
0
    line = next_token;
1378
1379
    /* bytes */
1380
0
    tokenlen = get_token_len (line, lineend, &next_token);
1381
0
    if (tokenlen == 0 || tokenlen > 10) {
1382
0
      return -1;
1383
0
    }
1384
1385
0
    ws_buftou32(line, tokenlen, &endp, &bytes);
1386
0
    proto_tree_add_uint (tree, hf_value_length, tvb, offset, tokenlen, bytes);
1387
1388
0
    offset += (int) (next_token - line);
1389
0
    line = next_token;
1390
1391
    /* cas id. */
1392
0
    if (opcode == OP_CAS) {
1393
0
      tokenlen = get_token_len (line, lineend, &next_token);
1394
0
      if (tokenlen == 0 || tokenlen > 20) {
1395
0
        return -1;
1396
0
      }
1397
1398
0
      ws_buftou64(line, tokenlen, &endp, &cas);
1399
0
      proto_tree_add_uint64 (tree, hf_cas, tvb, offset, tokenlen, cas);
1400
1401
0
      offset += (int) (next_token - line);
1402
0
      line = next_token;
1403
0
    }
1404
1405
    /* check if the following bit is "noreply" or
1406
     * the actual data block.
1407
     */
1408
0
    tokenlen = get_token_len (line, lineend, &next_token);
1409
0
    if (tokenlen != 0) {
1410
0
      if (tokenlen == 7 && strncmp ((char*)line, "noreply", 7) == 0) {
1411
0
        proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, ENC_ASCII);
1412
0
      }
1413
0
      offset += (int) (next_token - line);
1414
0
    }
1415
1416
0
    offset += 2 ; /* go past /r/n*/
1417
    /* <datablock>\r\n */
1418
0
    offset = content_data_dissector (tvb, pinfo, tree, offset, bytes, opcode);
1419
0
    break;
1420
1421
0
  case OP_INCREMENT:
1422
0
  case OP_DECREMENT:
1423
    /* key */
1424
0
    tokenlen = get_token_len (line, lineend, &next_token);
1425
0
    if (tokenlen == 0) {
1426
0
      return -1;
1427
0
    }
1428
0
    dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, true);
1429
0
    offset += (int) (next_token - line);
1430
0
    line = next_token;
1431
1432
    /* value */
1433
0
    tokenlen = get_token_len (line, lineend, &next_token);
1434
0
    if (tokenlen == 0) {
1435
0
      return -1;
1436
0
    }
1437
0
    proto_tree_add_item (tree, hf_value, tvb, offset, tokenlen, ENC_ASCII);
1438
0
    offset += (int) (next_token - line);
1439
0
    line = next_token;
1440
1441
    /* check for "noreply" */
1442
0
    tokenlen = get_token_len (line, lineend, &next_token);
1443
0
    if (tokenlen == 0) {
1444
0
      return offset; /* reached CRLF */
1445
0
    }
1446
0
    if (tokenlen == 7 && strncmp ((char*)line, "noreply", 7) == 0) {
1447
0
      proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, ENC_ASCII);
1448
0
      offset += (int) (next_token - line);
1449
0
      line = next_token;
1450
0
    } else {
1451
0
      return -1; /* should have been noreply or CRLF. */
1452
0
    }
1453
1454
    /* CRLF */
1455
0
    tokenlen = get_token_len (line, lineend, &next_token);
1456
0
    if (tokenlen == 0) {
1457
0
      return offset; /* CRLF */
1458
0
    } else {
1459
      /*something's wrong; invalid command maybe. */
1460
0
      return -1;
1461
0
    }
1462
0
    break;
1463
1464
0
  case OP_DELETE:
1465
    /* key */
1466
0
    tokenlen = get_token_len (line, lineend, &next_token);
1467
0
    if (tokenlen == 0) {
1468
0
      return -1;
1469
0
    }
1470
    /* dissect key. */
1471
0
    dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, true);
1472
0
    offset += (int) (next_token - line);
1473
0
    line = next_token;
1474
1475
    /* check if it's expiration or noreply */
1476
0
    tokenlen = get_token_len (line, lineend, &next_token);
1477
0
    if (tokenlen == 0) {
1478
0
      return offset; /* neither expiration nor noreply; CRLF */
1479
0
    }
1480
0
    if (tokenlen <= 10) {
1481
0
      if (tokenlen == 7 && strncmp ((char*)line, "noreply", 7) == 0) {
1482
        /* noreply */
1483
0
        proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, ENC_ASCII);
1484
0
      } else {
1485
        /* expiration */
1486
0
        ws_buftou32(line, tokenlen, &endp, &expiration);
1487
0
        proto_tree_add_uint (tree, hf_expiration, tvb, offset, tokenlen, expiration);
1488
0
      }
1489
0
      offset += (int) (next_token - line);
1490
0
      line = next_token;
1491
0
    } else {
1492
0
      return -1;
1493
0
    }
1494
1495
    /* CRLF */
1496
0
    tokenlen = get_token_len (line, lineend, &next_token);
1497
0
    if (tokenlen == 0) {
1498
0
      return offset;
1499
0
    } else {
1500
      /*something's wrong; invalid command maybe. */
1501
0
      return -1;
1502
0
    }
1503
0
    break;
1504
1505
0
  case OP_GET:
1506
0
  case OP_GETS:
1507
    /* could be followed by any number of keys, add
1508
     * them one by one. tokenlen cannot be 0 to begin
1509
     * with.
1510
     */
1511
0
    while (tokenlen != 0) {
1512
0
      tokenlen = get_token_len (line, lineend, &next_token);
1513
0
      if (tokenlen == 0) {
1514
0
        return offset; /* CRLF */
1515
0
      }
1516
0
      dissect_key (tvb, pinfo, tree, offset, tokenlen, opcode, true);
1517
0
      offset += (int) (next_token - line);
1518
0
      line = next_token;
1519
0
    }
1520
0
    break;
1521
1522
0
  case OP_STAT:
1523
0
    tokenlen = get_token_len (line, lineend, &next_token);
1524
0
    if (tokenlen == 0) { /* just the 'stats' command;*/
1525
0
      return offset;
1526
0
    } else { /* there is a sub command; record it*/
1527
0
      proto_tree_add_item (tree, hf_subcommand, tvb, offset, tokenlen, ENC_ASCII);
1528
0
      offset += (int) (next_token - line);
1529
0
      line = next_token;
1530
0
    }
1531
1532
    /* CRLF */
1533
0
    tokenlen = get_token_len (line, lineend, &next_token);
1534
0
    if (tokenlen == 0) {
1535
0
      return offset;
1536
0
    } else {
1537
      /* something's wrong; invalid command maybe. */
1538
0
      return -1;
1539
0
    }
1540
0
    break;
1541
1542
0
  case OP_FLUSH:
1543
    /* check if it's expiration or noreply */
1544
0
    tokenlen = get_token_len (line, lineend, &next_token);
1545
0
    if (tokenlen == 0) {
1546
0
      return offset; /* neither expiration nor noreply; CRLF */
1547
0
    }
1548
0
    if (tokenlen <= 10) {
1549
0
      if (tokenlen == 7 && strncmp ((char*)line, "noreply", 7) == 0) {
1550
        /* noreply */
1551
0
        proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, ENC_ASCII);
1552
0
      } else {
1553
        /* expiration */
1554
0
        ws_buftou32(line, tokenlen, &endp, &expiration);
1555
0
        proto_tree_add_uint (tree, hf_expiration, tvb, offset, tokenlen, expiration);
1556
0
      }
1557
0
      offset += (int) (next_token - line);
1558
0
      line = next_token;
1559
0
    } else {
1560
0
      return -1;
1561
0
    }
1562
1563
    /* maybe noreply now? */
1564
0
    tokenlen = get_token_len (line, lineend, &next_token);
1565
0
    if (tokenlen == 0) {
1566
0
      return offset;
1567
0
    }
1568
0
    if (tokenlen == 7 && strncmp ((char*)line, "noreply", 7) == 0) {
1569
      /* noreply */
1570
0
      proto_tree_add_item (tree, hf_noreply, tvb, offset, tokenlen, ENC_ASCII);
1571
0
      offset += (int) (next_token - line);
1572
0
    } else {
1573
0
      return -1; /* expecting CRLF and if not noreply*/
1574
0
    }
1575
0
    break;
1576
1577
0
  case OP_VERBOSE:
1578
    /* not implemented for now.*/
1579
0
    break;
1580
1581
0
  case OP_VERSION:
1582
0
  case OP_QUIT:
1583
    /* CRLF */
1584
0
    tokenlen = get_token_len (line, lineend, &next_token);
1585
0
    if (tokenlen == 0) {
1586
0
      return offset;
1587
0
    } else {
1588
      /*something's wrong; invalid command maybe. */
1589
0
      return -1;
1590
0
    }
1591
1592
0
  default:
1593
    /* invalid command maybe; break out. */
1594
0
    break;
1595
0
  }
1596
1597
0
  return offset;
1598
0
}
1599
1600
/*
1601
 * any message that is not starting with the following keywords
1602
 * is a response.
1603
 */
1604
static int
1605
is_memcache_request_or_reply (const char *data, int linelen, uint8_t *opcode,
1606
                             memcache_type_t *type, bool *expect_content_length,
1607
                             ReqRespDissector *reqresp_dissector)
1608
34
{
1609
34
  const unsigned char *ptr = (const unsigned char *)data;
1610
34
  bool          is_request_or_response = false;
1611
34
  int           indx = 0;
1612
1613
  /* look for a space */
1614
705
  while (indx < linelen) {
1615
682
    if (*ptr == ' ')
1616
11
      break;
1617
1618
671
    ptr++;
1619
671
    indx++;
1620
671
  }
1621
1622
  /* is it a response? */
1623
34
  switch (indx) {
1624
4
  case 2:
1625
4
    if (strncmp (data, "OK", indx) == 0) {
1626
0
      *type = MEMCACHE_RESPONSE;
1627
0
      is_request_or_response = true;
1628
0
    }
1629
4
    break;
1630
1631
2
  case 3:
1632
2
    if (strncmp (data, "END", indx) == 0) {
1633
0
      *type = MEMCACHE_RESPONSE;
1634
0
      is_request_or_response = true;
1635
0
    }
1636
2
    break;
1637
1638
2
  case 4:
1639
2
    if (strncmp (data, "STAT", indx) == 0) {
1640
0
      *opcode = OP_STAT;
1641
0
      *type = MEMCACHE_RESPONSE;
1642
0
      is_request_or_response = true;
1643
0
    }
1644
2
    break;
1645
1646
4
  case 5:
1647
4
    if (strncmp (data, "VALUE", indx) == 0) {
1648
0
      *opcode = OP_GET;
1649
0
      *type = MEMCACHE_RESPONSE;
1650
0
      *expect_content_length = true;
1651
0
      is_request_or_response = true;
1652
0
    }
1653
4
    break;
1654
1655
4
  case 6:
1656
4
    if (strncmp (data, "EXISTS", indx) == 0 ||
1657
4
        strncmp (data, "STORED", indx) == 0) {
1658
0
      *type = MEMCACHE_RESPONSE;
1659
0
      is_request_or_response = true;
1660
0
    }
1661
4
    break;
1662
1663
4
  case 7:
1664
4
    if (strncmp (data, "VERSION", indx) == 0) {
1665
0
      *opcode = OP_VERSION;
1666
0
      *type = MEMCACHE_RESPONSE;
1667
0
      is_request_or_response = true;
1668
4
    } else if (strncmp (data, "DELETED", indx) == 0) {
1669
0
      *opcode = OP_DELETE;
1670
0
      *type = MEMCACHE_RESPONSE;
1671
0
      is_request_or_response = true;
1672
0
    }
1673
4
    break;
1674
1675
2
  case 9:
1676
2
    if (strncmp (data, "NOT_FOUND", indx) == 0) {
1677
0
      *type = MEMCACHE_RESPONSE;
1678
0
      is_request_or_response = true;
1679
0
    }
1680
2
    break;
1681
1682
2
  case 10:
1683
2
    if (strncmp (data, "NOT_STORED", indx) == 0) {
1684
0
      *type = MEMCACHE_RESPONSE;
1685
0
      is_request_or_response = true;
1686
0
    }
1687
2
    break;
1688
1689
10
  default:
1690
10
    break; /* is it a request? */
1691
34
  }
1692
1693
34
  if (is_request_or_response && reqresp_dissector) {
1694
0
    *reqresp_dissector = memcache_response_dissector;
1695
0
    return is_request_or_response;
1696
0
  }
1697
1698
  /* is it a request?  */
1699
34
  switch (indx) {
1700
2
  case 3:
1701
2
    if (strncmp (data, "get", indx) == 0) {
1702
0
      *opcode = OP_GET;
1703
0
      *type = MEMCACHE_REQUEST;
1704
0
      is_request_or_response = true;
1705
2
    } else if (strncmp (data, "set", indx) == 0) {
1706
0
      *opcode = OP_SET;
1707
0
      *type = MEMCACHE_REQUEST;
1708
0
      *expect_content_length = true;
1709
0
      is_request_or_response = true;
1710
2
    } else if (strncmp (data, "add", indx) == 0) {
1711
0
      *opcode = OP_ADD;
1712
0
      *type = MEMCACHE_REQUEST;
1713
0
      *expect_content_length = true;
1714
0
      is_request_or_response = true;
1715
2
    } else if (strncmp (data, "cas", indx) == 0) {
1716
0
      *opcode = OP_CAS;
1717
0
      *type = MEMCACHE_REQUEST;
1718
0
      *expect_content_length = true;
1719
0
      is_request_or_response = true;
1720
0
    }
1721
2
    break;
1722
1723
2
  case 4:
1724
2
    if (strncmp (data, "gets", indx) == 0) {
1725
0
      *opcode = OP_GETS;
1726
0
      *type = MEMCACHE_REQUEST;
1727
0
      is_request_or_response = true;
1728
2
    } else if (strncmp (data, "incr", indx) == 0) {
1729
0
      *opcode = OP_INCREMENT;
1730
0
      *type = MEMCACHE_REQUEST;
1731
0
      is_request_or_response = true;
1732
2
    } else if (strncmp (data, "decr", indx) == 0) {
1733
0
      *opcode = OP_DECREMENT;
1734
0
      *type = MEMCACHE_REQUEST;
1735
0
      is_request_or_response = true;
1736
2
    } else if (strncmp (data, "quit", indx) == 0) {
1737
0
      *opcode = OP_QUIT;
1738
0
      *type = MEMCACHE_REQUEST;
1739
0
      is_request_or_response = true;
1740
0
    }
1741
2
    break;
1742
1743
4
  case 5:
1744
4
    if (strncmp (data, "stats", indx) == 0) {
1745
0
      *opcode = OP_STAT;
1746
0
      *type = MEMCACHE_REQUEST;
1747
0
      is_request_or_response = true;
1748
0
    }
1749
4
    break;
1750
1751
4
  case 6:
1752
4
    if (strncmp (data, "append", indx) == 0) {
1753
0
      *opcode = OP_APPEND;
1754
0
      *type = MEMCACHE_REQUEST;
1755
0
      *expect_content_length = true;
1756
0
      is_request_or_response = true;
1757
4
    } else if (strncmp (data, "delete", indx) == 0) {
1758
0
      *opcode = OP_DELETE;
1759
0
      *type = MEMCACHE_REQUEST;
1760
0
      is_request_or_response = true;
1761
0
    }
1762
4
    break;
1763
1764
4
  case 7:
1765
4
    if (strncmp (data, "replace", indx) == 0) {
1766
0
      *opcode = OP_REPLACE;
1767
0
      *type = MEMCACHE_REQUEST;
1768
0
      *expect_content_length = true;
1769
0
      is_request_or_response = true;
1770
4
    } else if (strncmp (data, "prepend", indx) == 0) {
1771
0
      *opcode = OP_PREPEND;
1772
0
      *type = MEMCACHE_REQUEST;
1773
0
      *expect_content_length = true;
1774
0
      is_request_or_response = true;
1775
4
    } else if (strncmp (data, "version", indx) == 0) {
1776
0
      *opcode = OP_VERSION;
1777
0
      *type = MEMCACHE_REQUEST;
1778
0
      is_request_or_response = true;
1779
0
    }
1780
4
    break;
1781
1782
2
  case 9:
1783
2
    if (strncmp (data, "flush_all", indx) == 0) {
1784
0
      *opcode = OP_FLUSH;
1785
0
      *type = MEMCACHE_REQUEST;
1786
0
      is_request_or_response = true;
1787
0
    }
1788
2
    break;
1789
1790
16
  default:
1791
16
    break; /* check if it is an 'incr' or 'stats sizes' response. */
1792
34
  }
1793
1794
34
  if (is_request_or_response && reqresp_dissector) {
1795
0
    *reqresp_dissector = memcache_request_dissector;
1796
0
    return is_request_or_response;
1797
0
  }
1798
1799
  /* XXX:
1800
   * Recognize 'incr', 'decr' and 'stats sizes' responses.
1801
   * I don't have a solution for this yet.
1802
   */
1803
34
  return is_request_or_response;
1804
34
}
1805
1806
/* dissect memcache textual protocol PDUs. */
1807
static void
1808
dissect_memcache_text (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1809
15
{
1810
15
  int   offset = 0;
1811
15
  int   len;
1812
1813
29
  while (tvb_reported_length_remaining (tvb, offset) != 0) {
1814
1815
    /* dissect the memcache packet. */
1816
15
    len = dissect_memcache_message (tvb, offset, pinfo, tree);
1817
15
    if (len == -1)
1818
1
      break;
1819
14
    offset += len;
1820
1821
    /*
1822
     * OK, we've set the Protocol and Info columns for the
1823
     * first MEMCACHE message; set a fence so that subsequent
1824
     * MEMCACHE messages don't overwrite the Info column.
1825
     */
1826
14
    col_set_fence (pinfo->cinfo, COL_INFO);
1827
14
  }
1828
15
}
1829
1830
/* Dissect tcp packets based on the type of protocol (text/binary) */
1831
static int
1832
dissect_memcache_tcp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
1833
62
{
1834
62
  uint8_t     magic;
1835
1836
62
  magic = tvb_get_uint8 (tvb, 0);
1837
1838
62
  if (try_val_to_str (magic, magic_vals) != NULL) {
1839
47
    tcp_dissect_pdus (tvb, pinfo, tree, memcache_desegment_body, 12,
1840
47
                      get_memcache_pdu_len, dissect_memcache, data);
1841
47
  } else {
1842
15
    dissect_memcache_text (tvb, pinfo, tree);
1843
15
  }
1844
1845
62
  return tvb_captured_length(tvb);
1846
62
}
1847
1848
/* Dissect udp packets based on the type of protocol (text/binary) */
1849
static int
1850
dissect_memcache_udp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
1851
37
{
1852
37
  uint8_t     magic;
1853
1854
37
  magic = tvb_get_uint8 (tvb, 0);
1855
1856
37
  if (try_val_to_str (magic, magic_vals) != NULL) {
1857
16
    dissect_memcache (tvb, pinfo, tree, data);
1858
21
  } else {
1859
21
    dissect_memcache_message (tvb, 0, pinfo, tree);
1860
21
  }
1861
1862
37
  return tvb_captured_length(tvb);
1863
37
}
1864
1865
/* Registration functions; register memcache protocol,
1866
 * its configuration options and also register the tcp and udp
1867
 * dissectors.
1868
 */
1869
void
1870
proto_register_memcache (void)
1871
16
{
1872
16
  static hf_register_info hf[] = {
1873
16
    { &hf_magic,
1874
16
      { "Magic", "memcache.magic",
1875
16
        FT_UINT8, BASE_DEC, VALS (magic_vals), 0x0,
1876
16
        "Magic number", HFILL } },
1877
1878
16
    { &hf_opcode,
1879
16
      { "Opcode", "memcache.opcode",
1880
16
        FT_UINT8, BASE_DEC, VALS (opcode_vals), 0x0,
1881
16
        "Command code", HFILL } },
1882
1883
16
    { &hf_extras_length,
1884
16
      { "Extras length", "memcache.extras.length",
1885
16
        FT_UINT8, BASE_DEC, NULL, 0x0,
1886
16
        "Length in bytes of the command extras", HFILL } },
1887
1888
16
    { &hf_key_length,
1889
16
      { "Key Length", "memcache.key.length",
1890
16
        FT_UINT16, BASE_DEC, NULL, 0x0,
1891
16
        "Length in bytes of the text key that follows the command extras", HFILL } },
1892
1893
16
    { &hf_value_length,
1894
16
      { "Value length", "memcache.value.length",
1895
16
        FT_UINT32, BASE_DEC, NULL, 0x0,
1896
16
        "Length in bytes of the value that follows the key", HFILL } },
1897
1898
16
    { &hf_data_type,
1899
16
      { "Data type", "memcache.data_type",
1900
16
        FT_UINT8, BASE_DEC, VALS (data_type_vals), 0x0,
1901
16
        NULL, HFILL } },
1902
1903
16
    { &hf_reserved,
1904
16
      { "Reserved", "memcache.reserved",
1905
16
        FT_UINT16, BASE_DEC, NULL, 0x0,
1906
16
        "Reserved for future use", HFILL } },
1907
1908
16
    { &hf_status,
1909
16
      { "Status", "memcache.status",
1910
16
        FT_UINT16, BASE_DEC, VALS (status_vals), 0x0,
1911
16
        "Status of the response", HFILL } },
1912
1913
16
    { &hf_total_body_length,
1914
16
      { "Total body length", "memcache.total_body_length",
1915
16
        FT_UINT32, BASE_DEC, NULL, 0x0,
1916
16
        "Length in bytes of extra + key + value", HFILL } },
1917
1918
16
    { &hf_opaque,
1919
16
      { "Opaque", "memcache.opaque",
1920
16
        FT_UINT32, BASE_DEC, NULL, 0x0,
1921
16
        NULL, HFILL } },
1922
1923
16
    { &hf_cas,
1924
16
      { "CAS", "memcache.cas",
1925
16
        FT_UINT64, BASE_DEC, NULL, 0x0,
1926
16
        "Data version check", HFILL } },
1927
1928
16
    { &hf_extras,
1929
16
      { "Extras", "memcache.extras",
1930
16
        FT_NONE, BASE_NONE, NULL, 0x0,
1931
16
        NULL, HFILL } },
1932
1933
16
    { &hf_extras_flags,
1934
16
      { "Flags", "memcache.extras.flags",
1935
16
        FT_UINT32, BASE_HEX, NULL, 0x0,
1936
16
        NULL, HFILL } },
1937
1938
16
    { &hf_extras_expiration,
1939
16
      { "Expiration", "memcache.extras.expiration",
1940
16
        FT_UINT32, BASE_DEC, NULL, 0x0,
1941
16
        NULL, HFILL } },
1942
1943
16
    { &hf_extras_delta,
1944
16
      { "Amount to add", "memcache.extras.delta",
1945
16
        FT_UINT64, BASE_DEC, NULL, 0x0,
1946
16
        NULL, HFILL } },
1947
1948
16
    { &hf_extras_initial,
1949
16
      { "Initial value", "memcache.extras.initial",
1950
16
        FT_UINT64, BASE_DEC, NULL, 0x0,
1951
16
        NULL, HFILL } },
1952
1953
16
    { &hf_extras_unknown,
1954
16
      { "Unknown", "memcache.extras.unknown",
1955
16
        FT_BYTES, BASE_NONE, NULL, 0x0,
1956
16
        "Unknown Extras", HFILL } },
1957
1958
16
    { &hf_key,
1959
16
      { "Key", "memcache.key",
1960
16
        FT_STRING, BASE_NONE, NULL, 0x0,
1961
16
        NULL, HFILL } },
1962
1963
16
    { &hf_value,
1964
16
      { "Value", "memcache.value",
1965
16
        FT_STRING, BASE_NONE, NULL, 0x0,
1966
16
        NULL, HFILL } },
1967
1968
16
    { &hf_uint64_response,
1969
16
      { "Response", "memcache.extras.response",
1970
16
        FT_UINT64, BASE_DEC, NULL, 0x0,
1971
16
        NULL, HFILL } },
1972
1973
16
    { &hf_command,
1974
16
      { "Command", "memcache.command",
1975
16
        FT_STRING, BASE_NONE , NULL, 0x0,
1976
16
        NULL, HFILL } },
1977
1978
16
    { &hf_subcommand,
1979
16
      { "Sub command", "memcache.subcommand",
1980
16
        FT_STRING, BASE_NONE, NULL, 0x0,
1981
16
        "Sub command if any", HFILL } },
1982
1983
16
    { &hf_flags,
1984
16
      { "Flags", "memcache.flags",
1985
16
        FT_UINT16, BASE_DEC, NULL, 0x0,
1986
16
        NULL, HFILL } },
1987
1988
16
    { &hf_expiration,
1989
16
      { "Expiration", "memcache.expiration",
1990
16
        FT_UINT32, BASE_DEC, NULL, 0x0,
1991
16
        NULL, HFILL } },
1992
1993
16
    { &hf_noreply,
1994
16
      { "Noreply", "memcache.noreply",
1995
16
        FT_STRING, BASE_NONE, NULL, 0x0,
1996
16
        "Client does not expect a reply", HFILL } },
1997
1998
16
    { &hf_response,
1999
16
      { "Response", "memcache.response",
2000
16
        FT_STRING, BASE_NONE, NULL, 0x0,
2001
16
        "Response command", HFILL } },
2002
2003
16
    { &hf_version,
2004
16
      { "Version", "memcache.version",
2005
16
        FT_STRING, BASE_NONE, NULL, 0x0,
2006
16
        "Version of running memcache", HFILL } },
2007
2008
16
    { &hf_slabclass,
2009
16
      { "Slab class", "memcache.slabclass",
2010
16
        FT_UINT32, BASE_DEC, NULL, 0x0,
2011
16
        "Slab class of a stat", HFILL } },
2012
2013
16
    { &hf_name,
2014
16
      { "Stat name", "memcache.name",
2015
16
        FT_STRING, BASE_NONE, NULL, 0x0,
2016
16
        "Name of a stat", HFILL } },
2017
2018
16
    { &hf_name_value,
2019
16
      { "Stat value", "memcache.name_value",
2020
16
        FT_STRING, BASE_NONE, NULL, 0x0,
2021
16
        "Value of a stat", HFILL } },
2022
16
  };
2023
2024
16
  static int *ett[] = {
2025
16
    &ett_memcache,
2026
16
    &ett_extras
2027
16
  };
2028
2029
16
  static ei_register_info ei[] = {
2030
16
      { &ei_extras_unknown,  { "memcache.extras.notexpected", PI_UNDECODED, PI_WARN, "shall not have Extras", EXPFILL }},
2031
16
      { &ei_extras_missing,  { "memcache.extras.missing", PI_UNDECODED, PI_WARN, "must have Extras", EXPFILL }},
2032
16
      { &ei_key_unknown,     { "memcache.key.notexpected", PI_UNDECODED, PI_WARN, "shall not have Key", EXPFILL }},
2033
16
      { &ei_key_missing,     { "memcache.key.missing", PI_UNDECODED, PI_WARN, "must have Key", EXPFILL }},
2034
16
      { &ei_value_length,    { "memcache.value.invalid", PI_UNDECODED, PI_WARN, "Illegal Value length, should be 8", EXPFILL }},
2035
16
      { &ei_value_unknown,   { "memcache.value.notexpected", PI_UNDECODED, PI_WARN, "shall not have Value", EXPFILL }},
2036
16
      { &ei_value_missing,   { "memcache.value.missing", PI_UNDECODED, PI_WARN, "must have Value", EXPFILL }},
2037
16
      { &ei_magic_unknown,   { "memcache.magic.unknown", PI_UNDECODED, PI_WARN, "Unknown magic byte", EXPFILL }},
2038
16
      { &ei_opcode_unknown,  { "memcache.opcode.unknown", PI_UNDECODED, PI_WARN, "Unknown opcode", EXPFILL }},
2039
16
      { &ei_status_response, { "memcache.status.response", PI_RESPONSE_CODE, PI_NOTE, "Error response", EXPFILL }},
2040
16
      { &ei_reserved_value,  { "memcache.reserved.expert", PI_UNDECODED, PI_WARN, "Reserved value", EXPFILL }},
2041
16
  };
2042
2043
16
  module_t        *memcache_module;
2044
16
  expert_module_t *expert_memcache;
2045
2046
16
  proto_memcache = proto_register_protocol ("Memcache Protocol", "MEMCACHE", "memcache");
2047
16
  memcache_tcp_handle = register_dissector ("memcache.tcp", dissect_memcache_tcp, proto_memcache);
2048
16
  memcache_udp_handle = register_dissector ("memcache.udp", dissect_memcache_udp, proto_memcache);
2049
2050
16
  proto_register_field_array (proto_memcache, hf, array_length (hf));
2051
16
  proto_register_subtree_array (ett, array_length (ett));
2052
16
  expert_memcache = expert_register_protocol(proto_memcache);
2053
16
  expert_register_field_array(expert_memcache, ei, array_length(ei));
2054
2055
  /* Register our configuration options */
2056
16
  memcache_module = prefs_register_protocol (proto_memcache, NULL);
2057
2058
16
  prefs_register_bool_preference (memcache_module, "desegment_headers",
2059
16
                                 "Reassemble MEMCACHE headers spanning multiple TCP segments",
2060
16
                                 "Whether the MEMCACHE dissector should reassemble headers "
2061
16
                                 "of a request spanning multiple TCP segments. "
2062
16
                                 "To use this option, you must also enable "
2063
16
                                 "\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
2064
16
                                 &memcache_desegment_headers);
2065
2066
16
  prefs_register_bool_preference (memcache_module, "desegment_pdus",
2067
16
                                  "Reassemble PDUs spanning multiple TCP segments",
2068
16
                                  "Whether the memcache dissector should reassemble PDUs"
2069
16
                                  " spanning multiple TCP segments."
2070
16
                                  " To use this option, you must also enable \"Allow subdissectors"
2071
16
                                  " to reassemble TCP streams\" in the TCP protocol settings.",
2072
16
                                  &memcache_desegment_body);
2073
16
}
2074
2075
/* Register the tcp and udp memcache dissectors. */
2076
void
2077
proto_reg_handoff_memcache (void)
2078
16
{
2079
16
  dissector_add_uint_range_with_preference("tcp.port", MEMCACHE_DEFAULT_RANGE, memcache_tcp_handle);
2080
16
  dissector_add_uint_range_with_preference("udp.port", MEMCACHE_DEFAULT_RANGE, memcache_udp_handle);
2081
16
}
2082
2083
/*
2084
 * Editor modelines
2085
 *
2086
 * Local Variables:
2087
 * c-basic-offset: 2
2088
 * tab-width: 8
2089
 * indent-tabs-mode: nil
2090
 * End:
2091
 *
2092
 * ex: set shiftwidth=2 tabstop=8 expandtab:
2093
 * :indentSize=2:tabSize=8:noTabs=true:
2094
 */