2 * Routines for Distributed Checksum Clearinghouse packet dissection
3 * DCC Home: http://www.rhyolite.com/anti-spam/dcc/
5 * Copyright 1999, Nathan Neulinger <nneul@umr.edu>
7 * $Id: packet-dccp.c,v 1.5 2002/05/03 20:34:14 nneul Exp $
9 * Ethereal - Network traffic analyzer
10 * By Gerald Combs <gerald@ethereal.com>
11 * Copyright 1998 Gerald Combs
13 * Copied from packet-tftp.c
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #ifdef HAVE_SYS_TYPES_H
37 # include <sys/types.h>
40 #ifdef HAVE_NETINET_IN_H
41 # include <netinet/in.h>
46 #include <epan/packet.h>
48 #include <packet-dccp.h>
50 static int proto_dccp = -1;
51 static int hf_dccp_len = -1;
52 static int hf_dccp_pkt_vers = -1;
53 static int hf_dccp_op = -1;
54 static int hf_dccp_clientid = -1;
55 static int hf_dccp_opnums_host = -1;
56 static int hf_dccp_opnums_pid = -1;
57 static int hf_dccp_opnums_report = -1;
58 static int hf_dccp_opnums_retrans = -1;
60 static int hf_dccp_signature = -1;
61 static int hf_dccp_max_pkt_vers = -1;
62 static int hf_dccp_qdelay_ms = -1;
63 static int hf_dccp_brand = -1;
65 static int hf_dccp_ck_type = -1;
66 static int hf_dccp_ck_len = -1;
67 static int hf_dccp_ck_sum = -1;
69 static int hf_dccp_date = -1;
70 static int hf_dccp_msg = -1;
72 static int hf_dccp_target = -1;
74 static int hf_dccp_adminop = -1;
75 static int hf_dccp_adminval = -1;
76 static int hf_dccp_floodop = -1;
77 static int hf_dccp_trace = -1;
78 static int hf_dccp_trace_admin = -1;
79 static int hf_dccp_trace_anon = -1;
80 static int hf_dccp_trace_client = -1;
81 static int hf_dccp_trace_rlim = -1;
82 static int hf_dccp_trace_query = -1;
83 static int hf_dccp_trace_ridc = -1;
84 static int hf_dccp_trace_flood = -1;
86 static gint ett_dccp = -1;
87 static gint ett_dccp_opnums = -1;
88 static gint ett_dccp_op = -1;
89 static gint ett_dccp_ck = -1;
90 static gint ett_dccp_trace = -1;
93 #define D_SIGNATURE() \
94 proto_tree_add_item(dccp_optree, hf_dccp_signature, tvb, \
95 offset, sizeof(DCC_SIGNATURE), FALSE); \
96 offset += sizeof(DCC_SIGNATURE);
98 #define D_LABEL(label,len) \
99 proto_tree_add_text(dccp_optree, tvb, offset, len, label); \
102 #define D_TEXT(label, endpad) { \
103 int next_offset,linelen,left; \
105 while (tvb_offset_exists(tvb, offset+endpad)) { \
106 left = tvb_length_remaining(tvb,offset) - endpad; \
107 linelen = tvb_find_line_end(tvb, offset, left, &next_offset); \
108 line = tvb_get_ptr(tvb, offset, linelen); \
109 proto_tree_add_text(dccp_optree, tvb, offset, \
110 next_offset - offset, "%s: %s", \
111 label, tvb_format_text(tvb, offset, next_offset - offset)); \
112 offset = next_offset; \
118 proto_tree_add_item_hidden(dccp_tree, hf_dccp_target, tvb, \
119 offset, sizeof(DCC_TGTS), FALSE); \
120 proto_tree_add_text(dccp_optree, tvb, offset, sizeof(DCC_TGTS), \
121 val_to_str(tvb_get_ntohl(tvb,offset), dccp_target_vals, "Targets (%u)")); \
122 offset += sizeof(DCC_TGTS); \
127 ts.secs = tvb_get_ntohl(tvb,offset); \
128 proto_tree_add_time(dccp_optree, hf_dccp_date, tvb, offset, 4, &ts); \
133 #define D_CHECKSUM() { \
134 proto_tree *cktree, *ti; \
135 ti = proto_tree_add_text(dccp_optree, tvb, offset, sizeof(DCC_CK), \
136 "Checksum - %s", val_to_str(tvb_get_guint8(tvb,offset), \
138 "Unknown Type: %u")); \
139 cktree = proto_item_add_subtree(ti, ett_dccp_ck); \
140 proto_tree_add_item(cktree, hf_dccp_ck_type, tvb, offset, 1, FALSE); \
142 proto_tree_add_item(cktree, hf_dccp_ck_len, tvb, offset, 1, FALSE); \
144 proto_tree_add_item(cktree, hf_dccp_ck_sum, tvb, offset, \
145 sizeof(DCC_SUM), FALSE); \
146 offset += sizeof(DCC_SUM); \
150 /* Lookup string tables */
151 static const value_string dccp_op_vals[] = {
152 {DCC_OP_INVALID, "Invalid Op"},
153 {DCC_OP_NOP, "No-Op"},
154 {DCC_OP_REPORT, "Report and Query"},
155 {DCC_OP_QUERY, "Query"},
156 {DCC_OP_QUERY_RESP, "Server Response"},
157 {DCC_OP_ADMN, "Admin"},
159 {DCC_OP_ERROR, "Server Failing"},
160 {DCC_OP_DELETE, "Delete Checksum(s)"},
164 static const value_string dccp_cktype_vals[] = {
165 {DCC_CK_INVALID, "Invalid/Deleted from DB when seen"},
166 {DCC_CK_IP, "MD5 of binary source IPv6 address"},
167 {DCC_CK_ENV_FROM, "MD5 of envelope Mail From value"},
168 {DCC_CK_FROM, "MD5 of header From: line"},
169 {DCC_CK_SUB, "MD5 of substitute header line"},
170 {DCC_CK_MESSAGE_ID, "MD5 of header Message-ID: line"},
171 {DCC_CK_RECEIVED, "MD5 of last header Received: line"},
172 {DCC_CK_BODY, "MD5 of body"},
173 {DCC_CK_FUZ1, "MD5 of filtered body - FUZ1"},
174 {DCC_CK_FUZ2, "MD5 of filtered body - FUZ2"},
175 {DCC_CK_FUZ3, "MD5 of filtered body - FUZ3"},
176 {DCC_CK_FUZ4, "MD5 of filtered body - FUZ4"},
177 {DCC_CK_SRVR_ID, "hostname for server-ID check "},
178 {DCC_CK_ENV_TO, "MD5 of envelope Rcpt To value"},
182 static const value_string dccp_adminop_vals[] = {
183 {DCC_AOP_OK, "Never sent"},
184 {DCC_AOP_STOP, "Stop Gracefully"},
185 {DCC_AOP_NEW_IDS, "Load keys and client IDs"},
186 {DCC_AOP_FLOD, "Flood control"},
187 {DCC_AOP_DB_UNLOCK, "Start Switch to new database"},
188 {DCC_AOP_DB_NEW, "Finish Switch to new database"},
189 {DCC_AOP_STATS, "Return counters"},
190 {DCC_AOP_STATS_CLEAR, "Return and zero counters"},
191 {DCC_AOP_TRACE_ON, "Enable tracing"},
192 {DCC_AOP_TRACE_OFF, "Disable tracing"},
193 {DCC_AOP_CUR_CLIENTS, "List clients"},
197 static const value_string dccp_target_vals[] = {
198 {DCC_TGTS_TOO_MANY, "Targets (>= 16777200)"},
199 {DCC_TGTS_OK, "Certified not spam"},
200 {DCC_TGTS_OK2, "Half certified not spam"},
201 {DCC_TGTS_DEL, "Deleted checksum"},
202 {DCC_TGTS_INVALID, "Invalid"},
206 static const value_string dccp_floodop_vals[] = {
207 {DCC_AOP_FLOD_CHECK, "Check"},
208 {DCC_AOP_FLOD_SHUTDOWN, "Shutdown"},
209 {DCC_AOP_FLOD_HALT, "Halt"},
210 {DCC_AOP_FLOD_RESUME, "Resume"},
211 {DCC_AOP_FLOD_REWIND, "Rewind"},
212 {DCC_AOP_FLOD_LIST, "List"},
213 {DCC_AOP_FLOD_STATS, "Stats"},
214 {DCC_AOP_FLOD_STATS_CLEAR, "Clear Stats"},
219 dissect_dccp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
221 proto_tree *dccp_tree, *dccp_optree, *dccp_opnumtree, *ti;
222 proto_tree *dccp_tracetree;
224 int client_is_le = 0;
226 int i, count, is_response;
228 if (pinfo->srcport != DCC_PORT && pinfo->destport != DCC_PORT) {
229 /* Not the right port - not a DCC packet. */
233 /* get at least a full packet structure */
234 if ( !tvb_bytes_exist(tvb, 0, sizeof(DCC_HDR)) ) {
235 /* Doesn't have enough bytes to contain packet header. */
239 if (check_col(pinfo->cinfo, COL_PROTOCOL))
240 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DCCP");
243 is_response = pinfo->srcport == DCC_PORT;
245 if (check_col(pinfo->cinfo, COL_INFO)) {
246 col_add_fstr(pinfo->cinfo, COL_INFO,
248 is_response ? "Response" : "Request",
249 val_to_str(tvb_get_guint8(tvb, offset+3),
250 dccp_op_vals, "Unknown Op: %u")
255 ti = proto_tree_add_item(tree, proto_dccp, tvb, offset, -1,
257 dccp_tree = proto_item_add_subtree(ti, ett_dccp);
259 proto_tree_add_item(dccp_tree, hf_dccp_len, tvb,
262 if ( !tvb_bytes_exist(tvb, 0, tvb_get_ntohs(tvb, offset))) {
263 /* Doesn't have number of bytes that header claims. */
264 proto_tree_add_text(dccp_tree, tvb, offset, 2, "Error - packet is shorter than header claims!");
268 proto_tree_add_item(dccp_tree, hf_dccp_pkt_vers, tvb,
272 op = tvb_get_guint8(tvb, offset);
273 proto_tree_add_item(dccp_tree, hf_dccp_op, tvb,
277 proto_tree_add_item(dccp_tree, hf_dccp_clientid, tvb,
281 ti = proto_tree_add_text(dccp_tree, tvb, offset, -1, "Operation Numbers (Opaque to Server)");
282 dccp_opnumtree = proto_item_add_subtree(ti, ett_dccp_opnums);
284 /* Note - these are indeterminate - they are sortof considered opaque to the client */
285 /* Make some attempt to figure out if this data is little endian, not guaranteed to be
286 correct if connection went through a firewall or similar. */
288 /* Very hokey check - if all three of pid/report/retrans look like little-endian
289 numbers, host is probably little endian. Probably innacurate on super-heavily-used
290 DCC clients though. This should be good enough for now. */
291 client_is_le = ( (tvb_get_guint8(tvb, offset+4) | tvb_get_guint8(tvb, offset+4)) &&
292 (tvb_get_guint8(tvb, offset+8) | tvb_get_guint8(tvb, offset+9)) &&
293 (tvb_get_guint8(tvb, offset+12) | tvb_get_guint8(tvb, offset+13)) );
295 proto_tree_add_item(dccp_opnumtree, hf_dccp_opnums_host, tvb,
296 offset, 4, client_is_le);
299 proto_tree_add_item(dccp_opnumtree, hf_dccp_opnums_pid, tvb,
300 offset, 4, client_is_le);
303 proto_tree_add_item(dccp_opnumtree, hf_dccp_opnums_report, tvb,
304 offset, 4, client_is_le);
307 proto_tree_add_item(dccp_opnumtree, hf_dccp_opnums_retrans, tvb,
308 offset, 4, client_is_le);
311 ti = proto_tree_add_text(dccp_tree, tvb, offset, -1, "Operation: %s",
312 val_to_str(op, dccp_op_vals, "Unknown Op: %u"));
313 dccp_optree = proto_item_add_subtree(ti, ett_dccp_op);
322 for (i=0; i<=DCC_QUERY_MAX &&
323 tvb_bytes_exist(tvb, offset+sizeof(DCC_SIGNATURE),1); i++)
330 case DCC_OP_QUERY_RESP:
331 for (i=0; i<=DCC_QUERY_MAX &&
332 tvb_bytes_exist(tvb, offset+sizeof(DCC_SIGNATURE),1); i++)
342 int left = tvb_length_remaining(tvb, offset) -
343 sizeof(DCC_SIGNATURE);
344 if ( left == sizeof(DCC_ADMN_RESP_CLIENTS) )
347 D_LABEL("Id", sizeof(DCC_CLNT_ID));
348 D_LABEL("Last Used", 4);
349 D_LABEL("Requests", 4);
353 D_TEXT("Response Text", sizeof(DCC_SIGNATURE));
363 aop = tvb_get_guint8(tvb, offset+4);
364 proto_tree_add_item(dccp_optree, hf_dccp_adminop, tvb, offset+4,
366 if (check_col(pinfo->cinfo, COL_INFO)) {
367 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
368 val_to_str(tvb_get_guint8(tvb,offset+4),
369 dccp_adminop_vals, "Unknown (%u)"));
372 if (aop == DCC_AOP_TRACE_ON || aop == DCC_AOP_TRACE_OFF )
374 ti = proto_tree_add_item(dccp_optree, hf_dccp_trace, tvb, offset,
376 dccp_tracetree = proto_item_add_subtree(ti, ett_dccp_trace);
377 proto_tree_add_item(dccp_tracetree, hf_dccp_trace_admin, tvb, offset, 4, FALSE);
378 proto_tree_add_item(dccp_tracetree, hf_dccp_trace_anon, tvb, offset, 4, FALSE);
379 proto_tree_add_item(dccp_tracetree, hf_dccp_trace_client, tvb, offset, 4, FALSE);
380 proto_tree_add_item(dccp_tracetree, hf_dccp_trace_rlim, tvb, offset, 4, FALSE);
381 proto_tree_add_item(dccp_tracetree, hf_dccp_trace_query, tvb, offset, 4, FALSE);
382 proto_tree_add_item(dccp_tracetree, hf_dccp_trace_ridc, tvb, offset, 4, FALSE);
383 proto_tree_add_item(dccp_tracetree, hf_dccp_trace_flood, tvb, offset, 4, FALSE);
385 else if ( aop == DCC_AOP_FLOD )
387 proto_tree_add_item(dccp_optree, hf_dccp_floodop,
388 tvb, offset, 4, FALSE);
389 if (check_col(pinfo->cinfo, COL_INFO)) {
390 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
391 val_to_str(tvb_get_ntohl(tvb,offset),
392 dccp_floodop_vals, "Unknown (%u)"));
397 proto_tree_add_item(dccp_optree, hf_dccp_adminval,
398 tvb, offset, 4, FALSE);
402 offset += 1; /* admin op we did in reverse order */
409 proto_tree_add_item(dccp_optree, hf_dccp_max_pkt_vers, tvb,
413 D_LABEL("Unused", 1);
415 proto_tree_add_item(dccp_optree, hf_dccp_qdelay_ms, tvb,
419 proto_tree_add_item(dccp_optree, hf_dccp_brand, tvb,
420 offset, sizeof(DCC_BRAND), FALSE);
421 offset += sizeof(DCC_BRAND);
436 proto_register_dccp(void)
438 static hf_register_info hf[] = {
440 "Packet Length", "dccp.len", FT_UINT16, BASE_DEC,
441 NULL, 0, "Packet Length", HFILL }},
443 { &hf_dccp_pkt_vers, {
444 "Packet Version", "dccp.pkt_vers", FT_UINT16, BASE_DEC,
445 NULL, 0, "Packet Version", HFILL }},
448 "Operation Type", "dccp.op", FT_UINT8, BASE_DEC,
449 VALS(dccp_op_vals), 0, "Operation Type", HFILL }},
451 { &hf_dccp_clientid, {
452 "Client ID", "dccp.clientid", FT_UINT32, BASE_DEC,
453 NULL, 0, "Client ID", HFILL }},
455 { &hf_dccp_opnums_host, {
456 "Host", "dccp.opnums.host", FT_IPv4, BASE_DEC,
457 NULL, 0, "Host", HFILL }},
459 { &hf_dccp_opnums_pid, {
460 "Process ID", "dccp.opnums.pid", FT_UINT32, BASE_DEC,
461 NULL, 0, "Process ID", HFILL }},
463 { &hf_dccp_opnums_report, {
464 "Report", "dccp.opnums.report", FT_UINT32, BASE_DEC,
465 NULL, 0, "Report", HFILL }},
467 { &hf_dccp_opnums_retrans, {
468 "Retransmission", "dccp.opnums.retrans", FT_UINT32, BASE_DEC,
469 NULL, 0, "Retransmission", HFILL }},
471 { &hf_dccp_signature, {
472 "Signature", "dccp.signature", FT_BYTES, BASE_HEX,
473 NULL, 0, "Signature", HFILL }},
475 { &hf_dccp_max_pkt_vers, {
476 "Maximum Packet Version", "dccp.max_pkt_vers", FT_UINT8, BASE_DEC,
477 NULL, 0, "Maximum Packet Version", HFILL }},
479 { &hf_dccp_qdelay_ms, {
480 "Client Delay", "dccp.qdelay_ms", FT_UINT16, BASE_DEC,
481 NULL, 0, "Client Delay", HFILL }},
484 "Server Brand", "dccp.brand", FT_STRING, BASE_DEC,
485 NULL, 0, "Server Brand", HFILL }},
487 { &hf_dccp_ck_type, {
488 "Type", "dccp.checksum.type", FT_UINT8, BASE_DEC,
489 VALS(dccp_cktype_vals), 0, "Checksum Type", HFILL }},
492 "Length", "dccp.checksum.length", FT_UINT8, BASE_DEC,
493 NULL, 0, "Checksum Length", HFILL }},
496 "Sum", "dccp.checksum.sum", FT_BYTES, BASE_HEX,
497 NULL, 0, "Checksum", HFILL }},
500 "Target", "dccp.target", FT_UINT32, BASE_HEX,
501 NULL, 0, "Target", HFILL }},
504 "Date", "dccp.date", FT_ABSOLUTE_TIME, BASE_DEC,
505 NULL, 0, "Date", HFILL }},
507 { &hf_dccp_adminop, {
508 "Admin Op", "dccp.adminop", FT_UINT8, BASE_DEC,
509 VALS(dccp_adminop_vals), 0, "Admin Op", HFILL }},
511 { &hf_dccp_adminval, {
512 "Admin Value", "dccp.adminval", FT_UINT32, BASE_DEC,
513 NULL, 0, "Admin Value", HFILL }},
516 "Trace Bits", "dccp.trace", FT_UINT32, BASE_HEX,
517 NULL, 0, "Trace Bits", HFILL }},
519 { &hf_dccp_trace_admin, {
520 "Admin Requests", "dccp.trace.admin", FT_UINT32, BASE_BIN,
521 NULL, 0x0001, "Admin Requests", HFILL }},
523 { &hf_dccp_trace_anon, {
524 "Anonymous Requests", "dccp.trace.anon", FT_UINT32, BASE_BIN,
525 NULL, 0x0002, "Anonymous Requests", HFILL }},
527 { &hf_dccp_trace_client, {
528 "Authenticated Client Requests", "dccp.trace.client", FT_UINT32, BASE_BIN,
529 NULL, 0x0004, "Authenticated Client Requests", HFILL }},
531 { &hf_dccp_trace_rlim, {
532 "Rate-Limited Requests", "dccp.trace.rlim", FT_UINT32, BASE_BIN,
533 NULL, 0x0008, "Rate-Limited Requests", HFILL }},
535 { &hf_dccp_trace_query, {
536 "Queries and Reports", "dccp.trace.query", FT_UINT32, BASE_BIN,
537 NULL, 0x0010, "Queries and Reports", HFILL }},
539 { &hf_dccp_trace_ridc, {
540 "RID Cache Messages", "dccp.trace.ridc", FT_UINT32, BASE_BIN,
541 NULL, 0x0020, "RID Cache Messages", HFILL }},
543 { &hf_dccp_trace_flood, {
544 "Input/Output Flooding", "dccp.trace.flood", FT_UINT32, BASE_BIN,
545 NULL, 0x0040, "Input/Output Flooding", HFILL }},
547 { &hf_dccp_floodop, {
548 "Flood Control Operation", "dccp.floodop", FT_UINT32, BASE_DEC,
549 VALS(dccp_floodop_vals), 0, "Flood Control Operation", HFILL }},
552 static gint *ett[] = {
560 proto_dccp = proto_register_protocol("Distributed Checksum Clearinghouse Prototocl",
563 proto_register_field_array(proto_dccp, hf, array_length(hf));
565 proto_register_subtree_array(ett, array_length(ett));
569 proto_reg_handoff_dccp(void)
571 heur_dissector_add("udp", dissect_dccp, proto_dccp);