9192d1d82304388e617051f15f4561e5f0509a5c
[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.3 2002/08/28 21:00:40 jmayer 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   char *str;
115   guint len;
116
117   len = tvb_get_ntohs(tvb, offset);
118   str = g_malloc(len+1);
119   stringCopy(str, tvb_get_ptr(tvb, offset+2, len), len);
120   proto_tree_add_string(tree, hf, tvb, offset, len+2, str);
121   g_free(str);
122
123   return len+2;
124 }
125
126 static gint xdmcp_add_text(proto_tree *tree, const char *text,
127                      tvbuff_t *tvb, gint offset)
128 {
129   char *str;
130   guint len;
131
132   len = tvb_get_ntohs(tvb, offset);
133   str = g_malloc(len+1);
134   stringCopy(str, tvb_get_ptr(tvb, offset+2, len), len);
135   proto_tree_add_text(tree, tvb, offset, len+2, "%s: %s", text, str);
136   g_free(str);
137
138   return len+2;
139 }
140
141 static gint xdmcp_add_bytes(proto_tree *tree, const char *text,
142                      tvbuff_t *tvb, gint offset)
143 {
144   guint len;
145   len = tvb_get_ntohs(tvb, offset);
146   proto_tree_add_text(tree, tvb, offset, len+2,
147                       "%s (%u byte%s)", text, len, plurality(len, "", "s"));
148   return len+2;
149 }
150
151 static gint xdmcp_add_authorization_names(proto_tree *tree,
152                                     tvbuff_t *tvb, gint offset)
153 {
154   proto_tree *anames_tree;
155   proto_item *anames_ti;
156   gint anames_len, anames_start_offset;
157
158   anames_start_offset = offset;
159   anames_len = tvb_get_guint8(tvb, offset);
160   anames_ti = proto_tree_add_text(tree, tvb,
161                                   anames_start_offset, -1,
162                                   "Authorization names (%d)",
163                                   anames_len);
164   anames_tree = proto_item_add_subtree(anames_ti,
165                                        ett_xdmcp_authorization_names);
166
167   anames_len = tvb_get_guint8(tvb, offset);
168   offset++;
169   while (anames_len > 0) {
170     offset += xdmcp_add_string(anames_tree, hf_xdmcp_authorization_name,
171                                tvb, offset);
172     anames_len--;
173   }
174   proto_item_set_len(anames_ti, offset - anames_start_offset);
175   return offset - anames_start_offset;
176 }
177
178 /*
179  * I didn't find any documentation for the XDMCP protocol, so
180  * this is reverse-engineered from XFree86 source files
181  * xc/programs/xdm/xdmcp.c and xc/programs/Xserver/os/xdmcp.c.
182  */
183
184 static void dissect_xdmcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
185 {
186   gint version = -1, opcode = -1;
187   gint offset = 0;
188   proto_item *ti;
189   proto_tree *xdmcp_tree = 0;
190
191   version = tvb_get_ntohs(tvb, offset);
192   if (version != XDMCP_PROTOCOL_VERSION) {
193     /* Only version 1 exists, so this probably is not XDMCP at all... */
194     return;
195   }
196
197   if (check_col(pinfo->cinfo, COL_PROTOCOL))
198     col_set_str(pinfo->cinfo, COL_PROTOCOL, "XDMCP");
199   if (check_col(pinfo->cinfo, COL_INFO))
200     col_clear(pinfo->cinfo, COL_INFO);
201
202   if (tree) {
203     ti = proto_tree_add_item(tree, proto_xdmcp, tvb, offset, -1, FALSE);
204     xdmcp_tree = proto_item_add_subtree(ti, ett_xdmcp);
205
206     proto_tree_add_uint(xdmcp_tree, hf_xdmcp_version, tvb,
207                         offset, 2, version);
208   }
209   offset += 2;
210
211   opcode = tvb_get_ntohs(tvb, offset);
212   if (tree) {
213     proto_tree_add_uint(xdmcp_tree, hf_xdmcp_opcode, tvb,
214                         offset, 2, opcode);
215   }
216   offset += 2;
217   if (check_col(pinfo->cinfo, COL_INFO)) {
218     col_add_fstr(pinfo->cinfo, COL_INFO, "%s",
219                  val_to_str(opcode, opcode_vals, "Unknown (0x%04x)"));
220
221   }
222
223   if (tree) {
224     proto_tree_add_item(xdmcp_tree, hf_xdmcp_length, tvb,
225                         offset, 2, FALSE);
226     offset += 2;
227
228     switch (opcode) {
229       case XDMCP_FORWARD_QUERY:
230       {
231         gint alen, plen;
232         alen = tvb_get_ntohs(tvb, offset);
233         /* I have never seen anything except IPv4 addresses here,
234          * but in theory the protocol should support other address
235          * families. */
236         if (alen == 4) {
237           proto_tree_add_text(xdmcp_tree, tvb, offset, alen+2,
238                               "Client address: %s",
239                               ip_to_str(tvb_get_ptr(tvb, offset+2, 4)));
240           offset += 6;
241         } else {
242           offset += xdmcp_add_bytes(xdmcp_tree, "Client address", tvb, offset);
243         }
244
245         plen = tvb_get_ntohs(tvb, offset);
246         if (plen == 2) {
247           proto_tree_add_text(xdmcp_tree, tvb, offset, plen+2,
248                               "Client port: %u",
249                               tvb_get_ntohs(tvb, offset+2));
250           offset += 4;
251         } else {
252           offset += xdmcp_add_bytes(xdmcp_tree, "Client port", tvb, offset);
253         }
254       }
255       /* fall-through */
256
257       case XDMCP_BROADCAST_QUERY:
258       case XDMCP_QUERY:
259       case XDMCP_INDIRECT_QUERY:
260         offset += xdmcp_add_authorization_names(xdmcp_tree, tvb, offset);
261         break;
262
263       case XDMCP_WILLING:
264         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authentication_name,
265                                    tvb, offset);
266         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_hostname,
267                                    tvb, offset);
268         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_status,
269                                    tvb, offset);
270         break;
271
272       case XDMCP_UNWILLING:
273         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_hostname,
274                                    tvb, offset);
275         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_status,
276                                    tvb, offset);
277         break;
278
279       case XDMCP_REQUEST:
280       {
281         proto_tree *clist_tree;
282         proto_item *clist_ti;
283         gint ctypes_len, caddrs_len, n;
284         gint ctypes_start_offset, caddrs_offset;
285
286         proto_tree_add_item(xdmcp_tree, hf_xdmcp_display_number, tvb,
287                             offset, 2, FALSE);
288         offset += 2;
289
290         ctypes_len = tvb_get_guint8(tvb, offset);
291         ctypes_start_offset = offset;
292         caddrs_offset = offset + 1 + 2*ctypes_len;
293         caddrs_len = tvb_get_guint8(tvb, caddrs_offset);
294         if (ctypes_len != caddrs_len) {
295           proto_tree_add_text(xdmcp_tree, NULL, 0, 0,
296                               "Error: Connection type/address arrays don't match");
297           return;
298         }
299
300         clist_ti = proto_tree_add_text(xdmcp_tree,
301                                        tvb, ctypes_start_offset, -1,
302                                        "Connections (%d)",
303                                        ctypes_len);
304         clist_tree = proto_item_add_subtree(clist_ti, ett_xdmcp_connections);
305
306         offset++;
307         caddrs_offset++;
308
309         n = 1;
310         while (ctypes_len > 0) {
311           proto_item *connection_ti;
312           proto_tree *connection_tree;
313           const char *ip_string;
314
315           gint alen;
316           gint ctype = tvb_get_ntohs(tvb, offset);
317           offset += 2;
318           alen = tvb_get_ntohs(tvb, caddrs_offset);
319           caddrs_offset += 2;
320
321           if ((ctype == 0) && (alen == 4)) {
322             ip_string = ip_to_str(tvb_get_ptr(tvb, caddrs_offset, 4));
323           } else {
324             ip_string = NULL;
325           }
326
327           connection_ti = proto_tree_add_text(clist_tree, NULL, 0, 0,
328                                               "Connection %d%s%s", n,
329                                               (ip_string ? ": " : ""),
330                                               (ip_string ? ip_string : ""));
331           connection_tree = proto_item_add_subtree(connection_ti,
332                                                    ett_xdmcp_connection);
333
334           proto_tree_add_text(connection_tree, tvb, offset-2, 2,
335                               "Type: %s",
336                               val_to_str(ctype, family_vals,
337                                          "Unknown (0x%04x)"));
338           if ((ctype == 0) && (alen == 4)) {
339             proto_tree_add_text(connection_tree, tvb, caddrs_offset-2, alen+2,
340                                 "Address: %s", ip_string);
341           } else {
342             proto_tree_add_text(connection_tree, tvb, caddrs_offset-2, alen+2,
343                                 "Address: (%u byte%s)", alen,
344                                 plurality(alen, "", "s"));
345           }
346           caddrs_offset += alen;
347           ctypes_len--;
348           n++;
349         }
350         offset = caddrs_offset;
351         proto_item_set_len(clist_ti, offset - ctypes_start_offset);
352
353         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authentication_name,
354                                    tvb, offset);
355         offset += xdmcp_add_bytes(xdmcp_tree, "Authentication data",
356                                   tvb, offset);
357
358         offset += xdmcp_add_authorization_names(xdmcp_tree, tvb, offset);
359
360         offset += xdmcp_add_text(xdmcp_tree, "Manufacturer display ID",
361                                  tvb, offset);
362         break;
363       }
364
365       case XDMCP_ACCEPT:
366         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
367                             offset, 4, FALSE);
368         offset += 4;
369         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authentication_name,
370                                    tvb, offset);
371         offset += xdmcp_add_bytes(xdmcp_tree, "Authentication data",
372                                   tvb, offset);
373         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authorization_name,
374                                    tvb, offset);
375         offset += xdmcp_add_bytes(xdmcp_tree, "Authorization data",
376                                   tvb, offset);
377         break;
378
379       case XDMCP_DECLINE:
380         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_status,
381                                    tvb, offset);
382         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_authentication_name,
383                                    tvb, offset);
384         offset += xdmcp_add_bytes(xdmcp_tree, "Authentication data",
385                                   tvb, offset);
386         break;
387
388       case XDMCP_MANAGE:
389         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
390                             offset, 4, FALSE);
391         offset += 4;
392
393         proto_tree_add_item(xdmcp_tree, hf_xdmcp_display_number, tvb,
394                             offset, 2, FALSE);
395         offset += 2;
396
397         offset += xdmcp_add_text(xdmcp_tree, "Display class",
398                                  tvb, offset);
399         break;
400
401       case XDMCP_REFUSE:
402         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
403                             offset, 4, FALSE);
404         offset += 4;
405         break;
406
407       case XDMCP_FAILED:
408         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
409                             offset, 4, FALSE);
410         offset += 4;
411
412         offset += xdmcp_add_string(xdmcp_tree, hf_xdmcp_status,
413                                    tvb, offset);
414         break;
415
416       case XDMCP_KEEPALIVE:
417         proto_tree_add_item(xdmcp_tree, hf_xdmcp_display_number, tvb,
418                             offset, 2, FALSE);
419         offset += 2;
420
421         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
422                             offset, 4, FALSE);
423         offset += 4;
424         break;
425
426       case XDMCP_ALIVE:
427         proto_tree_add_text(xdmcp_tree, tvb, offset, 1,
428                             "Session running: %s",
429                             (tvb_get_guint8(tvb, offset) ? "Yes" : "No"));
430         offset++;
431
432         proto_tree_add_item(xdmcp_tree, hf_xdmcp_session_id, tvb,
433                             offset, 4, FALSE);
434         offset += 4;
435         break;
436     }
437   }
438 }
439
440 /* Register the protocol with Ethereal */
441 void proto_register_xdmcp(void)
442 {
443   /* Setup list of header fields */
444   static hf_register_info hf[] = {
445     { &hf_xdmcp_version,
446       { "Version",           "xdmcp.version",
447       FT_UINT16, BASE_DEC, NULL, 0,
448       "Protocol version", HFILL }
449     },
450     { &hf_xdmcp_opcode,
451       { "Opcode",              "xdmcp.opcode",
452       FT_UINT16, BASE_HEX, VALS(opcode_vals), 0,
453       "Opcode", HFILL }
454     },
455     { &hf_xdmcp_length,
456       { "Message length",     "xdmcp.length",
457       FT_UINT16, BASE_DEC, NULL, 0,
458       "Length of the remaining message", HFILL }
459     },
460     { &hf_xdmcp_authentication_name,
461       { "Authentication name",     "xdmcp.authentication_name",
462       FT_STRING, BASE_DEC, NULL, 0,
463       "Authentication name", HFILL }
464     },
465     { &hf_xdmcp_authorization_name,
466       { "Authorization name",     "xdmcp.authorization_name",
467       FT_STRING, BASE_DEC, NULL, 0,
468       "Authorization name", HFILL }
469     },
470     { &hf_xdmcp_hostname,
471       { "Hostname",     "xdmcp.hostname",
472       FT_STRING, BASE_DEC, NULL, 0,
473       "Hostname", HFILL }
474     },
475     { &hf_xdmcp_status,
476       { "Status",     "xdmcp.status",
477       FT_STRING, BASE_DEC, NULL, 0,
478       "Status", HFILL }
479     },
480     { &hf_xdmcp_session_id,
481       { "Session ID",     "xdmcp.session_id",
482       FT_UINT32, BASE_HEX, NULL, 0,
483       "Session identifier", HFILL }
484     },
485     { &hf_xdmcp_display_number,
486       { "Display number",     "xdmcp.display_number",
487       FT_UINT16, BASE_DEC, NULL, 0,
488       "Display number", HFILL }
489     },
490   };
491
492   /* Setup protocol subtree array */
493   static gint *ett[] = {
494     &ett_xdmcp,
495     &ett_xdmcp_authorization_names,
496     &ett_xdmcp_connections,
497     &ett_xdmcp_connection
498   };
499
500   /* Register the protocol name and description */
501   proto_xdmcp = proto_register_protocol("X Display Manager Control Protocol",
502                                         "XDMCP", "xdmcp");
503
504   /* Required function calls to register the header fields and subtrees used */
505   proto_register_field_array(proto_xdmcp, hf, array_length(hf));
506   proto_register_subtree_array(ett, array_length(ett));
507 };
508
509 void
510 proto_reg_handoff_xdmcp(void)
511 {
512   dissector_handle_t xdmcp_handle;
513
514   xdmcp_handle = create_dissector_handle(dissect_xdmcp, proto_xdmcp);
515   dissector_add("udp.port", UDP_PORT_XDMCP, xdmcp_handle);
516 }