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 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * Copied from packet-tftp.c
13 * SPDX-License-Identifier: GPL-2.0-or-later
18 #include <epan/packet.h>
19 #include <epan/expert.h>
21 #include "packet-dcc.h"
23 void proto_register_dcc(void);
24 void proto_reg_handoff_dcc(void);
27 static int hf_dcc_len
;
28 static int hf_dcc_pkt_vers
;
30 static int hf_dcc_clientid
;
31 static int hf_dcc_opnums_host
;
32 static int hf_dcc_opnums_pid
;
33 static int hf_dcc_opnums_report
;
34 static int hf_dcc_opnums_retrans
;
36 static int hf_dcc_signature
;
37 static int hf_dcc_max_pkt_vers
;
38 static int hf_dcc_qdelay_ms
;
39 static int hf_dcc_brand
;
41 static int hf_dcc_ck_type
;
42 static int hf_dcc_ck_len
;
43 static int hf_dcc_ck_sum
;
45 static int hf_dcc_date
;
47 static int hf_dcc_target
;
48 static int hf_dcc_response_text
;
50 static int hf_dcc_adminop
;
51 static int hf_dcc_adminval
;
52 static int hf_dcc_floodop
;
53 static int hf_dcc_trace
;
54 static int hf_dcc_trace_admin
;
55 static int hf_dcc_trace_anon
;
56 static int hf_dcc_trace_client
;
57 static int hf_dcc_trace_rlim
;
58 static int hf_dcc_trace_query
;
59 static int hf_dcc_trace_ridc
;
60 static int hf_dcc_trace_flood
;
62 static int hf_dcc_addr
;
64 static int hf_dcc_last_used
;
65 static int hf_dcc_requests
;
66 static int hf_dcc_pad
;
67 static int hf_dcc_unused
;
70 static int ett_dcc_opnums
;
71 static int ett_dcc_op
;
72 static int ett_dcc_ck
;
73 static int ett_dcc_trace
;
75 static expert_field ei_dcc_len
;
78 #define D_SIGNATURE() \
79 proto_tree_add_item(dcc_optree, hf_dcc_signature, tvb, \
80 offset, (int)sizeof(DCC_SIGNATURE), ENC_NA); \
81 offset += (int)sizeof(DCC_SIGNATURE);
83 #define D_LABEL(hf_label, len, encoding) \
84 proto_tree_add_item(dcc_optree, hf_label, tvb, offset, len, encoding); \
87 #define D_TEXT(hf_label, endpad) { \
88 int next_offset,left; \
89 while (tvb_offset_exists(tvb, offset+endpad)) { \
90 left = tvb_reported_length_remaining(tvb,offset) - endpad; \
91 tvb_find_line_end(tvb, offset, left, &next_offset, \
93 proto_tree_add_item(dcc_optree, hf_label, tvb, offset, \
94 next_offset - offset, ENC_ASCII|ENC_NA); \
95 offset = next_offset; \
101 proto_tree_add_item(dcc_tree, hf_dcc_target, tvb, \
102 offset, (int)sizeof(DCC_TGTS), ENC_BIG_ENDIAN); \
103 offset += (int)sizeof(DCC_TGTS);
106 proto_tree_add_item(dcc_optree, hf_dcc_date, tvb, offset, 4, ENC_TIME_SECS|ENC_BIG_ENDIAN); \
111 #define D_CHECKSUM() { \
112 proto_tree *cktree; \
113 cktree = proto_tree_add_subtree_format(dcc_optree, tvb, offset, (int)sizeof(DCC_CK), \
114 ett_dcc_ck, NULL, "Checksum - %s", val_to_str(tvb_get_uint8(tvb,offset), \
116 "Unknown Type: %u")); \
117 proto_tree_add_item(cktree, hf_dcc_ck_type, tvb, offset, 1, ENC_BIG_ENDIAN); \
119 proto_tree_add_item(cktree, hf_dcc_ck_len, tvb, offset, 1, ENC_BIG_ENDIAN); \
121 proto_tree_add_item(cktree, hf_dcc_ck_sum, tvb, offset, \
122 (int)sizeof(DCC_SUM), ENC_NA); \
123 offset += (int)sizeof(DCC_SUM); \
127 /* Lookup string tables */
128 static const value_string dcc_op_vals
[] = {
129 {DCC_OP_INVALID
, "Invalid Op"},
130 {DCC_OP_NOP
, "No-Op"},
131 {DCC_OP_REPORT
, "Report and Query"},
132 {DCC_OP_QUERY
, "Query"},
133 {DCC_OP_QUERY_RESP
, "Server Response"},
134 {DCC_OP_ADMN
, "Admin"},
136 {DCC_OP_ERROR
, "Server Failing"},
137 {DCC_OP_DELETE
, "Delete Checksum(s)"},
141 static const value_string dcc_cktype_vals
[] = {
142 {DCC_CK_INVALID
, "Invalid/Deleted from DB when seen"},
143 {DCC_CK_IP
, "MD5 of binary source IPv6 address"},
144 {DCC_CK_ENV_FROM
, "MD5 of envelope Mail From value"},
145 {DCC_CK_FROM
, "MD5 of header From: line"},
146 {DCC_CK_SUB
, "MD5 of substitute header line"},
147 {DCC_CK_MESSAGE_ID
, "MD5 of header Message-ID: line"},
148 {DCC_CK_RECEIVED
, "MD5 of last header Received: line"},
149 {DCC_CK_BODY
, "MD5 of body"},
150 {DCC_CK_FUZ1
, "MD5 of filtered body - FUZ1"},
151 {DCC_CK_FUZ2
, "MD5 of filtered body - FUZ2"},
152 {DCC_CK_FUZ3
, "MD5 of filtered body - FUZ3"},
153 {DCC_CK_FUZ4
, "MD5 of filtered body - FUZ4"},
154 {DCC_CK_SRVR_ID
, "hostname for server-ID check "},
155 {DCC_CK_ENV_TO
, "MD5 of envelope Rcpt To value"},
159 static const value_string dcc_adminop_vals
[] = {
160 {DCC_AOP_OK
, "Never sent"},
161 {DCC_AOP_STOP
, "Stop Gracefully"},
162 {DCC_AOP_NEW_IDS
, "Load keys and client IDs"},
163 {DCC_AOP_FLOD
, "Flood control"},
164 {DCC_AOP_DB_UNLOCK
, "Start Switch to new database"},
165 {DCC_AOP_DB_NEW
, "Finish Switch to new database"},
166 {DCC_AOP_STATS
, "Return counters"},
167 {DCC_AOP_STATS_CLEAR
, "Return and zero counters"},
168 {DCC_AOP_TRACE_ON
, "Enable tracing"},
169 {DCC_AOP_TRACE_OFF
, "Disable tracing"},
170 {DCC_AOP_CUR_CLIENTS
, "List clients"},
174 static const value_string dcc_target_vals
[] = {
175 {DCC_TGTS_TOO_MANY
, "Targets (>= 16777200)"},
176 {DCC_TGTS_OK
, "Certified not spam"},
177 {DCC_TGTS_OK2
, "Half certified not spam"},
178 {DCC_TGTS_DEL
, "Deleted checksum"},
179 {DCC_TGTS_INVALID
, "Invalid"},
183 static const value_string dcc_floodop_vals
[] = {
184 {DCC_AOP_FLOD_CHECK
, "Check"},
185 {DCC_AOP_FLOD_SHUTDOWN
, "Shutdown"},
186 {DCC_AOP_FLOD_HALT
, "Halt"},
187 {DCC_AOP_FLOD_RESUME
, "Resume"},
188 {DCC_AOP_FLOD_REWIND
, "Rewind"},
189 {DCC_AOP_FLOD_LIST
, "List"},
190 {DCC_AOP_FLOD_STATS
, "Stats"},
191 {DCC_AOP_FLOD_STATS_CLEAR
, "Clear Stats"},
196 dissect_dcc(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
198 proto_tree
*dcc_tree
, *dcc_optree
, *dcc_opnumtree
, *ti
;
199 proto_tree
*dcc_tracetree
;
200 proto_item
*len_item
;
202 int client_is_le
= 0;
206 if (pinfo
->srcport
!= DCC_PORT
&& pinfo
->destport
!= DCC_PORT
) {
207 /* Not the right port - not a DCC packet. */
211 /* get at least a full packet structure */
212 if ( tvb_reported_length(tvb
) < sizeof(DCC_HDR
) ) {
213 /* Doesn't have enough bytes to contain packet header. */
217 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "DCC");
220 is_response
= pinfo
->srcport
== DCC_PORT
;
222 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
224 is_response
? "Response" : "Request",
225 val_to_str(tvb_get_uint8(tvb
, offset
+3),
226 dcc_op_vals
, "Unknown Op: %u"));
228 ti
= proto_tree_add_item(tree
, proto_dcc
, tvb
, offset
, -1,
230 dcc_tree
= proto_item_add_subtree(ti
, ett_dcc
);
232 len_item
= proto_tree_add_item(dcc_tree
, hf_dcc_len
, tvb
,
233 offset
, 2, ENC_BIG_ENDIAN
);
235 if ( tvb_reported_length(tvb
) < tvb_get_ntohs(tvb
, offset
)) {
236 /* Doesn't have number of bytes that header claims. */
237 expert_add_info(pinfo
, len_item
, &ei_dcc_len
);
242 proto_tree_add_item(dcc_tree
, hf_dcc_pkt_vers
, tvb
,
243 offset
, 1, ENC_BIG_ENDIAN
);
246 op
= tvb_get_uint8(tvb
, offset
);
247 proto_tree_add_item(dcc_tree
, hf_dcc_op
, tvb
,
248 offset
, 1, ENC_BIG_ENDIAN
);
251 proto_tree_add_item(dcc_tree
, hf_dcc_clientid
, tvb
,
252 offset
, 4, ENC_BIG_ENDIAN
);
255 dcc_opnumtree
= proto_tree_add_subtree(dcc_tree
, tvb
, offset
, -1, ett_dcc_opnums
, NULL
, "Operation Numbers (Opaque to Server)");
257 /* Note - these are indeterminate - they are sortof considered opaque to the client */
258 /* Make some attempt to figure out if this data is little endian, not guaranteed to be
259 correct if connection went through a firewall or similar. */
261 /* Very hokey check - if all three of pid/report/retrans look like little-endian
262 numbers, host is probably little endian. Probably innacurate on super-heavily-used
263 DCC clients though. This should be good enough for now. */
264 client_is_le
= ( (tvb_get_uint8(tvb
, offset
+4) | tvb_get_uint8(tvb
, offset
+5)) &&
265 (tvb_get_uint8(tvb
, offset
+8) | tvb_get_uint8(tvb
, offset
+9)) &&
266 (tvb_get_uint8(tvb
, offset
+12) | tvb_get_uint8(tvb
, offset
+13)) );
268 proto_tree_add_item(dcc_opnumtree
, hf_dcc_opnums_host
, tvb
,
269 offset
, 4, client_is_le
);
272 proto_tree_add_item(dcc_opnumtree
, hf_dcc_opnums_pid
, tvb
,
273 offset
, 4, client_is_le
);
276 proto_tree_add_item(dcc_opnumtree
, hf_dcc_opnums_report
, tvb
,
277 offset
, 4, client_is_le
);
280 proto_tree_add_item(dcc_opnumtree
, hf_dcc_opnums_retrans
, tvb
,
281 offset
, 4, client_is_le
);
284 dcc_optree
= proto_tree_add_subtree_format(dcc_tree
, tvb
, offset
, -1, ett_dcc_op
, NULL
,
285 "Operation: %s", val_to_str(op
, dcc_op_vals
, "Unknown Op: %u"));
294 for (i
=0; i
<=DCC_QUERY_MAX
&&
295 tvb_bytes_exist(tvb
, offset
+(int)sizeof(DCC_SIGNATURE
),1); i
++)
302 case DCC_OP_QUERY_RESP
:
303 for (i
=0; i
<=DCC_QUERY_MAX
&&
304 tvb_bytes_exist(tvb
, offset
+(int)sizeof(DCC_SIGNATURE
),1); i
++)
314 int left_local
= tvb_reported_length_remaining(tvb
, offset
) -
315 (int)sizeof(DCC_SIGNATURE
);
316 if ( left_local
== sizeof(DCC_ADMN_RESP_CLIENTS
) )
318 D_LABEL(hf_dcc_addr
, 16, ENC_NA
);
319 D_LABEL(hf_dcc_id
, (int)sizeof(DCC_CLNT_ID
), ENC_BIG_ENDIAN
);
320 D_LABEL(hf_dcc_last_used
, 4, ENC_BIG_ENDIAN
);
321 D_LABEL(hf_dcc_requests
, 4, ENC_BIG_ENDIAN
);
325 D_TEXT(hf_dcc_response_text
, (int)sizeof(DCC_SIGNATURE
));
335 aop
= tvb_get_uint8(tvb
, offset
+4);
336 proto_tree_add_item(dcc_optree
, hf_dcc_adminop
, tvb
, offset
+4,
338 col_append_fstr(pinfo
->cinfo
, COL_INFO
, ", %s",
339 val_to_str(tvb_get_uint8(tvb
,offset
+4),
340 dcc_adminop_vals
, "Unknown (%u)"));
342 if (aop
== DCC_AOP_TRACE_ON
|| aop
== DCC_AOP_TRACE_OFF
)
344 ti
= proto_tree_add_item(dcc_optree
, hf_dcc_trace
, tvb
, offset
,
346 dcc_tracetree
= proto_item_add_subtree(ti
, ett_dcc_trace
);
347 proto_tree_add_item(dcc_tracetree
, hf_dcc_trace_admin
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
348 proto_tree_add_item(dcc_tracetree
, hf_dcc_trace_anon
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
349 proto_tree_add_item(dcc_tracetree
, hf_dcc_trace_client
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
350 proto_tree_add_item(dcc_tracetree
, hf_dcc_trace_rlim
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
351 proto_tree_add_item(dcc_tracetree
, hf_dcc_trace_query
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
352 proto_tree_add_item(dcc_tracetree
, hf_dcc_trace_ridc
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
353 proto_tree_add_item(dcc_tracetree
, hf_dcc_trace_flood
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
355 else if ( aop
== DCC_AOP_FLOD
)
357 proto_tree_add_item(dcc_optree
, hf_dcc_floodop
,
358 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
359 col_append_fstr(pinfo
->cinfo
, COL_INFO
, ", %s",
360 val_to_str(tvb_get_ntohl(tvb
,offset
),
361 dcc_floodop_vals
, "Unknown (%u)"));
365 proto_tree_add_item(dcc_optree
, hf_dcc_adminval
,
366 tvb
, offset
, 4, ENC_BIG_ENDIAN
);
370 offset
+= 1; /* admin op we did in reverse order */
371 D_LABEL(hf_dcc_pad
, 3, ENC_NA
);
377 proto_tree_add_item(dcc_optree
, hf_dcc_max_pkt_vers
, tvb
,
378 offset
, 1, ENC_BIG_ENDIAN
);
381 D_LABEL(hf_dcc_unused
, 1, ENC_NA
);
383 proto_tree_add_item(dcc_optree
, hf_dcc_qdelay_ms
, tvb
,
384 offset
, 2, ENC_BIG_ENDIAN
);
387 proto_tree_add_item(dcc_optree
, hf_dcc_brand
, tvb
,
388 offset
, (int)sizeof(DCC_BRAND
), ENC_ASCII
);
389 offset
+= (int)sizeof(DCC_BRAND
);
404 proto_register_dcc(void)
406 static hf_register_info hf
[] = {
408 "Packet Length", "dcc.len", FT_UINT16
, BASE_DEC
,
409 NULL
, 0, NULL
, HFILL
}},
411 { &hf_dcc_pkt_vers
, {
412 "Packet Version", "dcc.pkt_vers", FT_UINT16
, BASE_DEC
,
413 NULL
, 0, NULL
, HFILL
}},
416 "Operation Type", "dcc.op", FT_UINT8
, BASE_DEC
,
417 VALS(dcc_op_vals
), 0, NULL
, HFILL
}},
419 { &hf_dcc_clientid
, {
420 "Client ID", "dcc.clientid", FT_UINT32
, BASE_DEC
,
421 NULL
, 0, NULL
, HFILL
}},
423 { &hf_dcc_opnums_host
, {
424 "Host", "dcc.opnums.host", FT_UINT32
, BASE_DEC
,
425 NULL
, 0, NULL
, HFILL
}},
427 { &hf_dcc_opnums_pid
, {
428 "Process ID", "dcc.opnums.pid", FT_UINT32
, BASE_DEC
,
429 NULL
, 0, NULL
, HFILL
}},
431 { &hf_dcc_opnums_report
, {
432 "Report", "dcc.opnums.report", FT_UINT32
, BASE_DEC
,
433 NULL
, 0, NULL
, HFILL
}},
435 { &hf_dcc_opnums_retrans
, {
436 "Retransmission", "dcc.opnums.retrans", FT_UINT32
, BASE_DEC
,
437 NULL
, 0, NULL
, HFILL
}},
439 { &hf_dcc_signature
, {
440 "Signature", "dcc.signature", FT_BYTES
, BASE_NONE
,
441 NULL
, 0, NULL
, HFILL
}},
443 { &hf_dcc_max_pkt_vers
, {
444 "Maximum Packet Version", "dcc.max_pkt_vers", FT_UINT8
, BASE_DEC
,
445 NULL
, 0, NULL
, HFILL
}},
447 { &hf_dcc_qdelay_ms
, {
448 "Client Delay", "dcc.qdelay_ms", FT_UINT16
, BASE_DEC
,
449 NULL
, 0, NULL
, HFILL
}},
452 "Server Brand", "dcc.brand", FT_STRING
, BASE_NONE
,
453 NULL
, 0, NULL
, HFILL
}},
456 "Type", "dcc.checksum.type", FT_UINT8
, BASE_DEC
,
457 VALS(dcc_cktype_vals
), 0, "Checksum Type", HFILL
}},
460 "Length", "dcc.checksum.length", FT_UINT8
, BASE_DEC
,
461 NULL
, 0, "Checksum Length", HFILL
}},
464 "Sum", "dcc.checksum.sum", FT_BYTES
, BASE_NONE
,
465 NULL
, 0, "Checksum", HFILL
}},
468 "Target", "dcc.target", FT_UINT32
, BASE_HEX
,
469 VALS(dcc_target_vals
), 0, NULL
, HFILL
}},
471 { &hf_dcc_response_text
, {
472 "Response Text", "dcc.response_text", FT_BYTES
, BASE_NONE
,
473 NULL
, 0, NULL
, HFILL
}},
476 "Date", "dcc.date", FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_LOCAL
,
477 NULL
, 0, NULL
, HFILL
}},
480 "Admin Op", "dcc.adminop", FT_UINT8
, BASE_DEC
,
481 VALS(dcc_adminop_vals
), 0, NULL
, HFILL
}},
483 { &hf_dcc_adminval
, {
484 "Admin Value", "dcc.adminval", FT_UINT32
, BASE_DEC
,
485 NULL
, 0, NULL
, HFILL
}},
488 "Trace Bits", "dcc.trace", FT_UINT32
, BASE_HEX
,
489 NULL
, 0, NULL
, HFILL
}},
491 { &hf_dcc_trace_admin
, {
492 "Admin Requests", "dcc.trace.admin", FT_BOOLEAN
, 32,
493 NULL
, 0x00000001, NULL
, HFILL
}},
495 { &hf_dcc_trace_anon
, {
496 "Anonymous Requests", "dcc.trace.anon", FT_BOOLEAN
, 32,
497 NULL
, 0x00000002, NULL
, HFILL
}},
499 { &hf_dcc_trace_client
, {
500 "Authenticated Client Requests", "dcc.trace.client", FT_BOOLEAN
, 32,
501 NULL
, 0x00000004, NULL
, HFILL
}},
503 { &hf_dcc_trace_rlim
, {
504 "Rate-Limited Requests", "dcc.trace.rlim", FT_BOOLEAN
, 32,
505 NULL
, 0x00000008, NULL
, HFILL
}},
507 { &hf_dcc_trace_query
, {
508 "Queries and Reports", "dcc.trace.query", FT_BOOLEAN
, 32,
509 NULL
, 0x00000010, NULL
, HFILL
}},
511 { &hf_dcc_trace_ridc
, {
512 "RID Cache Messages", "dcc.trace.ridc", FT_BOOLEAN
, 32,
513 NULL
, 0x00000020, NULL
, HFILL
}},
515 { &hf_dcc_trace_flood
, {
516 "Input/Output Flooding", "dcc.trace.flood", FT_BOOLEAN
, 32,
517 NULL
, 0x00000040, NULL
, HFILL
}},
520 "Flood Control Operation", "dcc.floodop", FT_UINT32
, BASE_DEC
,
521 VALS(dcc_floodop_vals
), 0, NULL
, HFILL
}},
524 "Id", "dcc.id", FT_UINT32
, BASE_DEC
,
525 NULL
, 0, NULL
, HFILL
}},
527 { &hf_dcc_last_used
, {
528 "Last Used", "dcc.last_used", FT_UINT32
, BASE_DEC
,
529 NULL
, 0, NULL
, HFILL
}},
531 { &hf_dcc_requests
, {
532 "Requests", "dcc.requests", FT_UINT32
, BASE_DEC
,
533 NULL
, 0, NULL
, HFILL
}},
536 "Addr", "dcc.addr", FT_BYTES
, BASE_NONE
,
537 NULL
, 0, NULL
, HFILL
}},
540 "Pad", "dcc.pad", FT_BYTES
, BASE_NONE
,
541 NULL
, 0, NULL
, HFILL
}},
544 "Unused", "dcc.unused", FT_BYTES
, BASE_NONE
,
545 NULL
, 0, NULL
, HFILL
}},
548 static int *ett
[] = {
556 static ei_register_info ei
[] = {
557 { &ei_dcc_len
, { "dcc.len.short", PI_MALFORMED
, PI_ERROR
, "Error - packet is shorter than header claims!", EXPFILL
}},
560 expert_module_t
* expert_dcc
;
562 proto_dcc
= proto_register_protocol("Distributed Checksum Clearinghouse protocol", "DCC", "dcc");
564 proto_register_field_array(proto_dcc
, hf
, array_length(hf
));
565 proto_register_subtree_array(ett
, array_length(ett
));
566 expert_dcc
= expert_register_protocol(proto_dcc
);
567 expert_register_field_array(expert_dcc
, ei
, array_length(ei
));
571 proto_reg_handoff_dcc(void)
573 heur_dissector_add("udp", dissect_dcc
, "Distributed Checksum Clearinghouse over UDP", "dcc_udp", proto_dcc
, HEURISTIC_ENABLE
);
577 * Editor modelines - https://www.wireshark.org/tools/modelines.html
582 * indent-tabs-mode: t
585 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
586 * :indentSize=8:tabSize=8:noTabs=false: