2 * Routines for XDMCP message dissection
3 * Copyright 2002, Pasi Eronen <pasi.eronen@nixu.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include <epan/packet.h>
15 #include <epan/to_str.h>
16 #include <epan/expert.h>
18 #define UDP_PORT_XDMCP 177
20 #define XDMCP_PROTOCOL_VERSION 1
22 #define XDMCP_BROADCAST_QUERY 1
24 #define XDMCP_INDIRECT_QUERY 3
25 #define XDMCP_FORWARD_QUERY 4
26 #define XDMCP_WILLING 5
27 #define XDMCP_UNWILLING 6
28 #define XDMCP_REQUEST 7
29 #define XDMCP_ACCEPT 8
30 #define XDMCP_DECLINE 9
31 #define XDMCP_MANAGE 10
32 #define XDMCP_REFUSE 11
33 #define XDMCP_FAILED 12
34 #define XDMCP_KEEPALIVE 13
35 #define XDMCP_ALIVE 14
37 void proto_register_xdmcp(void);
38 void proto_reg_handoff_xdmcp(void);
40 static dissector_handle_t xdmcp_handle
;
42 static const value_string opcode_vals
[] = {
43 { XDMCP_BROADCAST_QUERY
, "Broadcast_query" },
44 { XDMCP_QUERY
, "Query" },
45 { XDMCP_INDIRECT_QUERY
, "Indirect_query" },
46 { XDMCP_FORWARD_QUERY
, "Forward_query" },
47 { XDMCP_WILLING
, "Willing" },
48 { XDMCP_UNWILLING
, "Unwilling" },
49 { XDMCP_REQUEST
, "Request" },
50 { XDMCP_ACCEPT
, "Accept "},
51 { XDMCP_DECLINE
, "Decline" },
52 { XDMCP_MANAGE
, "Manage" },
53 { XDMCP_REFUSE
, "Refuse" },
54 { XDMCP_FAILED
, "Failed" },
55 { XDMCP_KEEPALIVE
, "Keepalive" },
56 { XDMCP_ALIVE
, "Alive" },
60 /* Copied from packet-x11.c */
61 static const value_string family_vals
[] = {
69 static int proto_xdmcp
;
70 static int hf_xdmcp_version
;
71 static int hf_xdmcp_opcode
;
72 static int hf_xdmcp_length
;
73 static int hf_xdmcp_authentication_name
;
74 static int hf_xdmcp_authorization_name
;
75 static int hf_xdmcp_hostname
;
76 static int hf_xdmcp_status
;
77 static int hf_xdmcp_session_id
;
78 static int hf_xdmcp_display_number
;
79 static int hf_xdmcp_manufacturer_display_id
;
80 static int hf_xdmcp_manufacturer_display_id_len
;
81 static int hf_xdmcp_display_class
;
82 static int hf_xdmcp_display_class_len
;
83 static int hf_xdmcp_client_address_ipv4
;
84 static int hf_xdmcp_client_address_ipv6
;
85 static int hf_xdmcp_client_address_bytes
;
86 static int hf_xdmcp_client_address_bytes_len
;
87 static int hf_xdmcp_client_port_u16
;
88 static int hf_xdmcp_client_port_bytes
;
89 static int hf_xdmcp_client_port_len
;
90 static int hf_xdmcp_authentication_data
;
91 static int hf_xdmcp_authentication_data_len
;
92 static int hf_xdmcp_authorization_data
;
93 static int hf_xdmcp_authorization_data_len
;
94 static int hf_xdmcp_connection_type
;
95 static int hf_xdmcp_connection_address_ipv4
;
96 static int hf_xdmcp_connection_address_ipv6
;
97 static int hf_xdmcp_connection_address_bytes
;
98 static int hf_xdmcp_session_running
;
100 static int ett_xdmcp
;
101 static int ett_xdmcp_authentication_names
;
102 static int ett_xdmcp_authorization_names
;
103 static int ett_xdmcp_connections
;
104 static int ett_xdmcp_connection
;
106 static expert_field ei_xdmcp_conn_address_mismatch
;
108 static int xdmcp_add_string(proto_tree
*tree
, int hf
,
109 tvbuff_t
*tvb
, int offset
)
114 len
= tvb_get_ntohs(tvb
, offset
);
115 str
= tvb_get_string_enc(wmem_packet_scope(), tvb
, offset
+2, len
, ENC_ASCII
);
116 proto_tree_add_string(tree
, hf
, tvb
, offset
, len
+2, str
);
121 static int xdmcp_add_bytes(proto_tree
*tree
, int hf_byte
, int hf_length
,
122 tvbuff_t
*tvb
, int offset
)
125 len
= tvb_get_ntohs(tvb
, offset
);
127 proto_tree_add_item(tree
, hf_length
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
128 proto_tree_add_item(tree
, hf_byte
, tvb
, offset
, len
+2, ENC_NA
);
132 static int xdmcp_add_authentication_names(proto_tree
*tree
,
133 tvbuff_t
*tvb
, int offset
)
135 proto_tree
*anames_tree
;
136 proto_item
*anames_ti
;
137 int anames_len
, anames_start_offset
;
139 anames_start_offset
= offset
;
140 anames_len
= tvb_get_uint8(tvb
, offset
);
141 anames_tree
= proto_tree_add_subtree_format(tree
, tvb
,
142 anames_start_offset
, -1,
143 ett_xdmcp_authentication_names
, &anames_ti
, "Authentication names (%d)",
146 anames_len
= tvb_get_uint8(tvb
, offset
);
148 while (anames_len
> 0) {
149 offset
+= xdmcp_add_string(anames_tree
, hf_xdmcp_authentication_name
,
153 proto_item_set_len(anames_ti
, offset
- anames_start_offset
);
154 return offset
- anames_start_offset
;
157 static int xdmcp_add_authorization_names(proto_tree
*tree
,
158 tvbuff_t
*tvb
, int offset
)
160 proto_tree
*anames_tree
;
161 proto_item
*anames_ti
;
162 int anames_len
, anames_start_offset
;
164 anames_start_offset
= offset
;
165 anames_len
= tvb_get_uint8(tvb
, offset
);
166 anames_tree
= proto_tree_add_subtree_format(tree
, tvb
,
167 anames_start_offset
, -1,
168 ett_xdmcp_authorization_names
, &anames_ti
, "Authorization names (%d)",
171 anames_len
= tvb_get_uint8(tvb
, offset
);
173 while (anames_len
> 0) {
174 offset
+= xdmcp_add_string(anames_tree
, hf_xdmcp_authorization_name
,
178 proto_item_set_len(anames_ti
, offset
- anames_start_offset
);
179 return offset
- anames_start_offset
;
183 * I didn't find any documentation for the XDMCP protocol, so
184 * this is reverse-engineered from XFree86 source files
185 * xc/programs/xdm/xdmcp.c and xc/programs/Xserver/os/xdmcp.c.
188 static int dissect_xdmcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
190 int version
= -1, opcode
= -1;
193 proto_tree
*xdmcp_tree
= 0;
195 version
= tvb_get_ntohs(tvb
, offset
);
196 if (version
!= XDMCP_PROTOCOL_VERSION
) {
197 /* Only version 1 exists, so this probably is not XDMCP at all... */
201 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "XDMCP");
202 col_clear(pinfo
->cinfo
, COL_INFO
);
204 ti
= proto_tree_add_item(tree
, proto_xdmcp
, tvb
, offset
, -1, ENC_NA
);
205 xdmcp_tree
= proto_item_add_subtree(ti
, ett_xdmcp
);
207 proto_tree_add_uint(xdmcp_tree
, hf_xdmcp_version
, tvb
,
212 opcode
= tvb_get_ntohs(tvb
, offset
);
214 proto_tree_add_uint(xdmcp_tree
, hf_xdmcp_opcode
, tvb
,
219 col_add_str(pinfo
->cinfo
, COL_INFO
,
220 val_to_str(opcode
, opcode_vals
, "Unknown (0x%04x)"));
222 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_length
, tvb
,
223 offset
, 2, ENC_BIG_ENDIAN
);
228 case XDMCP_FORWARD_QUERY
:
231 alen
= tvb_get_ntohs(tvb
, offset
);
232 /* I have never seen anything except IPv4 addresses here,
233 * but in theory the protocol should support other address
236 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_client_address_ipv4
, tvb
, offset
+2, alen
, ENC_BIG_ENDIAN
);
238 } else if (alen
== 16) {
239 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_client_address_ipv6
, tvb
, offset
+2, alen
, ENC_NA
);
242 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_client_address_bytes
, hf_xdmcp_client_address_bytes_len
,
246 plen
= tvb_get_ntohs(tvb
, offset
);
248 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_client_port_u16
, tvb
, offset
+2, plen
, ENC_BIG_ENDIAN
);
251 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_client_port_bytes
, hf_xdmcp_client_port_len
,
257 case XDMCP_BROADCAST_QUERY
:
259 case XDMCP_INDIRECT_QUERY
:
260 offset
+= xdmcp_add_authentication_names(xdmcp_tree
, tvb
, offset
);
264 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authentication_name
,
266 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_hostname
,
268 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_status
,
272 case XDMCP_UNWILLING
:
273 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_hostname
,
275 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_status
,
281 proto_tree
*clist_tree
;
282 proto_item
*clist_ti
;
283 int ctypes_len
, caddrs_len
, n
;
284 int ctypes_start_offset
, caddrs_offset
;
286 ti
= proto_tree_add_item(xdmcp_tree
, hf_xdmcp_display_number
, tvb
,
287 offset
, 2, ENC_BIG_ENDIAN
);
290 ctypes_len
= tvb_get_uint8(tvb
, offset
);
291 ctypes_start_offset
= offset
;
292 caddrs_offset
= offset
+ 1 + 2*ctypes_len
;
293 caddrs_len
= tvb_get_uint8(tvb
, caddrs_offset
);
294 if (ctypes_len
!= caddrs_len
) {
295 expert_add_info(pinfo
, ti
, &ei_xdmcp_conn_address_mismatch
);
299 clist_tree
= proto_tree_add_subtree_format(xdmcp_tree
,
300 tvb
, ctypes_start_offset
, -1,
301 ett_xdmcp_connections
, &clist_ti
, "Connections (%d)",
308 while (ctypes_len
> 0) {
309 proto_item
*connection_ti
;
310 proto_tree
*connection_tree
;
313 int ctype
= tvb_get_ntohs(tvb
, offset
);
315 alen
= tvb_get_ntohs(tvb
, caddrs_offset
);
318 connection_tree
= proto_tree_add_subtree_format(clist_tree
, tvb
, 0, 0,
319 ett_xdmcp_connection
, &connection_ti
, "Connection %d", n
);
321 proto_tree_add_item(connection_tree
, hf_xdmcp_connection_type
, tvb
, offset
-2, 2, ENC_BIG_ENDIAN
);
323 if ((ctype
== 0) && (alen
== 4)) {
324 proto_tree_add_item(connection_tree
, hf_xdmcp_connection_address_ipv4
, tvb
, caddrs_offset
, alen
, ENC_BIG_ENDIAN
);
325 proto_item_append_text(connection_ti
, ": %s", tvb_ip_to_str(pinfo
->pool
, tvb
, caddrs_offset
));
326 } else if ((ctype
== 6) && (alen
== 16)) {
327 proto_tree_add_item(connection_tree
, hf_xdmcp_connection_address_ipv6
, tvb
, caddrs_offset
, alen
, ENC_NA
);
328 proto_item_append_text(connection_ti
, ": %s", tvb_ip6_to_str(pinfo
->pool
, tvb
, caddrs_offset
));
330 proto_tree_add_item(connection_tree
, hf_xdmcp_connection_address_bytes
, tvb
, caddrs_offset
, alen
, ENC_NA
);
333 caddrs_offset
+= alen
;
337 offset
= caddrs_offset
;
338 proto_item_set_len(clist_ti
, offset
- ctypes_start_offset
);
340 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authentication_name
,
342 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_authentication_data
, hf_xdmcp_authentication_data_len
,
345 offset
+= xdmcp_add_authorization_names(xdmcp_tree
, tvb
, offset
);
347 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_manufacturer_display_id
, hf_xdmcp_manufacturer_display_id_len
,
353 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
354 offset
, 4, ENC_BIG_ENDIAN
);
356 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authentication_name
,
358 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_authentication_data
, hf_xdmcp_authentication_data_len
,
360 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authorization_name
,
362 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_authorization_data
, hf_xdmcp_authorization_data_len
,
367 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_status
,
369 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authentication_name
,
371 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_authentication_data
, hf_xdmcp_authentication_data_len
,
376 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
377 offset
, 4, ENC_BIG_ENDIAN
);
380 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_display_number
, tvb
,
381 offset
, 2, ENC_BIG_ENDIAN
);
384 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_display_class
, hf_xdmcp_display_class_len
,
389 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
390 offset
, 4, ENC_BIG_ENDIAN
);
395 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
396 offset
, 4, ENC_BIG_ENDIAN
);
399 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_status
,
403 case XDMCP_KEEPALIVE
:
404 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_display_number
, tvb
,
405 offset
, 2, ENC_BIG_ENDIAN
);
408 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
409 offset
, 4, ENC_BIG_ENDIAN
);
415 uint8_t session_running
= tvb_get_uint8(tvb
, offset
);
416 proto_tree_add_uint_format_value(xdmcp_tree
, hf_xdmcp_session_running
, tvb
,
417 offset
, 1, session_running
, "%s", session_running
? "Yes" : "No");
420 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
421 offset
, 4, ENC_BIG_ENDIAN
);
432 /* Register the protocol with Wireshark */
433 void proto_register_xdmcp(void)
435 /* Setup list of header fields */
436 static hf_register_info hf
[] = {
438 { "Version", "xdmcp.version",
439 FT_UINT16
, BASE_DEC
, NULL
, 0,
440 "Protocol version", HFILL
}
443 { "Opcode", "xdmcp.opcode",
444 FT_UINT16
, BASE_HEX
, VALS(opcode_vals
), 0,
448 { "Message length", "xdmcp.length",
449 FT_UINT16
, BASE_DEC
, NULL
, 0,
450 "Length of the remaining message", HFILL
}
452 { &hf_xdmcp_authentication_name
,
453 { "Authentication name", "xdmcp.authentication_name",
454 FT_STRING
, BASE_NONE
, NULL
, 0,
457 { &hf_xdmcp_authorization_name
,
458 { "Authorization name", "xdmcp.authorization_name",
459 FT_STRING
, BASE_NONE
, NULL
, 0,
462 { &hf_xdmcp_hostname
,
463 { "Hostname", "xdmcp.hostname",
464 FT_STRING
, BASE_NONE
, NULL
, 0,
468 { "Status", "xdmcp.status",
469 FT_STRING
, BASE_NONE
, NULL
, 0,
472 { &hf_xdmcp_session_id
,
473 { "Session ID", "xdmcp.session_id",
474 FT_UINT32
, BASE_HEX
, NULL
, 0,
475 "Session identifier", HFILL
}
477 { &hf_xdmcp_display_number
,
478 { "Display number", "xdmcp.display_number",
479 FT_UINT16
, BASE_DEC
, NULL
, 0,
482 { &hf_xdmcp_manufacturer_display_id_len
,
483 { "Manufacturer display ID Length", "xdmcp.manufacturer_display_id_len",
484 FT_UINT16
, BASE_DEC
, NULL
, 0,
487 { &hf_xdmcp_manufacturer_display_id
,
488 { "Manufacturer display ID", "xdmcp.manufacturer_display_id",
489 FT_BYTES
, BASE_NONE
, NULL
, 0,
492 { &hf_xdmcp_display_class_len
,
493 { "Display class Length", "xdmcp.display_class_len",
494 FT_UINT16
, BASE_DEC
, NULL
, 0,
497 { &hf_xdmcp_display_class
,
498 { "Display class", "xdmcp.display_class",
499 FT_BYTES
, BASE_NONE
, NULL
, 0,
502 /* XXX - the following 3 could be the same filter, but mixed types of the same filter seem to cause issues */
503 { &hf_xdmcp_client_address_ipv4
,
504 { "Client Address", "xdmcp.client_address_ipv4",
505 FT_IPv4
, BASE_NONE
, NULL
, 0,
508 { &hf_xdmcp_client_address_ipv6
,
509 { "Client Address", "xdmcp.client_address_ipv6",
510 FT_IPv6
, BASE_NONE
, NULL
, 0,
513 { &hf_xdmcp_client_address_bytes
,
514 { "Client Address", "xdmcp.client_address_bytes",
515 FT_BYTES
, BASE_NONE
, NULL
, 0,
518 { &hf_xdmcp_client_address_bytes_len
,
519 { "Client Address Length", "xdmcp.client_address_bytes_len",
520 FT_UINT16
, BASE_DEC
, NULL
, 0,
523 { &hf_xdmcp_client_port_len
,
524 { "Client port Length", "xdmcp.client_port_len",
525 FT_UINT16
, BASE_DEC
, NULL
, 0,
528 { &hf_xdmcp_client_port_bytes
,
529 { "Client port", "xdmcp.client_port_bytes",
530 FT_BYTES
, BASE_NONE
, NULL
, 0,
533 { &hf_xdmcp_client_port_u16
,
534 { "Client port", "xdmcp.client_port",
535 FT_UINT16
, BASE_DEC
, NULL
, 0,
538 { &hf_xdmcp_authentication_data_len
,
539 { "Authentication data Length", "xdmcp.authentication_data_len",
540 FT_UINT16
, BASE_DEC
, NULL
, 0,
543 { &hf_xdmcp_authentication_data
,
544 { "Authentication data", "xdmcp.authentication_data",
545 FT_BYTES
, BASE_NONE
, NULL
, 0,
548 { &hf_xdmcp_authorization_data_len
,
549 { "Authorization data Length", "xdmcp.authorization_data_len",
550 FT_UINT16
, BASE_DEC
, NULL
, 0,
553 { &hf_xdmcp_authorization_data
,
554 { "Authorization data", "xdmcp.authorization_data",
555 FT_BYTES
, BASE_NONE
, NULL
, 0,
558 /* XXX - the following 3 could be the same filter, but mixed types of the same filter seem to cause issues */
559 { &hf_xdmcp_connection_address_ipv4
,
560 { "Address", "xdmcp.connection_address_ipv4",
561 FT_IPv4
, BASE_NONE
, NULL
, 0,
564 { &hf_xdmcp_connection_address_ipv6
,
565 { "Address", "xdmcp.connection_address_ipv6",
566 FT_IPv6
, BASE_NONE
, NULL
, 0,
569 { &hf_xdmcp_connection_address_bytes
,
570 { "Address", "xdmcp.connection_address_bytes",
571 FT_BYTES
, BASE_NONE
, NULL
, 0,
574 { &hf_xdmcp_connection_type
,
575 { "Type", "xdmcp.connection_type",
576 FT_UINT16
, BASE_HEX
, VALS(family_vals
), 0,
579 { &hf_xdmcp_session_running
,
580 { "Session running", "xdmcp.session_running",
581 FT_UINT8
, BASE_DEC
, NULL
, 0,
587 /* Setup protocol subtree array */
588 static int *ett
[] = {
590 &ett_xdmcp_authentication_names
,
591 &ett_xdmcp_authorization_names
,
592 &ett_xdmcp_connections
,
593 &ett_xdmcp_connection
596 static ei_register_info ei
[] = {
597 { &ei_xdmcp_conn_address_mismatch
, { "xdmcp.conn_address_mismatch", PI_PROTOCOL
, PI_WARN
, "Error: Connection type/address arrays don't match", EXPFILL
}},
600 expert_module_t
* expert_xdmcp
;
602 /* Register the protocol name and description */
603 proto_xdmcp
= proto_register_protocol("X Display Manager Control Protocol", "XDMCP", "xdmcp");
605 /* Required function calls to register the header fields and subtrees used */
606 proto_register_field_array(proto_xdmcp
, hf
, array_length(hf
));
607 proto_register_subtree_array(ett
, array_length(ett
));
608 expert_xdmcp
= expert_register_protocol(proto_xdmcp
);
609 expert_register_field_array(expert_xdmcp
, ei
, array_length(ei
));
611 /* Register the dissector handle */
612 xdmcp_handle
= register_dissector("xdmcp", dissect_xdmcp
, proto_xdmcp
);
616 proto_reg_handoff_xdmcp(void)
618 dissector_add_uint_with_preference("udp.port", UDP_PORT_XDMCP
, xdmcp_handle
);
626 * indent-tabs-mode: nil
629 * ex: set shiftwidth=2 tabstop=8 expandtab:
630 * :indentSize=2:tabSize=8:noTabs=true: