2 * Routines for XDMCP message dissection
3 * Copyright 2002, Pasi Eronen <pasi.eronen@nixu.com>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
32 #include <epan/packet.h>
33 #include <epan/to_str.h>
34 #include <epan/expert.h>
36 #define UDP_PORT_XDMCP 177
38 #define XDMCP_PROTOCOL_VERSION 1
40 #define XDMCP_BROADCAST_QUERY 1
42 #define XDMCP_INDIRECT_QUERY 3
43 #define XDMCP_FORWARD_QUERY 4
44 #define XDMCP_WILLING 5
45 #define XDMCP_UNWILLING 6
46 #define XDMCP_REQUEST 7
47 #define XDMCP_ACCEPT 8
48 #define XDMCP_DECLINE 9
49 #define XDMCP_MANAGE 10
50 #define XDMCP_REFUSE 11
51 #define XDMCP_FAILED 12
52 #define XDMCP_KEEPALIVE 13
53 #define XDMCP_ALIVE 14
55 static const value_string opcode_vals
[] = {
56 { XDMCP_BROADCAST_QUERY
, "Broadcast_query" },
57 { XDMCP_QUERY
, "Query" },
58 { XDMCP_INDIRECT_QUERY
, "Indirect_query" },
59 { XDMCP_FORWARD_QUERY
, "Forward_query" },
60 { XDMCP_WILLING
, "Willing" },
61 { XDMCP_UNWILLING
, "Unwilling" },
62 { XDMCP_REQUEST
, "Request" },
63 { XDMCP_ACCEPT
, "Accept "},
64 { XDMCP_DECLINE
, "Decline" },
65 { XDMCP_MANAGE
, "Manage" },
66 { XDMCP_REFUSE
, "Refuse" },
67 { XDMCP_FAILED
, "Failed" },
68 { XDMCP_KEEPALIVE
, "Keepalive" },
69 { XDMCP_ALIVE
, "Alive" },
73 /* Copied from packet-x11.c */
74 static const value_string family_vals
[] = {
82 static gint proto_xdmcp
= -1;
83 static gint hf_xdmcp_version
= -1;
84 static gint hf_xdmcp_opcode
= -1;
85 static gint hf_xdmcp_length
= -1;
86 static gint hf_xdmcp_authentication_name
= -1;
87 static gint hf_xdmcp_authorization_name
= -1;
88 static gint hf_xdmcp_hostname
= -1;
89 static gint hf_xdmcp_status
= -1;
90 static gint hf_xdmcp_session_id
= -1;
91 static gint hf_xdmcp_display_number
= -1;
92 static gint hf_xdmcp_manufacturer_display_id
= -1;
93 static gint hf_xdmcp_manufacturer_display_id_len
= -1;
94 static gint hf_xdmcp_display_class
= -1;
95 static gint hf_xdmcp_display_class_len
= -1;
96 static gint hf_xdmcp_client_address_ipv4
= -1;
97 static gint hf_xdmcp_client_address_ipv6
= -1;
98 static gint hf_xdmcp_client_address_bytes
= -1;
99 static gint hf_xdmcp_client_address_bytes_len
= -1;
100 static gint hf_xdmcp_client_port_u16
= -1;
101 static gint hf_xdmcp_client_port_bytes
= -1;
102 static gint hf_xdmcp_client_port_len
= -1;
103 static gint hf_xdmcp_authentication_data
= -1;
104 static gint hf_xdmcp_authentication_data_len
= -1;
105 static gint hf_xdmcp_authorization_data
= -1;
106 static gint hf_xdmcp_authorization_data_len
= -1;
107 static gint hf_xdmcp_connection_type
= -1;
108 static gint hf_xdmcp_connection_address_ipv4
= -1;
109 static gint hf_xdmcp_connection_address_ipv6
= -1;
110 static gint hf_xdmcp_connection_address_bytes
= -1;
111 static gint hf_xdmcp_session_running
= -1;
113 static gint ett_xdmcp
= -1;
114 static gint ett_xdmcp_authentication_names
= -1;
115 static gint ett_xdmcp_authorization_names
= -1;
116 static gint ett_xdmcp_connections
= -1;
117 static gint ett_xdmcp_connection
= -1;
119 static expert_field ei_xdmcp_conn_address_mismatch
= EI_INIT
;
121 static gint
xdmcp_add_string(proto_tree
*tree
, gint hf
,
122 tvbuff_t
*tvb
, gint offset
)
127 len
= tvb_get_ntohs(tvb
, offset
);
128 str
= tvb_get_string(wmem_packet_scope(), tvb
, offset
+2, len
);
129 proto_tree_add_string(tree
, hf
, tvb
, offset
, len
+2, str
);
134 static gint
xdmcp_add_bytes(proto_tree
*tree
, gint hf_byte
, gint hf_length
,
135 tvbuff_t
*tvb
, gint offset
)
138 len
= tvb_get_ntohs(tvb
, offset
);
140 proto_tree_add_item(tree
, hf_length
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
141 proto_tree_add_item(tree
, hf_byte
, tvb
, offset
, len
+2, ENC_NA
);
145 static gint
xdmcp_add_authentication_names(proto_tree
*tree
,
146 tvbuff_t
*tvb
, gint offset
)
148 proto_tree
*anames_tree
;
149 proto_item
*anames_ti
;
150 gint anames_len
, anames_start_offset
;
152 anames_start_offset
= offset
;
153 anames_len
= tvb_get_guint8(tvb
, offset
);
154 anames_ti
= proto_tree_add_text(tree
, tvb
,
155 anames_start_offset
, -1,
156 "Authentication names (%d)",
158 anames_tree
= proto_item_add_subtree(anames_ti
,
159 ett_xdmcp_authentication_names
);
161 anames_len
= tvb_get_guint8(tvb
, offset
);
163 while (anames_len
> 0) {
164 offset
+= xdmcp_add_string(anames_tree
, hf_xdmcp_authentication_name
,
168 proto_item_set_len(anames_ti
, offset
- anames_start_offset
);
169 return offset
- anames_start_offset
;
172 static gint
xdmcp_add_authorization_names(proto_tree
*tree
,
173 tvbuff_t
*tvb
, gint offset
)
175 proto_tree
*anames_tree
;
176 proto_item
*anames_ti
;
177 gint anames_len
, anames_start_offset
;
179 anames_start_offset
= offset
;
180 anames_len
= tvb_get_guint8(tvb
, offset
);
181 anames_ti
= proto_tree_add_text(tree
, tvb
,
182 anames_start_offset
, -1,
183 "Authorization names (%d)",
185 anames_tree
= proto_item_add_subtree(anames_ti
,
186 ett_xdmcp_authorization_names
);
188 anames_len
= tvb_get_guint8(tvb
, offset
);
190 while (anames_len
> 0) {
191 offset
+= xdmcp_add_string(anames_tree
, hf_xdmcp_authorization_name
,
195 proto_item_set_len(anames_ti
, offset
- anames_start_offset
);
196 return offset
- anames_start_offset
;
200 * I didn't find any documentation for the XDMCP protocol, so
201 * this is reverse-engineered from XFree86 source files
202 * xc/programs/xdm/xdmcp.c and xc/programs/Xserver/os/xdmcp.c.
205 static int dissect_xdmcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
207 gint version
= -1, opcode
= -1;
210 proto_tree
*xdmcp_tree
= 0;
212 version
= tvb_get_ntohs(tvb
, offset
);
213 if (version
!= XDMCP_PROTOCOL_VERSION
) {
214 /* Only version 1 exists, so this probably is not XDMCP at all... */
218 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "XDMCP");
219 col_clear(pinfo
->cinfo
, COL_INFO
);
221 ti
= proto_tree_add_item(tree
, proto_xdmcp
, tvb
, offset
, -1, ENC_NA
);
222 xdmcp_tree
= proto_item_add_subtree(ti
, ett_xdmcp
);
224 proto_tree_add_uint(xdmcp_tree
, hf_xdmcp_version
, tvb
,
229 opcode
= tvb_get_ntohs(tvb
, offset
);
231 proto_tree_add_uint(xdmcp_tree
, hf_xdmcp_opcode
, tvb
,
236 col_add_str(pinfo
->cinfo
, COL_INFO
,
237 val_to_str(opcode
, opcode_vals
, "Unknown (0x%04x)"));
239 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_length
, tvb
,
240 offset
, 2, ENC_BIG_ENDIAN
);
245 case XDMCP_FORWARD_QUERY
:
248 alen
= tvb_get_ntohs(tvb
, offset
);
249 /* I have never seen anything except IPv4 addresses here,
250 * but in theory the protocol should support other address
253 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_client_address_ipv4
, tvb
, offset
+2, alen
, ENC_BIG_ENDIAN
);
255 } else if (alen
== 16) {
256 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_client_address_ipv6
, tvb
, offset
+2, alen
, ENC_BIG_ENDIAN
);
259 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_client_address_bytes
, hf_xdmcp_client_address_bytes_len
,
263 plen
= tvb_get_ntohs(tvb
, offset
);
265 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_client_port_u16
, tvb
, offset
+2, plen
, ENC_BIG_ENDIAN
);
268 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_client_port_bytes
, hf_xdmcp_client_port_len
,
274 case XDMCP_BROADCAST_QUERY
:
276 case XDMCP_INDIRECT_QUERY
:
277 offset
+= xdmcp_add_authentication_names(xdmcp_tree
, tvb
, offset
);
281 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authentication_name
,
283 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_hostname
,
285 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_status
,
289 case XDMCP_UNWILLING
:
290 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_hostname
,
292 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_status
,
298 proto_tree
*clist_tree
;
299 proto_item
*clist_ti
;
300 gint ctypes_len
, caddrs_len
, n
;
301 gint ctypes_start_offset
, caddrs_offset
;
303 ti
= proto_tree_add_item(xdmcp_tree
, hf_xdmcp_display_number
, tvb
,
304 offset
, 2, ENC_BIG_ENDIAN
);
307 ctypes_len
= tvb_get_guint8(tvb
, offset
);
308 ctypes_start_offset
= offset
;
309 caddrs_offset
= offset
+ 1 + 2*ctypes_len
;
310 caddrs_len
= tvb_get_guint8(tvb
, caddrs_offset
);
311 if (ctypes_len
!= caddrs_len
) {
312 expert_add_info(pinfo
, ti
, &ei_xdmcp_conn_address_mismatch
);
316 clist_ti
= proto_tree_add_text(xdmcp_tree
,
317 tvb
, ctypes_start_offset
, -1,
320 clist_tree
= proto_item_add_subtree(clist_ti
, ett_xdmcp_connections
);
326 while (ctypes_len
> 0) {
327 proto_item
*connection_ti
;
328 proto_tree
*connection_tree
;
331 gint ctype
= tvb_get_ntohs(tvb
, offset
);
333 alen
= tvb_get_ntohs(tvb
, caddrs_offset
);
336 connection_ti
= proto_tree_add_text(clist_tree
, NULL
, 0, 0,
338 connection_tree
= proto_item_add_subtree(connection_ti
,
339 ett_xdmcp_connection
);
341 proto_tree_add_item(connection_tree
, hf_xdmcp_connection_type
, tvb
, offset
-2, 2, ENC_BIG_ENDIAN
);
343 if ((ctype
== 0) && (alen
== 4)) {
344 proto_tree_add_item(connection_tree
, hf_xdmcp_connection_address_ipv4
, tvb
, caddrs_offset
, alen
, ENC_BIG_ENDIAN
);
345 proto_item_append_text(connection_ti
, ": %s", tvb_ip_to_str(tvb
, caddrs_offset
));
346 } else if ((ctype
== 6) && (alen
== 16)) {
347 proto_tree_add_item(connection_tree
, hf_xdmcp_connection_address_ipv6
, tvb
, caddrs_offset
, alen
, ENC_BIG_ENDIAN
);
348 proto_item_append_text(connection_ti
, ": %s", tvb_ip6_to_str(tvb
, caddrs_offset
));
350 proto_tree_add_item(connection_tree
, hf_xdmcp_connection_address_bytes
, tvb
, caddrs_offset
, alen
, ENC_NA
);
353 caddrs_offset
+= alen
;
357 offset
= caddrs_offset
;
358 proto_item_set_len(clist_ti
, offset
- ctypes_start_offset
);
360 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authentication_name
,
362 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_authentication_data
, hf_xdmcp_authentication_data_len
,
365 offset
+= xdmcp_add_authorization_names(xdmcp_tree
, tvb
, offset
);
367 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_manufacturer_display_id
, hf_xdmcp_manufacturer_display_id_len
,
373 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
374 offset
, 4, ENC_BIG_ENDIAN
);
376 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authentication_name
,
378 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_authentication_data
, hf_xdmcp_authentication_data_len
,
380 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authorization_name
,
382 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_authorization_data
, hf_xdmcp_authorization_data_len
,
387 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_status
,
389 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_authentication_name
,
391 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_authentication_data
, hf_xdmcp_authentication_data_len
,
396 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
397 offset
, 4, ENC_BIG_ENDIAN
);
400 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_display_number
, tvb
,
401 offset
, 2, ENC_BIG_ENDIAN
);
404 offset
+= xdmcp_add_bytes(xdmcp_tree
, hf_xdmcp_display_class
, hf_xdmcp_display_class_len
,
409 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
410 offset
, 4, ENC_BIG_ENDIAN
);
415 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
416 offset
, 4, ENC_BIG_ENDIAN
);
419 offset
+= xdmcp_add_string(xdmcp_tree
, hf_xdmcp_status
,
423 case XDMCP_KEEPALIVE
:
424 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_display_number
, tvb
,
425 offset
, 2, ENC_BIG_ENDIAN
);
428 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
429 offset
, 4, ENC_BIG_ENDIAN
);
435 guint8 session_running
= tvb_get_guint8(tvb
, offset
);
436 proto_tree_add_uint_format_value(xdmcp_tree
, hf_xdmcp_session_running
, tvb
,
437 offset
, 1, session_running
, "%s", session_running
? "Yes" : "No");
440 proto_tree_add_item(xdmcp_tree
, hf_xdmcp_session_id
, tvb
,
441 offset
, 4, ENC_BIG_ENDIAN
);
452 /* Register the protocol with Wireshark */
453 void proto_register_xdmcp(void)
455 /* Setup list of header fields */
456 static hf_register_info hf
[] = {
458 { "Version", "xdmcp.version",
459 FT_UINT16
, BASE_DEC
, NULL
, 0,
460 "Protocol version", HFILL
}
463 { "Opcode", "xdmcp.opcode",
464 FT_UINT16
, BASE_HEX
, VALS(opcode_vals
), 0,
468 { "Message length", "xdmcp.length",
469 FT_UINT16
, BASE_DEC
, NULL
, 0,
470 "Length of the remaining message", HFILL
}
472 { &hf_xdmcp_authentication_name
,
473 { "Authentication name", "xdmcp.authentication_name",
474 FT_STRING
, BASE_NONE
, NULL
, 0,
477 { &hf_xdmcp_authorization_name
,
478 { "Authorization name", "xdmcp.authorization_name",
479 FT_STRING
, BASE_NONE
, NULL
, 0,
482 { &hf_xdmcp_hostname
,
483 { "Hostname", "xdmcp.hostname",
484 FT_STRING
, BASE_NONE
, NULL
, 0,
488 { "Status", "xdmcp.status",
489 FT_STRING
, BASE_NONE
, NULL
, 0,
492 { &hf_xdmcp_session_id
,
493 { "Session ID", "xdmcp.session_id",
494 FT_UINT32
, BASE_HEX
, NULL
, 0,
495 "Session identifier", HFILL
}
497 { &hf_xdmcp_display_number
,
498 { "Display number", "xdmcp.display_number",
499 FT_UINT16
, BASE_DEC
, NULL
, 0,
502 { &hf_xdmcp_manufacturer_display_id_len
,
503 { "Manufacturer display ID Length", "xdmcp.manufacturer_display_id_len",
504 FT_UINT16
, BASE_DEC
, NULL
, 0,
507 { &hf_xdmcp_manufacturer_display_id
,
508 { "Manufacturer display ID", "xdmcp.manufacturer_display_id",
509 FT_BYTES
, BASE_NONE
, NULL
, 0,
512 { &hf_xdmcp_display_class_len
,
513 { "Display class Length", "xdmcp.display_class_len",
514 FT_UINT16
, BASE_DEC
, NULL
, 0,
517 { &hf_xdmcp_display_class
,
518 { "Display class", "xdmcp.display_class",
519 FT_BYTES
, BASE_NONE
, NULL
, 0,
522 /* XXX - the following 3 could be the same filter, but mixed types of the same filter seem to cause issues */
523 { &hf_xdmcp_client_address_ipv4
,
524 { "Client Address", "xdmcp.client_address_ipv4",
525 FT_IPv4
, BASE_NONE
, NULL
, 0,
528 { &hf_xdmcp_client_address_ipv6
,
529 { "Client Address", "xdmcp.client_address_ipv6",
530 FT_IPv6
, BASE_NONE
, NULL
, 0,
533 { &hf_xdmcp_client_address_bytes
,
534 { "Client Address", "xdmcp.client_address_bytes",
535 FT_BYTES
, BASE_NONE
, NULL
, 0,
538 { &hf_xdmcp_client_address_bytes_len
,
539 { "Client Address Length", "xdmcp.client_address_bytes_len",
540 FT_UINT16
, BASE_DEC
, NULL
, 0,
543 { &hf_xdmcp_client_port_len
,
544 { "Client port Length", "xdmcp.client_port_len",
545 FT_UINT16
, BASE_DEC
, NULL
, 0,
548 { &hf_xdmcp_client_port_bytes
,
549 { "Client port", "xdmcp.client_port_bytes",
550 FT_BYTES
, BASE_NONE
, NULL
, 0,
553 { &hf_xdmcp_client_port_u16
,
554 { "Client port", "xdmcp.client_port",
555 FT_UINT16
, BASE_DEC
, NULL
, 0,
558 { &hf_xdmcp_authentication_data_len
,
559 { "Authentication data Length", "xdmcp.authentication_data_len",
560 FT_UINT16
, BASE_DEC
, NULL
, 0,
563 { &hf_xdmcp_authentication_data
,
564 { "Authentication data", "xdmcp.authentication_data",
565 FT_BYTES
, BASE_NONE
, NULL
, 0,
568 { &hf_xdmcp_authorization_data_len
,
569 { "Authorization data Length", "xdmcp.authorization_data_len",
570 FT_UINT16
, BASE_DEC
, NULL
, 0,
573 { &hf_xdmcp_authorization_data
,
574 { "Authorization data", "xdmcp.authorization_data",
575 FT_BYTES
, BASE_NONE
, NULL
, 0,
578 /* XXX - the following 3 could be the same filter, but mixed types of the same filter seem to cause issues */
579 { &hf_xdmcp_connection_address_ipv4
,
580 { "Address", "xdmcp.connection_address_ipv4",
581 FT_IPv4
, BASE_NONE
, NULL
, 0,
584 { &hf_xdmcp_connection_address_ipv6
,
585 { "Address", "xdmcp.connection_address_ipv6",
586 FT_IPv6
, BASE_NONE
, NULL
, 0,
589 { &hf_xdmcp_connection_address_bytes
,
590 { "Address", "xdmcp.connection_address_bytes",
591 FT_BYTES
, BASE_NONE
, NULL
, 0,
594 { &hf_xdmcp_connection_type
,
595 { "Type", "xdmcp.connection_type",
596 FT_UINT16
, BASE_HEX
, VALS(family_vals
), 0,
599 { &hf_xdmcp_session_running
,
600 { "Session running", "xdmcp.session_running",
601 FT_UINT8
, BASE_DEC
, NULL
, 0,
607 /* Setup protocol subtree array */
608 static gint
*ett
[] = {
610 &ett_xdmcp_authentication_names
,
611 &ett_xdmcp_authorization_names
,
612 &ett_xdmcp_connections
,
613 &ett_xdmcp_connection
616 static ei_register_info ei
[] = {
617 { &ei_xdmcp_conn_address_mismatch
, { "xdmcp.conn_address_mismatch", PI_PROTOCOL
, PI_WARN
, "Error: Connection type/address arrays don't match", EXPFILL
}},
620 expert_module_t
* expert_xdmcp
;
622 /* Register the protocol name and description */
623 proto_xdmcp
= proto_register_protocol("X Display Manager Control Protocol",
626 /* Required function calls to register the header fields and subtrees used */
627 proto_register_field_array(proto_xdmcp
, hf
, array_length(hf
));
628 proto_register_subtree_array(ett
, array_length(ett
));
629 expert_xdmcp
= expert_register_protocol(proto_xdmcp
);
630 expert_register_field_array(expert_xdmcp
, ei
, array_length(ei
));
634 proto_reg_handoff_xdmcp(void)
636 dissector_handle_t xdmcp_handle
;
638 xdmcp_handle
= new_create_dissector_handle(dissect_xdmcp
, proto_xdmcp
);
639 dissector_add_uint("udp.port", UDP_PORT_XDMCP
, xdmcp_handle
);
647 * indent-tabs-mode: nil
650 * ex: set shiftwidth=2 tabstop=8 expandtab:
651 * :indentSize=2:tabSize=8:noTabs=true: