From Graeme Hewson:
[obnox/wireshark/wip.git] / packet-xdmcp.c
1 /* packet-xdmcp.c
2  * Routines for XDMCP message dissection
3  * Copyright 2002, Pasi Eronen <pasi.eronen@nixu.com>
4  *
5  * $Id: packet-xdmcp.c,v 1.5 2004/02/25 09:31:07 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  *
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.
20  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33
34 #include <string.h>
35 #include <glib.h>
36 #include <epan/packet.h>
37
38 #define UDP_PORT_XDMCP 177
39
40 #define XDMCP_PROTOCOL_VERSION 1
41
42 #define XDMCP_BROADCAST_QUERY 1
43 #define XDMCP_QUERY 2
44 #define XDMCP_INDIRECT_QUERY 3
45 #define XDMCP_FORWARD_QUERY 4
46 #define XDMCP_WILLING 5
47 #define XDMCP_UNWILLING 6
48 #define XDMCP_REQUEST 7
49 #define XDMCP_ACCEPT 8
50 #define XDMCP_DECLINE 9
51 #define XDMCP_MANAGE 10
52 #define XDMCP_REFUSE 11
53 #define XDMCP_FAILED 12
54 #define XDMCP_KEEPALIVE 13
55 #define XDMCP_ALIVE 14
56
57 static const value_string opcode_vals[] = {
58   { XDMCP_BROADCAST_QUERY, "Broadcast_query" },
59   { XDMCP_QUERY, "Query" },
60   { XDMCP_INDIRECT_QUERY, "Indirect_query" },
61   { XDMCP_FORWARD_QUERY, "Forward_query" },
62   { XDMCP_WILLING, "Willing" },
63   { XDMCP_UNWILLING, "Unwilling" },
64   { XDMCP_REQUEST, "Request" },
65   { XDMCP_ACCEPT, "Accept "},
66   { XDMCP_DECLINE, "Decline" },
67   { XDMCP_MANAGE, "Manage" },
68   { XDMCP_REFUSE, "Refuse" },
69   { XDMCP_FAILED, "Failed" },
70   { XDMCP_KEEPALIVE, "Keepalive" },
71   { XDMCP_ALIVE, "Alive" },
72   { 0, NULL }
73 };
74
75 /* Copied from packet-x11.c */
76 static const value_string family_vals[] = {
77   { 0, "Internet" },
78   { 1, "DECnet" },
79   { 2, "Chaos" },
80   { 0, NULL }
81 };
82
83 static gint proto_xdmcp = -1;
84 static gint hf_xdmcp_version = -1;
85 static gint hf_xdmcp_opcode = -1;
86 static gint hf_xdmcp_length = -1;
87 static gint hf_xdmcp_authentication_name = -1;
88 static gint hf_xdmcp_authorization_name = -1;
89 static gint hf_xdmcp_hostname = -1;
90 static gint hf_xdmcp_status = -1;
91 static gint hf_xdmcp_session_id = -1;
92 static gint hf_xdmcp_display_number = -1;
93
94 static gint ett_xdmcp = -1;
95 static gint ett_xdmcp_authorization_names = -1;
96 static gint ett_xdmcp_connections = -1;
97 static gint ett_xdmcp_connection = -1;
98
99 /* Copied from packet-x11.c */
100 static void stringCopy(char *dest, const char *source, int length)
101 {
102   guchar c;
103   while(length--) {
104     c = *source++;
105     if (!isgraph(c) && c != ' ') c = '.';
106     *dest++ = c;
107   }
108   *dest++ = '\0';
109 }
110
111 static gint xdmcp_add_string(proto_tree *tree, gint hf,
112                              tvbuff_t *tvb, gint offset)
113 {
114   const guint8 *p;
115   char *str;
116   guint len;
117
118   len = tvb_get_ntohs(tvb, offset);
119   p = tvb_get_ptr(tvb, offset+2, len);
120   str = g_malloc(len+1);
121   stringCopy(str, p, len);
122   proto_tree_add_string(tree, hf, tvb, offset, len+2, str);
123   g_free(str);
124
125   return len+2;
126 }
127
128 static gint xdmcp_add_text(proto_tree *tree, const char *text,
129                      tvbuff_t *tvb, gint offset)
130 {
131   const guint8 *p;
132   char *str;
133   guint len;
134
135   len = tvb_get_ntohs(tvb, offset);
136   p = tvb_get_ptr(tvb, offset+2, len);
137   str = g_malloc(len+1);
138   stringCopy(str, p, len);
139   proto_tree_add_text(tree, tvb, offset, len+2, "%s: %s", text, str);
140   g_free(str);
141
142   return len+2;
143 }
144
145 static gint xdmcp_add_bytes(proto_tree *tree, const char *text,
146                      tvbuff_t *tvb, gint offset)
147 {
148   guint len;
149   len = tvb_get_ntohs(tvb, offset);
150   proto_tree_add_text(tree, tvb, offset, len+2,
151                       "%s (%u byte%s)", text, len, plurality(len, "", "s"));
152   return len+2;
153 }
154
155 static gint xdmcp_add_authorization_names(proto_tree *tree,
156                                     tvbuff_t *tvb, gint offset)
157 {
158   proto_tree *anames_tree;
159   proto_item *anames_ti;
160   gint anames_len, anames_start_offset;
161
162   anames_start_offset = offset;
163   anames_len = tvb_get_guint8(tvb, offset);
164   anames_ti = proto_tree_add_text(tree, tvb,
165                                   anames_start_offset, -1,
166                                   "Authorization names (%d)",
167                                   anames_len);
168   anames_tree = proto_item_add_subtree(anames_ti,
169                                        ett_xdmcp_authorization_names);
170
171   anames_len = tvb_get_guint8(tvb, offset);
172   offset++;
173   while (anames_len > 0) {
174     offset += xdmcp_add_string(anames_tree, hf_xdmcp_authorization_name,
175                                tvb, offset);
176     anames_len--;
177   }
178   proto_item_set_len(anames_ti, offset - anames_start_offset);
179   return offset - anames_start_offset;
180 }
181
182 /*
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.
186  */
187
188 static void dissect_xdmcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
189 {
190   gint version = -1, opcode = -1;
191   gint offset = 0;
192   proto_item *ti;
193   proto_tree *xdmcp_tree = 0;
194
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... */
198     return;
199   }
200
201   if (check_col(pinfo->cinfo, COL_PROTOCOL))
202     col_set_str(pinfo->cinfo, COL_PROTOCOL, "XDMCP");
203   if (check_col(pinfo->cinfo, COL_INFO))
204     col_clear(pinfo->cinfo, COL_INFO);
205
206   if (tree) {
207     ti = proto_tree_add_item(tree, proto_xdmcp, tvb, offset, -1, FALSE);
208     xdmcp_tree = proto_item_add_subtree(ti, ett_xdmcp);
209
210     proto_tree_add_uint(xdmcp_tree, hf_xdmcp_version, tvb,
211                         offset, 2, version);
212   }
213   offset += 2;
214
215   opcode = tvb_get_ntohs(tvb, offset);
216   if (tree) {
217     proto_tree_add_uint(xdmcp_tree, hf_xdmcp_opcode, tvb,
218                         offset, 2, opcode);
219   }
220   offset += 2;
221   if (check_col(pinfo->cinfo, COL_INFO)) {
222     col_add_fstr(pinfo->cinfo, COL_INFO, "%s",
223                  val_to_str(opcode, opcode_vals, "Unknown (0x%04x)"));
224
225   }
226
227   if (tree) {
228     proto_tree_add_item(xdmcp_tree, hf_xdmcp_length, tvb,
229                         offset, 2, FALSE);
230     offset += 2;
231
232     switch (opcode) {
233       case XDMCP_FORWARD_QUERY:
234       {
235         gint alen, plen;
236         alen = tvb_get_ntohs(tvb, offset);
237         /* I have never seen anything except IPv4 addresses here,
238          * but in theory the protocol should support other address
239          * families. */
240         if (alen == 4) {
241           proto_tree_add_text(xdmcp_tree, tvb, offset, alen+2,
242                               "Client address: %s",
243                               ip_to_str(tvb_get_ptr(tvb, offset+2, 4)));
244           offset += 6;
245         } else {
246           offset += xdmcp_add_bytes(xdmcp_tree, "Client address", tvb, offset);
247         }
248
249         plen = tvb_get_ntohs(tvb, offset);
250         if (plen == 2) {
251           proto_tree_add_text(xdmcp_tree, tvb, offset, plen+2,
252                               "Client port: %u",
253                               tvb_get_ntohs(tvb, offset+2));
254           offset += 4;
255         } else {
256           offset += xdmcp_add_bytes(xdmcp_tree, "Client port", tvb, offset);
257         }
258       }
259       /* fall-through */
260
261       case XDMCP_BROADCAST_QUERY:
262       case XDMCP_QUERY:
263       case XDMCP_INDIRECT_QUERY:
264         offset += xdmcp_add_authorization_names(xdmcp_tree, tvb, offset);
265         break;
266
267       case XDMCP_WILLING:
268         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authentication_name,
269                                    tvb, offset);
270         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_hostname,
271                                    tvb, offset);
272         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_status,
273                                    tvb, offset);
274         break;
275
276       case XDMCP_UNWILLING:
277         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_hostname,
278                                    tvb, offset);
279         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_status,
280                                    tvb, offset);
281         break;
282
283       case XDMCP_REQUEST:
284       {
285         proto_tree *clist_tree;
286         proto_item *clist_ti;
287         gint ctypes_len, caddrs_len, n;
288         gint ctypes_start_offset, caddrs_offset;
289
290         proto_tree_add_item(xdmcp_tree, hf_xdmcp_display_number, tvb,
291                             offset, 2, FALSE);
292         offset += 2;
293
294         ctypes_len = tvb_get_guint8(tvb, offset);
295         ctypes_start_offset = offset;
296         caddrs_offset = offset + 1 + 2*ctypes_len;
297         caddrs_len = tvb_get_guint8(tvb, caddrs_offset);
298         if (ctypes_len != caddrs_len) {
299           proto_tree_add_text(xdmcp_tree, NULL, 0, 0,
300                               "Error: Connection type/address arrays don't match");
301           return;
302         }
303
304         clist_ti = proto_tree_add_text(xdmcp_tree,
305                                        tvb, ctypes_start_offset, -1,
306                                        "Connections (%d)",
307                                        ctypes_len);
308         clist_tree = proto_item_add_subtree(clist_ti, ett_xdmcp_connections);
309
310         offset++;
311         caddrs_offset++;
312
313         n = 1;
314         while (ctypes_len > 0) {
315           proto_item *connection_ti;
316           proto_tree *connection_tree;
317           const char *ip_string;
318
319           gint alen;
320           gint ctype = tvb_get_ntohs(tvb, offset);
321           offset += 2;
322           alen = tvb_get_ntohs(tvb, caddrs_offset);
323           caddrs_offset += 2;
324
325           if ((ctype == 0) && (alen == 4)) {
326             ip_string = ip_to_str(tvb_get_ptr(tvb, caddrs_offset, 4));
327           } else {
328             ip_string = NULL;
329           }
330
331           connection_ti = proto_tree_add_text(clist_tree, NULL, 0, 0,
332                                               "Connection %d%s%s", n,
333                                               (ip_string ? ": " : ""),
334                                               (ip_string ? ip_string : ""));
335           connection_tree = proto_item_add_subtree(connection_ti,
336                                                    ett_xdmcp_connection);
337
338           proto_tree_add_text(connection_tree, tvb, offset-2, 2,
339                               "Type: %s",
340                               val_to_str(ctype, family_vals,
341                                          "Unknown (0x%04x)"));
342           if ((ctype == 0) && (alen == 4)) {
343             proto_tree_add_text(connection_tree, tvb, caddrs_offset-2, alen+2,
344                                 "Address: %s", ip_string);
345           } else {
346             proto_tree_add_text(connection_tree, tvb, caddrs_offset-2, alen+2,
347                                 "Address: (%u byte%s)", alen,
348                                 plurality(alen, "", "s"));
349           }
350           caddrs_offset += alen;
351           ctypes_len--;
352           n++;
353         }
354         offset = caddrs_offset;
355         proto_item_set_len(clist_ti, offset - ctypes_start_offset);
356
357         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authentication_name,
358                                    tvb, offset);
359         offset += xdmcp_add_bytes(xdmcp_tree, "Authentication data",
360                                   tvb, offset);
361
362         offset += xdmcp_add_authorization_names(xdmcp_tree, tvb, offset);
363
364         offset += xdmcp_add_text(xdmcp_tree, "Manufacturer display ID",
365                                  tvb, offset);
366         break;
367       }
368
369       case XDMCP_ACCEPT:
370         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
371                             offset, 4, FALSE);
372         offset += 4;
373         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authentication_name,
374                                    tvb, offset);
375         offset += xdmcp_add_bytes(xdmcp_tree, "Authentication data",
376                                   tvb, offset);
377         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authorization_name,
378                                    tvb, offset);
379         offset += xdmcp_add_bytes(xdmcp_tree, "Authorization data",
380                                   tvb, offset);
381         break;
382
383       case XDMCP_DECLINE:
384         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_status,
385                                    tvb, offset);
386         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authentication_name,
387                                    tvb, offset);
388         offset += xdmcp_add_bytes(xdmcp_tree, "Authentication data",
389                                   tvb, offset);
390         break;
391
392       case XDMCP_MANAGE:
393         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
394                             offset, 4, FALSE);
395         offset += 4;
396
397         proto_tree_add_item(xdmcp_tree, hf_xdmcp_display_number, tvb,
398                             offset, 2, FALSE);
399         offset += 2;
400
401         offset += xdmcp_add_text(xdmcp_tree, "Display class",
402                                  tvb, offset);
403         break;
404
405       case XDMCP_REFUSE:
406         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
407                             offset, 4, FALSE);
408         offset += 4;
409         break;
410
411       case XDMCP_FAILED:
412         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
413                             offset, 4, FALSE);
414         offset += 4;
415
416         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_status,
417                                    tvb, offset);
418         break;
419
420       case XDMCP_KEEPALIVE:
421         proto_tree_add_item(xdmcp_tree, hf_xdmcp_display_number, tvb,
422                             offset, 2, FALSE);
423         offset += 2;
424
425         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
426                             offset, 4, FALSE);
427         offset += 4;
428         break;
429
430       case XDMCP_ALIVE:
431         proto_tree_add_text(xdmcp_tree, tvb, offset, 1,
432                             "Session running: %s",
433                             (tvb_get_guint8(tvb, offset) ? "Yes" : "No"));
434         offset++;
435
436         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
437                             offset, 4, FALSE);
438         offset += 4;
439         break;
440     }
441   }
442 }
443
444 /* Register the protocol with Ethereal */
445 void proto_register_xdmcp(void)
446 {
447   /* Setup list of header fields */
448   static hf_register_info hf[] = {
449     { &hf_xdmcp_version,
450       { "Version",           "xdmcp.version",
451       FT_UINT16, BASE_DEC, NULL, 0,
452       "Protocol version", HFILL }
453     },
454     { &hf_xdmcp_opcode,
455       { "Opcode",              "xdmcp.opcode",
456       FT_UINT16, BASE_HEX, VALS(opcode_vals), 0,
457       "Opcode", HFILL }
458     },
459     { &hf_xdmcp_length,
460       { "Message length",     "xdmcp.length",
461       FT_UINT16, BASE_DEC, NULL, 0,
462       "Length of the remaining message", HFILL }
463     },
464     { &hf_xdmcp_authentication_name,
465       { "Authentication name",     "xdmcp.authentication_name",
466       FT_STRING, BASE_DEC, NULL, 0,
467       "Authentication name", HFILL }
468     },
469     { &hf_xdmcp_authorization_name,
470       { "Authorization name",     "xdmcp.authorization_name",
471       FT_STRING, BASE_DEC, NULL, 0,
472       "Authorization name", HFILL }
473     },
474     { &hf_xdmcp_hostname,
475       { "Hostname",     "xdmcp.hostname",
476       FT_STRING, BASE_DEC, NULL, 0,
477       "Hostname", HFILL }
478     },
479     { &hf_xdmcp_status,
480       { "Status",     "xdmcp.status",
481       FT_STRING, BASE_DEC, NULL, 0,
482       "Status", HFILL }
483     },
484     { &hf_xdmcp_session_id,
485       { "Session ID",     "xdmcp.session_id",
486       FT_UINT32, BASE_HEX, NULL, 0,
487       "Session identifier", HFILL }
488     },
489     { &hf_xdmcp_display_number,
490       { "Display number",     "xdmcp.display_number",
491       FT_UINT16, BASE_DEC, NULL, 0,
492       "Display number", HFILL }
493     },
494   };
495
496   /* Setup protocol subtree array */
497   static gint *ett[] = {
498     &ett_xdmcp,
499     &ett_xdmcp_authorization_names,
500     &ett_xdmcp_connections,
501     &ett_xdmcp_connection
502   };
503
504   /* Register the protocol name and description */
505   proto_xdmcp = proto_register_protocol("X Display Manager Control Protocol",
506                                         "XDMCP", "xdmcp");
507
508   /* Required function calls to register the header fields and subtrees used */
509   proto_register_field_array(proto_xdmcp, hf, array_length(hf));
510   proto_register_subtree_array(ett, array_length(ett));
511 }
512
513 void
514 proto_reg_handoff_xdmcp(void)
515 {
516   dissector_handle_t xdmcp_handle;
517
518   xdmcp_handle = create_dissector_handle(dissect_xdmcp, proto_xdmcp);
519   dissector_add("udp.port", UDP_PORT_XDMCP, xdmcp_handle);
520 }