HACK: pinfo->private_data points to smb_info again
[wireshark-wip.git] / epan / dissectors / packet-xmcp.c
blob42af4c73a383563d817d95c5e62eced49d9edc79
1 /* packet-xmcp.c
2 * Routines for eXtensible Messaging Client Protocol (XMCP) dissection
3 * Copyright 2011, Glenn Matthews <glenn.matthews@cisco.com>
5 * $Id$
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * Copied from packet-stun.c
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 * XMCP is a proprietary Cisco protocol based very loosely on the
29 * Session Traversal Utilities for NAT (STUN) protocol.
30 * This dissector is capable of understanding XMCP versions 1.0 and 2.0.
33 #include "config.h"
35 #include <glib.h>
37 #include <epan/packet.h>
38 #include <epan/ipproto.h>
39 #include <epan/addr_resolv.h>
40 #include <packet-tcp.h>
41 #include <epan/prefs.h>
42 #include <epan/conversation.h>
43 #include <epan/wmem/wmem.h>
44 #include <epan/expert.h>
46 static dissector_table_t media_type_dissector_table;
48 /* Initialize the protocol and registered fields */
49 static int proto_xmcp = -1;
51 static int hf_xmcp_response_in = -1;
52 static int hf_xmcp_response_to = -1;
53 static int hf_xmcp_time = -1;
55 typedef struct _xmcp_transaction_t {
56 guint32 request_frame;
57 guint32 response_frame;
58 nstime_t request_time;
59 gboolean request_is_keepalive;
60 } xmcp_transaction_t;
62 typedef struct _xmcp_conv_info_t {
63 wmem_tree_t *transaction_pdus;
64 } xmcp_conv_info_t;
66 static int hf_xmcp_type = -1;
67 static int hf_xmcp_type_reserved = -1;
68 static int hf_xmcp_type_class = -1;
69 static int hf_xmcp_type_method = -1;
70 static int hf_xmcp_length = -1;
71 static int hf_xmcp_cookie = -1;
72 static int hf_xmcp_id = -1;
73 static int hf_xmcp_attributes = -1;
74 static int hf_xmcp_attr = -1;
75 static int hf_xmcp_msg_is_keepalive = -1;
77 static int xmcp_attr_type = -1;
78 static int xmcp_attr_length = -1;
79 static int xmcp_attr_value = -1; /* generic value for unrecognized attrs */
80 static int xmcp_attr_padding = -1; /* generic value for TLV padding bytes */
81 static int xmcp_attr_reserved = -1;
82 static int xmcp_attr_username = -1;
83 static int xmcp_attr_message_integrity = -1;
84 static int xmcp_attr_error_reserved = -1;
85 static int xmcp_attr_error_class = -1;
86 static int xmcp_attr_error_number = -1;
87 static int xmcp_attr_error_code = -1;
88 static int xmcp_attr_error_reason = -1;
89 static int xmcp_attr_realm = -1;
90 static int xmcp_attr_nonce = -1;
91 static int xmcp_attr_client_name = -1;
92 static int xmcp_attr_client_handle = -1;
93 static int xmcp_attr_version_major = -1;
94 static int xmcp_attr_version_minor = -1;
95 static int xmcp_attr_page_size = -1;
96 static int xmcp_attr_client_label = -1;
97 static int xmcp_attr_keepalive = -1;
98 static int xmcp_attr_serv_service = -1;
99 static int xmcp_attr_serv_subservice = -1;
100 static int xmcp_attr_serv_instance = -1;
101 static int xmcp_attr_servtrans_family = -1;
102 static int xmcp_attr_servtrans_port = -1;
103 static int xmcp_attr_servtrans_ipv4 = -1;
104 static int xmcp_attr_servtrans_ipv6 = -1;
105 static int xmcp_attr_service_protocol = -1;
106 static int xmcp_attr_flag = -1;
107 static int xmcp_attr_flag_type = -1;
108 static int xmcp_attr_flag_value = -1;
109 static int xmcp_attr_flag_removal_reason_network_withdraw = -1;
110 static int xmcp_attr_flag_removal_reason_reserved = -1;
111 static int xmcp_attr_flag_trust = -1;
112 static int xmcp_attr_flag_visibility_unauthenticated = -1;
113 static int xmcp_attr_flag_visibility_reserved = -1;
114 static int xmcp_attr_service_version = -1;
115 static int xmcp_attr_service_data = -1;
116 static int xmcp_attr_subscription_id = -1;
117 static int xmcp_attr_service_removed_reason = -1;
118 static int xmcp_attr_domain = -1;
120 static gint ett_xmcp = -1;
121 static gint ett_xmcp_type = -1;
122 static gint ett_xmcp_attr_all = -1;
123 static gint ett_xmcp_attr = -1;
124 static gint ett_xmcp_attr_flag = -1;
126 static expert_field ei_xmcp_message_class_reserved = EI_INIT;
127 static expert_field ei_xmcp_attr_length_bad = EI_INIT;
128 static expert_field ei_xmcp_attr_error_number_out_of_range = EI_INIT;
129 static expert_field ei_xmcp_type_reserved_not_zero = EI_INIT;
130 static expert_field ei_xmcp_data_following_message_integrity = EI_INIT;
131 static expert_field ei_xmcp_msg_type_method_reserved = EI_INIT;
132 static expert_field ei_xmcp_xmcp_attr_servtrans_unknown = EI_INIT;
133 static expert_field ei_xmcp_attr_realm_incorrect = EI_INIT;
134 static expert_field ei_xmcp_new_session = EI_INIT;
135 static expert_field ei_xmcp_response_without_request = EI_INIT;
136 static expert_field ei_xmcp_length_bad = EI_INIT;
137 static expert_field ei_xmcp_error_response = EI_INIT;
138 static expert_field ei_xmcp_magic_cookie_incorrect = EI_INIT;
139 static expert_field ei_xmcp_attr_type_unknown = EI_INIT;
140 static expert_field ei_xmcp_session_termination = EI_INIT;
141 static expert_field ei_xmcp_attr_error_code_unusual = EI_INIT;
143 #define TCP_PORT_XMCP 4788
144 #define XMCP_MAGIC_COOKIE 0x7f5a9bc7
146 void proto_reg_handoff_xmcp(void);
147 static guint global_xmcp_tcp_port = TCP_PORT_XMCP;
149 #define XMCP_HDR_LEN 20
150 #define XMCP_ATTR_HDR_LEN 4
152 #define XMCP_TYPE_RESERVED 0xc000
153 #define XMCP_TYPE_CLASS 0x0110
154 #define XMCP_TYPE_METHOD 0x3eef
156 static const int *xmcp_type_fields[] = {
157 &hf_xmcp_type_reserved,
158 &hf_xmcp_type_method,
159 &hf_xmcp_type_class,
160 NULL
163 #define XMCP_CLASS_REQUEST 0x00
164 #define XMCP_CLASS_RESERVED 0x01
165 #define XMCP_CLASS_RESPONSE_SUCCESS 0x10
166 #define XMCP_CLASS_RESPONSE_ERROR 0x11
168 static const value_string classes[] = {
169 {XMCP_CLASS_REQUEST, "Request"},
170 {XMCP_CLASS_RESERVED, "RESERVED-CLASS"},
171 {XMCP_CLASS_RESPONSE_SUCCESS, "Success Response"},
172 {XMCP_CLASS_RESPONSE_ERROR, "Error Response"},
173 {0, NULL}
176 #define XMCP_METHOD_ILLEGAL 0x000
177 #define XMCP_METHOD_REGISTER 0x001
178 #define XMCP_METHOD_UNREGISTER 0x002
179 #define XMCP_METHOD_REG_REVOKE 0x003
180 #define XMCP_METHOD_PUBLISH 0x004
181 #define XMCP_METHOD_UNPUBLISH 0x005
182 #define XMCP_METHOD_PUB_REVOKE 0x006
183 #define XMCP_METHOD_SUBSCRIBE 0x007
184 #define XMCP_METHOD_UNSUBSCRIBE 0x008
185 #define XMCP_METHOD_WITHDRAW 0x009
186 #define XMCP_METHOD_NOTIFY 0x00a
187 #define XMCP_METHOD_KEEPALIVE 0x00b
189 static const value_string methods[] = {
190 {XMCP_METHOD_ILLEGAL, "Illegal"},
191 {XMCP_METHOD_REGISTER, "Register"},
192 {XMCP_METHOD_UNREGISTER, "Unregister"},
193 {XMCP_METHOD_REG_REVOKE, "RegisterRevoke"},
194 {XMCP_METHOD_PUBLISH, "Publish"},
195 {XMCP_METHOD_UNPUBLISH, "Unpublish"},
196 {XMCP_METHOD_PUB_REVOKE, "PublishRevoke"},
197 {XMCP_METHOD_SUBSCRIBE, "Subscribe"},
198 {XMCP_METHOD_UNSUBSCRIBE, "Unsubscribe"},
199 {XMCP_METHOD_WITHDRAW, "Withdraw"},
200 {XMCP_METHOD_NOTIFY, "Notify"},
201 {XMCP_METHOD_KEEPALIVE, "Keepalive"},
202 {0, NULL}
205 #define XMCP_USERNAME 0x0006
206 #define XMCP_MESSAGE_INTEGRITY 0x0008
207 #define XMCP_ERROR_CODE 0x0009
208 #define XMCP_REALM 0x0014
209 #define XMCP_NONCE 0x0015
210 #define XMCP_CLIENT_NAME 0x1001
211 #define XMCP_CLIENT_HANDLE 0x1002
212 #define XMCP_PROTOCOL_VERSION 0x1003
213 #define XMCP_PAGE_SIZE 0x1004
214 #define XMCP_CLIENT_LABEL 0x1005
215 #define XMCP_KEEPALIVE 0x1006
216 #define XMCP_SERVICE_IDENTITY 0x1007
217 #define XMCP_SERVICE_TRANSPORT 0x1008
218 #define XMCP_SERVICE_PROTOCOL 0x1009
219 #define XMCP_FLAGS 0x100a
220 #define XMCP_SERVICE_VERSION 0x100b
221 #define XMCP_SERVICE_DATA 0x100c
222 #define XMCP_SUBSCRIPTION_ID 0x100e
223 #define XMCP_SERVICE_REMOVED_REASON 0x100f
224 #define XMCP_DOMAIN 0x1011
226 static const value_string attributes[] = {
227 /* Attributes inherited from STUN */
228 {XMCP_USERNAME, "Username"},
229 {XMCP_MESSAGE_INTEGRITY, "Message-Integrity"},
230 {XMCP_ERROR_CODE, "Error-Code"},
231 {XMCP_REALM, "Realm"},
232 {XMCP_NONCE, "Nonce"},
233 /* Attributes specific to XMCP */
234 {XMCP_CLIENT_NAME, "Client-Name"},
235 {XMCP_CLIENT_HANDLE, "Client-Handle"},
236 {XMCP_PROTOCOL_VERSION, "Protocol-Version"},
237 {XMCP_PAGE_SIZE, "PageSize"},
238 {XMCP_CLIENT_LABEL, "ClientLabel"},
239 {XMCP_KEEPALIVE, "Keepalive"},
240 {XMCP_SERVICE_IDENTITY, "ServiceIdentity"},
241 {XMCP_SERVICE_TRANSPORT, "ServiceTransportAddr"},
242 {XMCP_SERVICE_PROTOCOL, "ServiceProtocol"},
243 {XMCP_FLAGS, "Flags"},
244 {XMCP_SERVICE_VERSION, "ServiceVersion"},
245 {XMCP_SERVICE_DATA, "ServiceData"},
246 {XMCP_SUBSCRIPTION_ID, "SubscriptionID"},
247 {XMCP_SERVICE_REMOVED_REASON, "ServiceRemovedReason"},
248 {XMCP_DOMAIN, "Domain"},
249 {0, NULL}
252 static const value_string error_codes[] = {
253 {400, "Bad Request"},
254 {401, "Unauthorized"},
255 {413, "Request Too Large"},
256 {431, "Integrity Check Failure"},
257 {435, "Nonce Required"},
258 {436, "Unknown Username"},
259 {438, "Stale Nonce"},
260 {471, "Bad Client Handle"},
261 {472, "Version Number Too Low"},
262 {473, "Unknown Service"},
263 {474, "Unregistered"},
264 {475, "Invalid ServiceIdentity"},
265 {476, "Unknown Subscription"},
266 {477, "Already Registered"},
267 {478, "Unsupported Protocol Version"},
268 {479, "Unknown or Forbidden Domain"},
269 {499, "Miscellaneous Request Error"},
270 {500, "Responder Error"},
271 {501, "Not Implemented"},
272 {0, NULL}
275 static const value_string address_families[] = {
276 {0x01, "IPv4"},
277 {0x02, "IPv6"},
278 {0, NULL}
281 #define XMCP_FLAG_REMOVAL_REASON 0x0001
282 #define XMCP_FLAG_TRUST 0x0002
283 #define XMCP_FLAG_SERVICE_VISIBILITY 0x0003
285 static const value_string flag_types[] = {
286 {XMCP_FLAG_REMOVAL_REASON, "Removal Reason"},
287 {XMCP_FLAG_TRUST, "Trust"},
288 {XMCP_FLAG_SERVICE_VISIBILITY, "Service Visibility"},
289 {0, NULL}
292 /* Values for specific flag types */
293 #define XMCP_REMOVAL_REASON_NETWORK_WITHDRAW 0x0001
294 #define XMCP_REMOVAL_REASON_RESERVED 0xfffe
296 #define XMCP_TRUST_LOCAL 0
297 #define XMCP_TRUST_LEARNED 1
299 static const value_string flag_trust_values[] = {
300 {XMCP_TRUST_LOCAL, "Local"},
301 {XMCP_TRUST_LEARNED, "Learned"},
302 {0, NULL}
305 #define XMCP_SERVICE_VISIBILITY_UNAUTHENTICATED 0x0001
306 #define XMCP_SERVICE_VISIBILITY_RESERVED 0xfffe
308 static const value_string service_removed_reasons[] = {
309 {0, "Network withdraw"},
310 {1, "Source withdraw"},
311 {0, NULL}
314 /* Dissector state variables */
315 static guint16 xmcp_msg_type_method = XMCP_METHOD_ILLEGAL;
316 static guint16 xmcp_msg_type_class = XMCP_CLASS_RESERVED;
317 static gboolean xmcp_msg_is_keepalive = FALSE;
318 static gint16 xmcp_service_protocol = -1;
319 static gint32 xmcp_service_port = -1;
320 static proto_item *xmcp_it_service_port = NULL;
322 static guint
323 get_xmcp_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
325 return(XMCP_HDR_LEN + tvb_get_ntohs(tvb, offset+2));
328 static guint16
329 get_xmcp_attr_padded_len(guint16 attr_length)
332 * As in STUN, all XMCP attributes report their length in bytes,
333 * but are padded to the next 4-byte multiple.
335 return((attr_length + 3) & 0xfffc);
338 static guint16
339 get_xmcp_attr_fixed_len(guint16 xmcp_attr)
342 * For fixed-length attributes, return their length.
343 * For variable-length attributes, return 0.
345 switch (xmcp_attr) {
346 case XMCP_CLIENT_HANDLE:
347 case XMCP_PROTOCOL_VERSION:
348 case XMCP_PAGE_SIZE:
349 case XMCP_KEEPALIVE:
350 case XMCP_SERVICE_PROTOCOL:
351 case XMCP_SERVICE_VERSION:
352 case XMCP_SUBSCRIPTION_ID:
353 case XMCP_SERVICE_REMOVED_REASON:
354 case XMCP_DOMAIN:
355 return(4);
356 case XMCP_SERVICE_IDENTITY:
357 return(20);
358 default:
359 return(0);
363 static guint16
364 get_xmcp_attr_min_len(guint16 xmcp_attr)
366 switch (xmcp_attr) {
367 case XMCP_USERNAME:
368 case XMCP_NONCE:
369 case XMCP_CLIENT_NAME:
370 case XMCP_CLIENT_LABEL:
371 return(1);
372 case XMCP_ERROR_CODE:
373 return(4);
374 case XMCP_SERVICE_TRANSPORT:
375 return(8); /* 4-byte fixed plus an IPv4 address */
376 case XMCP_MESSAGE_INTEGRITY:
377 return(20); /* HMAC-SHA1 */
378 default:
379 return(get_xmcp_attr_fixed_len(xmcp_attr));
383 static guint16
384 get_xmcp_attr_max_len(guint16 xmcp_attr) {
385 guint16 fixed_len;
387 switch (xmcp_attr) {
388 case XMCP_SERVICE_TRANSPORT:
389 return(20); /* 4-byte fixed plus an IPv6 address */
390 case XMCP_MESSAGE_INTEGRITY:
391 return(32); /* HMAC-SHA-256 */
392 case XMCP_NONCE:
393 case XMCP_CLIENT_NAME:
394 case XMCP_CLIENT_LABEL:
395 return(255);
396 default:
397 fixed_len = get_xmcp_attr_fixed_len(xmcp_attr);
398 return(fixed_len ? fixed_len : 0xffff);
402 static void
403 add_xmcp_port_name (void)
405 if (!xmcp_it_service_port || xmcp_service_port == -1)
406 return;
408 switch(xmcp_service_protocol) {
409 case IP_PROTO_TCP:
410 proto_item_append_text(xmcp_it_service_port, " (TCP: %s)",
411 get_tcp_port(xmcp_service_port));
412 break;
413 case IP_PROTO_UDP:
414 proto_item_append_text(xmcp_it_service_port, " (UDP: %s)",
415 get_udp_port(xmcp_service_port));
416 break;
417 case IP_PROTO_DCCP:
418 proto_item_append_text(xmcp_it_service_port, " (DCCP: %s)",
419 get_dccp_port(xmcp_service_port));
420 break;
421 case IP_PROTO_SCTP:
422 proto_item_append_text(xmcp_it_service_port, " (SCTP: %s)",
423 get_sctp_port(xmcp_service_port));
424 break;
425 default:
426 break;
430 static void
431 decode_xmcp_attr_value (proto_tree *attr_tree, guint16 attr_type,
432 guint16 attr_length, tvbuff_t *tvb, guint16 offset,
433 packet_info *pinfo)
435 proto_item *it;
437 switch (attr_type) {
438 case XMCP_USERNAME:
439 proto_tree_add_item(attr_tree, xmcp_attr_username, tvb, offset,
440 attr_length, ENC_ASCII|ENC_NA);
441 proto_item_append_text(attr_tree, ": %s",
442 tvb_get_string(wmem_packet_scope(), tvb, offset, attr_length));
444 * Many message methods may include this attribute,
445 * but it's only interesting when Registering at first
447 if (xmcp_msg_type_method == XMCP_METHOD_REGISTER) {
448 col_append_fstr(pinfo->cinfo, COL_INFO, ", user \"%s\"",
449 tvb_get_string(wmem_packet_scope(), tvb, offset, attr_length));
451 break;
452 case XMCP_MESSAGE_INTEGRITY:
453 proto_tree_add_item(attr_tree, xmcp_attr_message_integrity, tvb, offset,
454 attr_length, ENC_NA);
455 /* Message-integrity should be the last attribute in the message */
456 if ((guint)(offset + get_xmcp_attr_padded_len(attr_length)) < tvb_reported_length(tvb)) {
457 expert_add_info(pinfo, attr_tree, &ei_xmcp_data_following_message_integrity);
459 break;
460 case XMCP_ERROR_CODE:
461 if (attr_length < 4)
462 break;
463 proto_tree_add_item(attr_tree, xmcp_attr_error_reserved, tvb, offset,
464 3, ENC_BIG_ENDIAN);
465 proto_tree_add_item(attr_tree, xmcp_attr_error_class, tvb, offset,
466 3, ENC_BIG_ENDIAN);
468 guint8 error_class, error_number;
469 guint16 error_code;
470 it = proto_tree_add_item(attr_tree, xmcp_attr_error_number, tvb,
471 (offset+3), 1, ENC_BIG_ENDIAN);
473 error_class = tvb_get_guint8(tvb, offset+2) & 0x07;
474 error_number = tvb_get_guint8(tvb, offset+3);
476 if (error_number > 99) {
477 expert_add_info(pinfo, it, &ei_xmcp_attr_error_number_out_of_range);
478 } else {
479 /* Error code = error class + (error num % 100) */
480 error_code = (error_class * 100) + error_number;
481 it = proto_tree_add_uint(attr_tree, xmcp_attr_error_code, tvb,
482 (offset+2), 2, error_code);
483 PROTO_ITEM_SET_GENERATED(it);
484 proto_item_append_text(attr_tree, ": %d", error_code);
485 col_append_fstr(pinfo->cinfo, COL_INFO, ", error %d (%s)", error_code,
486 val_to_str_const(error_code, error_codes, "Unknown"));
489 * All error responses default to a PI_NOTE severity.
490 * Some specific error codes are more significant, so mark them up.
492 switch (error_code) {
493 case 400: /* Bad Request */
494 case 431: /* Integrity Check Failure */
495 case 473: /* Unknown Service */
496 case 476: /* Unknown Subscription */
497 case 477: /* Already Registered */
498 case 499: /* Miscellaneous Request Error */
499 case 500: /* Responder Error */
500 expert_add_info_format(pinfo, it, &ei_xmcp_attr_error_code_unusual, "Unusual error code (%u, %s)", error_code, val_to_str_const(error_code, error_codes, "Unknown"));
501 break;
502 default:
503 break;
507 if (attr_length < 5)
508 break;
509 proto_tree_add_item(attr_tree, xmcp_attr_error_reason, tvb, (offset+4),
510 (attr_length - 4), ENC_ASCII|ENC_NA);
511 proto_item_append_text(attr_tree, " (%s)",
512 tvb_get_string(wmem_packet_scope(), tvb, (offset+4),
513 (attr_length-4)));
514 break;
515 case XMCP_REALM:
516 it = proto_tree_add_item(attr_tree, xmcp_attr_realm, tvb, offset,
517 attr_length, ENC_ASCII|ENC_NA);
519 guint8 *realm;
520 realm = tvb_get_string(wmem_packet_scope(), tvb, offset, attr_length);
521 proto_item_append_text(attr_tree, ": %s", realm);
522 /* In XMCP the REALM string should always be "SAF" including the quotes */
523 if (attr_length != 5 || strncmp(realm, "\"SAF\"", attr_length)) {
524 expert_add_info(pinfo, it, &ei_xmcp_attr_realm_incorrect);
527 break;
528 case XMCP_NONCE:
529 proto_tree_add_item(attr_tree, xmcp_attr_nonce, tvb, offset,
530 attr_length, ENC_ASCII|ENC_NA);
531 proto_item_append_text(attr_tree, ": %s",
532 tvb_get_string(wmem_packet_scope(), tvb, offset, attr_length));
533 break;
534 case XMCP_CLIENT_NAME:
535 proto_tree_add_item(attr_tree, xmcp_attr_client_name, tvb, offset,
536 attr_length, ENC_ASCII|ENC_NA);
537 proto_item_append_text(attr_tree, ": %s",
538 tvb_get_string(wmem_packet_scope(), tvb, offset, attr_length));
539 col_append_fstr(pinfo->cinfo, COL_INFO, ", name \"%s\"",
540 tvb_get_string(wmem_packet_scope(), tvb, offset, attr_length));
541 break;
542 case XMCP_CLIENT_HANDLE:
543 if (attr_length < 4)
544 break;
545 proto_tree_add_item(attr_tree, xmcp_attr_client_handle, tvb, offset,
546 4, ENC_BIG_ENDIAN);
547 proto_item_append_text(attr_tree, ": %u", tvb_get_ntohl(tvb, offset));
548 col_append_fstr(pinfo->cinfo, COL_INFO, ", handle %u",
549 tvb_get_ntohl(tvb, offset));
551 * A Register request containing a Client-Handle is considered
552 * to be a Keepalive.
554 if (xmcp_msg_type_method == XMCP_METHOD_REGISTER &&
555 xmcp_msg_type_class == XMCP_CLASS_REQUEST) {
556 xmcp_msg_is_keepalive = TRUE;
558 break;
559 case XMCP_PROTOCOL_VERSION:
560 if (attr_length < 2)
561 break;
562 proto_tree_add_item(attr_tree, xmcp_attr_version_major, tvb, offset,
563 2, ENC_BIG_ENDIAN);
564 if (attr_length < 4)
565 break;
566 proto_tree_add_item(attr_tree, xmcp_attr_version_minor, tvb, (offset+2),
567 2, ENC_BIG_ENDIAN);
568 proto_item_append_text(attr_tree, ": %u.%u", tvb_get_ntohs(tvb, offset),
569 tvb_get_ntohs(tvb, (offset+2)));
570 break;
571 case XMCP_PAGE_SIZE:
572 if (attr_length < 4)
573 break;
574 proto_tree_add_item(attr_tree, xmcp_attr_page_size, tvb, offset, 4, ENC_BIG_ENDIAN);
575 proto_item_append_text(attr_tree, ": %u", tvb_get_ntohl(tvb, offset));
576 break;
577 case XMCP_CLIENT_LABEL:
578 proto_tree_add_item(attr_tree, xmcp_attr_client_label, tvb, offset,
579 attr_length, ENC_ASCII|ENC_NA);
580 proto_item_append_text(attr_tree, ": %s",
581 tvb_get_string(wmem_packet_scope(), tvb, offset, attr_length));
582 col_append_fstr(pinfo->cinfo, COL_INFO, ", label \"%s\"",
583 tvb_get_string(wmem_packet_scope(), tvb, offset, attr_length));
584 break;
585 case XMCP_KEEPALIVE:
586 if (attr_length < 4)
587 break;
588 proto_tree_add_item(attr_tree, xmcp_attr_keepalive, tvb, offset, 4, ENC_BIG_ENDIAN);
589 proto_item_append_text(attr_tree, ": %u", tvb_get_ntohl(tvb, offset));
590 break;
591 case XMCP_SERVICE_IDENTITY:
592 if (attr_length < 2)
593 break;
594 proto_tree_add_item(attr_tree, xmcp_attr_serv_service, tvb, offset,
595 2, ENC_BIG_ENDIAN);
596 if (attr_length < 4)
597 break;
598 proto_tree_add_item(attr_tree, xmcp_attr_serv_subservice, tvb, (offset+2),
599 2, ENC_BIG_ENDIAN);
600 if (attr_length < 20)
601 break;
602 proto_tree_add_item(attr_tree, xmcp_attr_serv_instance, tvb, (offset+4),
603 16, ENC_BIG_ENDIAN);
605 e_guid_t guid;
606 char buf[GUID_STR_LEN];
607 tvb_get_guid(tvb, (offset+4), &guid, ENC_BIG_ENDIAN);
608 guid_to_str_buf(&guid, buf, sizeof(buf));
609 proto_item_append_text(attr_tree, ": %u:%u:%s",
610 tvb_get_ntohs(tvb, offset),
611 tvb_get_ntohs(tvb, (offset+2)), buf);
612 col_append_fstr(pinfo->cinfo, COL_INFO, ", service %u:%u:%s",
613 tvb_get_ntohs(tvb, offset),
614 tvb_get_ntohs(tvb, (offset+2)), buf);
616 break;
617 case XMCP_SERVICE_TRANSPORT:
619 * One byte of padding, one byte indicating family,
620 * two bytes for port, followed by addr
622 if (attr_length < 1)
623 break;
624 proto_tree_add_item(attr_tree, xmcp_attr_reserved, tvb, offset, 1, ENC_NA);
625 if (attr_length < 2)
626 break;
627 proto_tree_add_item(attr_tree, xmcp_attr_servtrans_family, tvb,
628 (offset+1), 1, ENC_BIG_ENDIAN);
629 if (attr_length < 4)
630 break;
631 xmcp_service_port = tvb_get_ntohs(tvb, (offset+2));
632 xmcp_it_service_port = proto_tree_add_item(attr_tree,
633 xmcp_attr_servtrans_port,
634 tvb, (offset+2), 2, ENC_BIG_ENDIAN);
635 /* If we now know both port and protocol number, fill in the port name */
636 if (xmcp_service_protocol != -1) {
637 add_xmcp_port_name();
639 switch (tvb_get_guint8(tvb, (offset+1))) {
640 case 0x01: /* IPv4 */
641 if (attr_length != 8) {
642 expert_add_info_format(pinfo, attr_tree, &ei_xmcp_attr_length_bad, "Malformed IPv4 address");
643 } else {
644 guint32 ip;
645 proto_tree_add_item(attr_tree, xmcp_attr_servtrans_ipv4, tvb,
646 (offset+4), 4, ENC_BIG_ENDIAN);
647 ip = tvb_get_ipv4(tvb, (offset+4));
648 proto_item_append_text(attr_tree, ": %s:%u", ip_to_str((guint8 *)&ip),
649 tvb_get_ntohs(tvb, (offset+2)));
651 break;
652 case 0x02: /* IPv6 */
653 if (attr_length != 20) {
654 expert_add_info_format(pinfo, attr_tree, &ei_xmcp_attr_length_bad, "Malformed IPv6 address");
655 } else {
656 struct e_in6_addr ipv6;
657 proto_tree_add_item(attr_tree, xmcp_attr_servtrans_ipv6, tvb,
658 (offset+4), 16, ENC_NA);
659 tvb_get_ipv6(tvb, (offset+4), &ipv6);
660 proto_item_append_text(attr_tree, ": [%s]:%u", ip6_to_str(&ipv6),
661 tvb_get_ntohs(tvb, (offset+2)));
663 break;
664 default:
665 expert_add_info(pinfo, attr_tree, &ei_xmcp_xmcp_attr_servtrans_unknown);
666 break;
668 break;
669 case XMCP_SERVICE_PROTOCOL:
670 /* Three bytes of padding followed by a 1-byte protocol number */
671 if (attr_length < 4)
672 break;
673 proto_tree_add_item(attr_tree, xmcp_attr_reserved, tvb, offset, 3, ENC_NA);
674 proto_tree_add_item(attr_tree, xmcp_attr_service_protocol, tvb,
675 (offset+3), 1, ENC_BIG_ENDIAN);
676 xmcp_service_protocol = tvb_get_guint8(tvb, (offset+3));
677 proto_item_append_text(attr_tree, ": %u (%s)", xmcp_service_protocol,
678 val_to_str_ext_const(xmcp_service_protocol,
679 &ipproto_val_ext, "Unknown"));
680 /* If we now know both port and protocol number, fill in the port name */
681 if (xmcp_service_port != -1 && xmcp_it_service_port != NULL) {
682 add_xmcp_port_name();
684 break;
685 case XMCP_FLAGS:
686 /* Flags is a series of type-value pairs */
687 if (attr_length % 4 != 0) {
688 expert_add_info_format(pinfo, attr_tree, &ei_xmcp_attr_length_bad, "Malformed Flags - length not divisible by 4");
691 guint16 flag_type, flag_value, current_offset = offset;
692 proto_item *ti;
693 proto_tree *flag_tree;
694 while ((current_offset-offset)+3 < attr_length) {
695 flag_type = tvb_get_ntohs(tvb, (current_offset));
696 flag_value = tvb_get_ntohs(tvb, (current_offset+2));
697 ti = proto_tree_add_none_format(attr_tree, xmcp_attr_flag, tvb,
698 current_offset, 4,
699 "Flag: %s:",
700 val_to_str_const(flag_type, flag_types,
701 "Unknown"));
702 flag_tree = proto_item_add_subtree(ti, ett_xmcp_attr_flag);
703 proto_tree_add_item(flag_tree, xmcp_attr_flag_type, tvb,
704 current_offset, 2, ENC_BIG_ENDIAN);
706 current_offset += 2;
707 switch (flag_type) {
708 case XMCP_FLAG_REMOVAL_REASON:
709 proto_tree_add_item(flag_tree, xmcp_attr_flag_removal_reason_reserved,
710 tvb, current_offset, 2, ENC_BIG_ENDIAN);
711 proto_tree_add_item(flag_tree,
712 xmcp_attr_flag_removal_reason_network_withdraw,
713 tvb, current_offset, 2, ENC_BIG_ENDIAN);
714 if (flag_value & XMCP_REMOVAL_REASON_NETWORK_WITHDRAW) {
715 proto_item_append_text(flag_tree, " (network withdraw)");
717 if (!flag_value) {
718 proto_item_append_text(flag_tree, " (source withdraw)");
720 break;
721 case XMCP_FLAG_TRUST:
722 proto_tree_add_item(flag_tree, xmcp_attr_flag_trust, tvb,
723 current_offset, 2, ENC_BIG_ENDIAN);
724 proto_item_append_text(flag_tree, " %s",
725 val_to_str_const(flag_value, flag_trust_values,
726 "Unknown"));
727 break;
728 case XMCP_FLAG_SERVICE_VISIBILITY:
729 proto_tree_add_item(flag_tree, xmcp_attr_flag_visibility_reserved,
730 tvb, current_offset, 2, ENC_BIG_ENDIAN);
731 proto_tree_add_item(flag_tree,
732 xmcp_attr_flag_visibility_unauthenticated,
733 tvb, current_offset, 2, ENC_BIG_ENDIAN);
734 if (flag_value & XMCP_SERVICE_VISIBILITY_UNAUTHENTICATED) {
735 proto_item_append_text(flag_tree,
736 " (visible to unauthenticated clients)");
738 if (!flag_value) {
739 proto_item_append_text(flag_tree, " (default)");
741 break;
742 default:
743 proto_tree_add_item(flag_tree, xmcp_attr_flag_value, tvb,
744 current_offset, 2, ENC_BIG_ENDIAN);
745 proto_item_append_text(flag_tree, " 0x%04x", flag_value);
746 break;
748 current_offset += 2;
751 break;
752 case XMCP_SERVICE_VERSION:
753 if (attr_length < 4)
754 break;
755 proto_tree_add_item(attr_tree, xmcp_attr_service_version, tvb, offset,
756 4, ENC_BIG_ENDIAN);
757 proto_item_append_text(attr_tree, ": %u", tvb_get_ntohl(tvb, offset));
758 break;
759 case XMCP_SERVICE_DATA:
760 proto_tree_add_item(attr_tree, xmcp_attr_service_data, tvb, offset,
761 attr_length, ENC_NA);
762 if (attr_length > 0) {
763 tvbuff_t *next_tvb;
764 guint8 *test_string, *tok;
766 next_tvb = tvb_new_subset(tvb, offset, attr_length, attr_length);
768 * Service-Data is usually (but not always) plain text, specifically XML.
769 * If it "looks like" XML (begins with optional whitespace followed by
770 * a '<'), try XML.
771 * Otherwise, try plain-text.
773 test_string = tvb_get_string(wmem_packet_scope(), next_tvb, 0, (attr_length < 32 ?
774 attr_length : 32));
775 tok = strtok(test_string, " \t\r\n");
776 if (tok && tok[0] == '<') {
777 /* Looks like XML */
778 dissector_try_string(media_type_dissector_table, "application/xml",
779 next_tvb, pinfo, attr_tree, NULL);
780 } else {
781 /* Try plain text */
782 dissector_try_string(media_type_dissector_table, "text/plain",
783 next_tvb, pinfo, attr_tree, NULL);
786 break;
787 case XMCP_SUBSCRIPTION_ID:
788 if (attr_length < 4)
789 break;
790 proto_tree_add_item(attr_tree, xmcp_attr_subscription_id, tvb, offset,
791 4, ENC_BIG_ENDIAN);
792 proto_item_append_text(attr_tree, ": %u", tvb_get_ntohl(tvb, offset));
793 col_append_fstr(pinfo->cinfo, COL_INFO, ", subscription %u",
794 tvb_get_ntohl(tvb, offset));
795 break;
796 case XMCP_SERVICE_REMOVED_REASON:
797 if (attr_length < 4)
798 break;
799 proto_tree_add_item(attr_tree, xmcp_attr_service_removed_reason, tvb,
800 offset, 4, ENC_BIG_ENDIAN);
801 proto_item_append_text(attr_tree, ": %s",
802 val_to_str_const(tvb_get_ntohl(tvb, offset),
803 service_removed_reasons,
804 "Unknown"));
805 break;
806 case XMCP_DOMAIN:
807 if (attr_length < 4)
808 break;
809 proto_tree_add_item(attr_tree, xmcp_attr_domain, tvb, offset, 4, ENC_BIG_ENDIAN);
810 proto_item_append_text(attr_tree, ": %u", tvb_get_ntohl(tvb, offset));
811 break;
812 default:
813 proto_tree_add_item(attr_tree, xmcp_attr_value, tvb, offset,
814 attr_length, ENC_NA);
815 expert_add_info(pinfo, attr_tree, &ei_xmcp_attr_type_unknown);
816 break;
818 if (attr_length % 4 != 0) {
819 proto_tree_add_item(attr_tree, xmcp_attr_padding, tvb, (offset+attr_length),
820 (4 - (attr_length % 4)), ENC_NA);
822 if (attr_length < get_xmcp_attr_min_len(attr_type)) {
823 expert_add_info_format(pinfo, attr_tree, &ei_xmcp_attr_length_bad, "Length less than minimum for this attribute type");
824 } else if (attr_length > get_xmcp_attr_max_len(attr_type)) {
825 expert_add_info_format(pinfo, attr_tree, &ei_xmcp_attr_length_bad, "Length exceeds maximum for this attribute type");
829 static int
830 dissect_xmcp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
832 guint16 msg_type, msg_length;
833 proto_item *ti = NULL;
834 proto_tree *xmcp_tree, *attr_all_tree, *attr_tree;
835 guint16 offset, attr_type, attr_length;
837 /* For request/response association */
838 guint32 transaction_id[3];
839 wmem_tree_key_t transaction_id_key[2];
840 conversation_t *conversation;
841 xmcp_conv_info_t *xmcp_conv_info;
842 xmcp_transaction_t *xmcp_trans;
844 if (tvb_reported_length(tvb) < XMCP_HDR_LEN) {
845 return 0;
847 /* Check for valid message type field */
848 msg_type = tvb_get_ntohs(tvb, 0);
849 if (msg_type & XMCP_TYPE_RESERVED) { /* First 2 bits must be 0 */
850 return 0;
852 /* Check for valid "magic cookie" field */
853 if (tvb_get_ntohl(tvb, 4) != XMCP_MAGIC_COOKIE) {
854 return 0;
857 col_set_str(pinfo->cinfo, COL_PROTOCOL, "XMCP");
858 /* Clear out stuff in the info column */
859 col_clear(pinfo->cinfo, COL_INFO);
861 /* As in STUN, the first 2 bytes contain the message class and method */
862 xmcp_msg_type_class = ((msg_type & XMCP_TYPE_CLASS) >> 4);
863 xmcp_msg_type_method = (msg_type & XMCP_TYPE_METHOD);
864 col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s",
865 val_to_str_const(xmcp_msg_type_method, methods, "Unknown"),
866 val_to_str_const(xmcp_msg_type_class, classes, "Unknown"));
868 /* Get the transaction ID */
869 transaction_id[0] = tvb_get_ntohl(tvb, 8);
870 transaction_id[1] = tvb_get_ntohl(tvb, 12);
871 transaction_id[2] = tvb_get_ntohl(tvb, 16);
873 transaction_id_key[0].length = 3;
874 transaction_id_key[0].key = transaction_id;
875 transaction_id_key[1].length = 0;
876 transaction_id_key[1].key = NULL;
878 conversation = find_or_create_conversation(pinfo);
880 /* Do we already have XMCP state for this conversation? */
881 xmcp_conv_info = (xmcp_conv_info_t *)conversation_get_proto_data(conversation, proto_xmcp);
882 if (!xmcp_conv_info) {
883 xmcp_conv_info = wmem_new(wmem_file_scope(), xmcp_conv_info_t);
884 xmcp_conv_info->transaction_pdus = wmem_tree_new(wmem_file_scope());
885 conversation_add_proto_data(conversation, proto_xmcp, xmcp_conv_info);
888 /* Find existing transaction entry or create a new one */
889 xmcp_trans = (xmcp_transaction_t *)wmem_tree_lookup32_array(xmcp_conv_info->transaction_pdus,
890 transaction_id_key);
891 if (!xmcp_trans) {
892 xmcp_trans = wmem_new(wmem_file_scope(), xmcp_transaction_t);
893 xmcp_trans->request_frame = 0;
894 xmcp_trans->response_frame = 0;
895 xmcp_trans->request_time = pinfo->fd->abs_ts;
896 xmcp_trans->request_is_keepalive = FALSE;
897 wmem_tree_insert32_array(xmcp_conv_info->transaction_pdus,
898 transaction_id_key, (void *)xmcp_trans);
901 /* Update transaction entry */
902 if (!pinfo->fd->flags.visited) {
903 if (xmcp_msg_type_class == XMCP_CLASS_REQUEST) {
904 if (xmcp_trans->request_frame == 0) {
905 xmcp_trans->request_frame = pinfo->fd->num;
906 xmcp_trans->request_time = pinfo->fd->abs_ts;
908 } else if (xmcp_msg_type_class != XMCP_CLASS_RESERVED) {
909 if (xmcp_trans->response_frame == 0) {
910 xmcp_trans->response_frame = pinfo->fd->num;
915 ti = proto_tree_add_item(tree, proto_xmcp, tvb, 0, -1, ENC_NA);
916 xmcp_tree = proto_item_add_subtree(ti, ett_xmcp);
918 ti = proto_tree_add_bitmask(xmcp_tree, tvb, 0, hf_xmcp_type, ett_xmcp_type,
919 xmcp_type_fields, ENC_BIG_ENDIAN);
921 if (msg_type & XMCP_TYPE_RESERVED) {
922 expert_add_info(pinfo, ti, &ei_xmcp_type_reserved_not_zero);
924 if (xmcp_msg_type_class == XMCP_CLASS_RESERVED) {
925 expert_add_info(pinfo, ti, &ei_xmcp_message_class_reserved);
926 } else if (xmcp_msg_type_class == XMCP_CLASS_RESPONSE_ERROR) {
927 expert_add_info(pinfo, ti, &ei_xmcp_error_response);
930 if (xmcp_msg_type_method < 0x001 || xmcp_msg_type_method > 0x00b) {
931 expert_add_info(pinfo, ti, &ei_xmcp_msg_type_method_reserved);
935 * Some forms of XMCP overload the Register method for Keepalive packets
936 * rather than using a separate Keepalive method. We'll try to determine from
937 * the message contents whether this message is a Keepalive. Initialize first.
939 xmcp_msg_is_keepalive = (xmcp_trans->request_is_keepalive ||
940 (xmcp_msg_type_method == XMCP_METHOD_KEEPALIVE));
942 /* After the class/method, we have a 2 byte length...*/
943 ti = proto_tree_add_item(xmcp_tree, hf_xmcp_length, tvb, 2, 2, ENC_BIG_ENDIAN);
944 msg_length = tvb_get_ntohs(tvb, 2);
945 if ((guint)(msg_length + XMCP_HDR_LEN) > tvb_reported_length(tvb)) {
946 expert_add_info_format(pinfo, ti, &ei_xmcp_length_bad, "XMCP message length (%u-byte header + %u) exceeds packet length (%u)", XMCP_HDR_LEN, msg_length, tvb_reported_length(tvb));
947 return tvb_length(tvb);
950 /* ...a 4 byte magic cookie... */
951 ti = proto_tree_add_item(xmcp_tree, hf_xmcp_cookie, tvb, 4, 4, ENC_BIG_ENDIAN);
952 if (tvb_get_ntohl(tvb, 4) != XMCP_MAGIC_COOKIE) {
953 expert_add_info(pinfo, ti, &ei_xmcp_magic_cookie_incorrect);
956 /* ...and a 12-byte transaction id */
957 ti = proto_tree_add_item(xmcp_tree, hf_xmcp_id, tvb, 8, 12, ENC_NA);
959 /* Print state tracking in the tree */
960 if (xmcp_msg_type_class == XMCP_CLASS_REQUEST) {
961 if (xmcp_trans->response_frame) {
962 ti = proto_tree_add_uint(xmcp_tree, hf_xmcp_response_in, tvb, 0, 0,
963 xmcp_trans->response_frame);
964 PROTO_ITEM_SET_GENERATED(ti);
966 } else if (xmcp_msg_type_class != XMCP_CLASS_RESERVED) {
967 if (xmcp_trans->request_frame) {
968 nstime_t ns;
970 ti = proto_tree_add_uint(xmcp_tree, hf_xmcp_response_to, tvb, 0, 0,
971 xmcp_trans->request_frame);
972 PROTO_ITEM_SET_GENERATED(ti);
974 nstime_delta(&ns, &pinfo->fd->abs_ts, &xmcp_trans->request_time);
975 ti = proto_tree_add_time(xmcp_tree, hf_xmcp_time, tvb, 0, 0, &ns);
976 PROTO_ITEM_SET_GENERATED(ti);
977 } else {
978 /* This is a response, but we don't know about a request for this response? */
979 expert_add_info(pinfo, ti, &ei_xmcp_response_without_request);
983 xmcp_service_protocol = -1;
984 xmcp_service_port = -1;
985 xmcp_it_service_port = NULL;
987 /* The header is then followed by "msg_length" bytes of TLV attributes */
988 if (msg_length > 0) {
989 ti = proto_tree_add_item(xmcp_tree, hf_xmcp_attributes, tvb,
990 XMCP_HDR_LEN, msg_length, ENC_NA);
991 attr_all_tree = proto_item_add_subtree(ti, ett_xmcp_attr_all);
993 offset = XMCP_HDR_LEN;
995 while (offset < (msg_length + XMCP_HDR_LEN)) {
996 /* Get type/length of next TLV */
997 attr_type = tvb_get_ntohs(tvb, offset);
998 attr_length = tvb_get_ntohs(tvb, offset+2);
999 ti = proto_tree_add_none_format(attr_all_tree, hf_xmcp_attr, tvb, offset,
1000 (XMCP_ATTR_HDR_LEN +
1001 get_xmcp_attr_padded_len(attr_length)),
1002 "%s, length %u",
1003 val_to_str_const(attr_type, attributes,
1004 "Unknown"),
1005 attr_length);
1007 /* Add subtree for this TLV */
1008 attr_tree = proto_item_add_subtree(ti, ett_xmcp_attr);
1010 proto_tree_add_item(attr_tree, xmcp_attr_type, tvb,
1011 offset, 2, ENC_BIG_ENDIAN);
1012 offset += 2;
1013 ti = proto_tree_add_item(attr_tree, xmcp_attr_length, tvb,
1014 offset, 2, ENC_BIG_ENDIAN);
1015 offset += 2;
1017 if ((offset + attr_length) > (XMCP_HDR_LEN + msg_length)) {
1018 proto_item_append_text(ti, " (bogus, exceeds message length)");
1019 expert_add_info_format(pinfo, attr_tree, &ei_xmcp_attr_length_bad, "Attribute length exceeds message length");
1020 break;
1023 decode_xmcp_attr_value(attr_tree, attr_type, attr_length, tvb,
1024 offset, pinfo);
1026 offset += get_xmcp_attr_padded_len(attr_length);
1031 * Flag this message as a keepalive if the attribute analysis
1032 * suggested that it is one
1034 if (xmcp_msg_is_keepalive) {
1035 ti = proto_tree_add_none_format(xmcp_tree, hf_xmcp_msg_is_keepalive, tvb,
1036 0, 0, "This is a Keepalive message");
1037 PROTO_ITEM_SET_GENERATED(ti);
1038 if (xmcp_msg_type_method != XMCP_METHOD_KEEPALIVE) {
1039 col_prepend_fstr(pinfo->cinfo, COL_INFO, "[Keepalive] ");
1041 if (xmcp_msg_type_class == XMCP_CLASS_REQUEST) {
1042 xmcp_trans->request_is_keepalive = TRUE;
1044 } else if (xmcp_msg_type_class == XMCP_CLASS_REQUEST ||
1045 xmcp_msg_type_class == XMCP_CLASS_RESPONSE_SUCCESS) {
1046 if (xmcp_msg_type_method == XMCP_METHOD_REGISTER) {
1047 expert_add_info_format(pinfo, xmcp_tree, &ei_xmcp_new_session, "New session - Register %s", val_to_str_const(xmcp_msg_type_class, classes, ""));
1048 } else if (xmcp_msg_type_method == XMCP_METHOD_UNREGISTER ||
1049 xmcp_msg_type_method == XMCP_METHOD_REG_REVOKE) {
1050 expert_add_info_format(pinfo, xmcp_tree, &ei_xmcp_session_termination, "Session termination - %s %s", val_to_str_const(xmcp_msg_type_method, methods, ""), val_to_str_const(xmcp_msg_type_class, classes, ""));
1054 return tvb_length(tvb);
1057 static int
1058 dissect_xmcp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
1060 tcp_dissect_pdus(tvb, pinfo, tree, TRUE, XMCP_HDR_LEN,
1061 get_xmcp_message_len, dissect_xmcp_message, data);
1062 return tvb_length(tvb);
1065 static gboolean
1066 dissect_xmcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1068 /* See if this looks like a real XMCP packet */
1069 if (tvb_length(tvb) < XMCP_HDR_LEN) {
1070 return FALSE;
1072 /* Check for valid message type field */
1073 if (tvb_get_ntohs(tvb, 0) & XMCP_TYPE_RESERVED) { /* First 2 bits must be 0 */
1074 return FALSE;
1076 /* Check for valid "magic cookie" field */
1077 if (tvb_get_ntohl(tvb, 4) != XMCP_MAGIC_COOKIE) {
1078 return FALSE;
1081 /* Good enough to consider a match! */
1082 tcp_dissect_pdus(tvb, pinfo, tree, TRUE, XMCP_HDR_LEN,
1083 get_xmcp_message_len, dissect_xmcp_message, data);
1084 return TRUE;
1087 void
1088 proto_register_xmcp(void)
1090 static hf_register_info hf[] = {
1091 { &hf_xmcp_type,
1092 { "Message Type", "xmcp.type",
1093 FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }
1095 { &hf_xmcp_type_reserved,
1096 { "Reserved", "xmcp.type.reserved",
1097 FT_UINT16, BASE_HEX, NULL, XMCP_TYPE_RESERVED, NULL, HFILL }
1099 { &hf_xmcp_type_class,
1100 { "Class", "xmcp.type.class",
1101 FT_UINT16, BASE_HEX, VALS(classes), XMCP_TYPE_CLASS, NULL, HFILL }
1103 { &hf_xmcp_type_method,
1104 { "Method", "xmcp.type.method",
1105 FT_UINT16, BASE_HEX, VALS(methods), XMCP_TYPE_METHOD, NULL, HFILL }
1107 { &hf_xmcp_msg_is_keepalive,
1108 { "Message is Keepalive", "xmcp.analysis.keepalive",
1109 FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
1111 { &hf_xmcp_length,
1112 { "Message Length", "xmcp.length",
1113 FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
1115 { &hf_xmcp_cookie,
1116 { "XMCP Magic Cookie", "xmcp.cookie",
1117 FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }
1119 { &hf_xmcp_id,
1120 { "Transaction ID", "xmcp.id",
1121 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
1123 { &hf_xmcp_response_in,
1124 { "Response In", "xmcp.response-in",
1125 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1126 "The response to this XMCP request is in this frame", HFILL }
1128 { &hf_xmcp_response_to,
1129 { "Response To", "xmcp.response-to",
1130 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
1131 "This is a response to the XMCP request in this frame", HFILL }
1133 { &hf_xmcp_time,
1134 { "Elapsed Time", "xmcp.time",
1135 FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1136 "The time between the Request and the Response", HFILL }
1138 { &hf_xmcp_attributes,
1139 { "Attributes", "xmcp.attributes",
1140 FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
1142 { &hf_xmcp_attr,
1143 { "Attribute", "xmcp.attr",
1144 FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
1146 { &xmcp_attr_type,
1147 { "Attribute Type", "xmcp.attr.type",
1148 FT_UINT16, BASE_HEX, VALS(attributes), 0x0, NULL, HFILL }
1150 { &xmcp_attr_length,
1151 { "Attribute Length", "xmcp.attr.length",
1152 FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
1154 { &xmcp_attr_value,
1155 { "Attribute Value", "xmcp.attr.value",
1156 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}
1158 { &xmcp_attr_padding,
1159 { "Padding", "xmcp.attr.padding",
1160 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
1162 { &xmcp_attr_reserved,
1163 { "Reserved", "xmcp.attr.reserved",
1164 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
1166 { &xmcp_attr_username,
1167 { "Username", "xmcp.attr.username",
1168 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
1170 { &xmcp_attr_message_integrity,
1171 { "Message-Integrity", "xmcp.attr.hmac",
1172 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
1174 { &xmcp_attr_error_reserved,
1175 { "Reserved", "xmcp.attr.error.reserved",
1176 FT_UINT24, BASE_HEX, NULL, 0xFFFFF8, NULL, HFILL }
1178 { &xmcp_attr_error_class,
1179 { "Error Class", "xmcp.attr.error.class",
1180 FT_UINT24, BASE_DEC, NULL, 0x000007, NULL, HFILL}
1182 { &xmcp_attr_error_number,
1183 { "Error Number", "xmcp.attr.error.number",
1184 FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}
1186 { &xmcp_attr_error_code,
1187 { "Error Code", "xmcp.attr.error",
1188 FT_UINT16, BASE_DEC, VALS(error_codes), 0x0, NULL, HFILL}
1190 { &xmcp_attr_error_reason,
1191 { "Error Reason Phrase", "xmcp.attr.error.reason",
1192 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}
1194 { &xmcp_attr_realm,
1195 { "Realm", "xmcp.attr.realm",
1196 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
1198 { &xmcp_attr_nonce,
1199 { "Nonce", "xmcp.attr.nonce",
1200 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
1202 { &xmcp_attr_client_name,
1203 { "Client-Name", "xmcp.attr.client-name",
1204 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
1206 { &xmcp_attr_client_handle,
1207 { "Client-Handle", "xmcp.attr.client-handle",
1208 FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
1210 { &xmcp_attr_version_major,
1211 { "Protocol Major Version", "xmcp.attr.version.major",
1212 FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
1214 { &xmcp_attr_version_minor,
1215 { "Protocol Minor Version", "xmcp.attr.version.minor",
1216 FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
1218 { &xmcp_attr_page_size,
1219 { "Page-Size", "xmcp.attr.page-size",
1220 FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
1222 { &xmcp_attr_client_label,
1223 { "Client-Label", "xmcp.attr.client-label",
1224 FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }
1226 { &xmcp_attr_keepalive,
1227 { "Keepalive", "xmcp.attr.keepalive",
1228 FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
1230 { &xmcp_attr_serv_service,
1231 { "Service ID", "xmcp.attr.service.service",
1232 FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
1234 { &xmcp_attr_serv_subservice,
1235 { "Subservice ID", "xmcp.attr.service.subservice",
1236 FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
1238 { &xmcp_attr_serv_instance,
1239 { "Instance ID", "xmcp.attr.service.instance",
1240 FT_GUID, BASE_NONE, NULL, 0x0, NULL, HFILL }
1242 { &xmcp_attr_servtrans_family,
1243 { "Family", "xmcp.attr.service.transport.family",
1244 FT_UINT8, BASE_HEX, VALS(address_families), 0x0, NULL, HFILL }
1246 { &xmcp_attr_servtrans_port,
1247 { "Port", "xmcp.attr.service.transport.port",
1248 FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }
1250 { &xmcp_attr_servtrans_ipv4,
1251 { "IPv4 Address", "xmcp.attr.service.transport.ipv4",
1252 FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }
1254 { &xmcp_attr_servtrans_ipv6,
1255 { "IPv6 Address", "xmcp.attr.service.transport.ipv6",
1256 FT_IPv6, BASE_NONE, NULL, 0x0, NULL, HFILL }
1258 { &xmcp_attr_service_protocol,
1259 { "Protocol", "xmcp.attr.service.transport.protocol",
1260 FT_UINT8, BASE_DEC|BASE_EXT_STRING, (&ipproto_val_ext),
1261 0x0, NULL, HFILL }
1263 { &xmcp_attr_flag,
1264 { "Flag", "xmcp.attr.flag",
1265 FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }
1267 { &xmcp_attr_flag_type,
1268 { "Flag Type", "xmcp.attr.flag.type",
1269 FT_UINT16, BASE_HEX, VALS(flag_types), 0x0, NULL, HFILL }
1271 { &xmcp_attr_flag_value,
1272 { "Flag Value", "xmcp.attr.flag.value",
1273 FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }
1275 { &xmcp_attr_flag_removal_reason_network_withdraw,
1276 { "Network Withdraw",
1277 "xmcp.attr.flag.removal-reason.network-withdraw",
1278 FT_BOOLEAN, 16, TFS(&tfs_true_false),
1279 XMCP_REMOVAL_REASON_NETWORK_WITHDRAW, NULL, HFILL }
1281 { &xmcp_attr_flag_removal_reason_reserved,
1282 { "Reserved", "xmcp.attr.flag.removal-reason.reserved",
1283 FT_UINT16, BASE_HEX, NULL, XMCP_REMOVAL_REASON_RESERVED, NULL, HFILL }
1285 { &xmcp_attr_flag_trust,
1286 { "Trust", "xmcp.attr.flag.trust",
1287 FT_UINT16, BASE_HEX, VALS(flag_trust_values), 0x0, NULL, HFILL }
1289 { &xmcp_attr_flag_visibility_unauthenticated,
1290 { "Visible to Unauthenticated Clients",
1291 "xmcp.attr.flag.service-visibility.unauthenticated",
1292 FT_BOOLEAN, 16, TFS(&tfs_yes_no),
1293 XMCP_SERVICE_VISIBILITY_UNAUTHENTICATED, NULL, HFILL }
1295 { &xmcp_attr_flag_visibility_reserved,
1296 { "Reserved", "xmcp.attr.flag.service-visibility.reserved",
1297 FT_UINT16, BASE_HEX, NULL,
1298 XMCP_SERVICE_VISIBILITY_RESERVED, NULL, HFILL }
1300 { &xmcp_attr_service_version,
1301 { "Service Version", "xmcp.attr.service.version",
1302 FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
1304 { &xmcp_attr_service_data,
1305 { "Service Data", "xmcp.attr.service.data",
1306 FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }
1308 { &xmcp_attr_subscription_id,
1309 { "Subscription ID", "xmcp.attr.subscription-id",
1310 FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
1312 { &xmcp_attr_service_removed_reason,
1313 { "Service Removed Reason", "xmcp.attr.service-removed-reason",
1314 FT_UINT32, BASE_DEC, VALS(service_removed_reasons), 0x0, NULL, HFILL }
1316 { &xmcp_attr_domain,
1317 { "Domain", "xmcp.attr.domain",
1318 FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }
1322 /* Setup protocol subtree array */
1323 static gint *ett[] = {
1324 &ett_xmcp,
1325 &ett_xmcp_type,
1326 &ett_xmcp_attr_all,
1327 &ett_xmcp_attr,
1328 &ett_xmcp_attr_flag
1331 static ei_register_info ei[] = {
1332 { &ei_xmcp_data_following_message_integrity, { "xmcp.data_following_message_integrity", PI_PROTOCOL, PI_WARN, "Data following message-integrity", EXPFILL }},
1333 { &ei_xmcp_attr_error_number_out_of_range, { "xmcp.attr.error.number.out_of_range", PI_PROTOCOL, PI_WARN, "Error number out of 0-99 range", EXPFILL }},
1334 { &ei_xmcp_attr_error_code_unusual, { "xmcp.attr.error.unusual", PI_RESPONSE_CODE, PI_WARN, "Unusual error code", EXPFILL }},
1335 { &ei_xmcp_attr_realm_incorrect, { "xmcp.attr.realm.incorrect", PI_PROTOCOL, PI_WARN, "Incorrect Realm", EXPFILL }},
1336 { &ei_xmcp_attr_length_bad, { "xmcp.attr.length.bad", PI_PROTOCOL, PI_WARN, "Malformed IPv4 address", EXPFILL }},
1337 { &ei_xmcp_xmcp_attr_servtrans_unknown, { "xmcp.attr.service.transport.unknown", PI_PROTOCOL, PI_WARN, "Unknown transport type", EXPFILL }},
1338 { &ei_xmcp_attr_type_unknown, { "xmcp.attr.type.unknown", PI_PROTOCOL, PI_NOTE, "Unrecognized attribute type", EXPFILL }},
1339 { &ei_xmcp_type_reserved_not_zero, { "xmcp.type.reserved.not_zero", PI_PROTOCOL, PI_WARN, "First two bits not zero", EXPFILL }},
1340 { &ei_xmcp_message_class_reserved, { "xmcp.message_class.reserved", PI_PROTOCOL, PI_WARN, "Reserved message class", EXPFILL }},
1341 { &ei_xmcp_error_response, { "xmcp.error_response", PI_RESPONSE_CODE, PI_NOTE, "Error Response", EXPFILL }},
1342 { &ei_xmcp_msg_type_method_reserved, { "xmcp.msg_type_method.reserved", PI_PROTOCOL, PI_WARN, "Reserved message method", EXPFILL }},
1343 { &ei_xmcp_length_bad, { "xmcp.length.bad", PI_PROTOCOL, PI_ERROR, "XMCP message length exceeds packet length", EXPFILL }},
1344 { &ei_xmcp_magic_cookie_incorrect, { "xmcp.cookie.incorrect", PI_PROTOCOL, PI_WARN, "Magic cookie not correct for XMCP", EXPFILL }},
1345 { &ei_xmcp_response_without_request, { "xmcp.response_without_request", PI_SEQUENCE, PI_NOTE, "Response without corresponding request", EXPFILL }},
1346 { &ei_xmcp_new_session, { "xmcp.new_session", PI_SEQUENCE, PI_CHAT, "New session - Register", EXPFILL }},
1347 { &ei_xmcp_session_termination, { "xmcp.session_termination", PI_SEQUENCE, PI_CHAT, "Session termination", EXPFILL }},
1350 module_t *xmcp_module;
1351 expert_module_t* expert_xmcp;
1353 proto_xmcp = proto_register_protocol("eXtensible Messaging Client Protocol",
1354 "XMCP", "xmcp");
1356 proto_register_field_array(proto_xmcp, hf, array_length(hf));
1357 proto_register_subtree_array(ett, array_length(ett));
1358 expert_xmcp = expert_register_protocol(proto_xmcp);
1359 expert_register_field_array(expert_xmcp, ei, array_length(ei));
1361 /* Register XMCP configuration options */
1362 xmcp_module = prefs_register_protocol(proto_xmcp, proto_reg_handoff_xmcp);
1364 prefs_register_uint_preference(xmcp_module, "tcp.port", "XMCP TCP Port",
1365 "Set the port for XMCP messages (if other"
1366 " than the default of 4788)",
1367 10, &global_xmcp_tcp_port);
1371 void
1372 proto_reg_handoff_xmcp(void)
1374 static gboolean xmcp_prefs_initialized = FALSE;
1375 static dissector_handle_t xmcp_tcp_handle;
1376 static guint xmcp_tcp_port;
1378 if (!xmcp_prefs_initialized) {
1379 xmcp_tcp_handle = new_create_dissector_handle(dissect_xmcp_tcp, proto_xmcp);
1380 heur_dissector_add("tcp", dissect_xmcp_heur, proto_xmcp);
1381 media_type_dissector_table = find_dissector_table("media_type");
1382 xmcp_prefs_initialized = TRUE;
1383 } else {
1384 dissector_delete_uint("tcp.port", xmcp_tcp_port, xmcp_tcp_handle);
1387 xmcp_tcp_port = global_xmcp_tcp_port;
1388 dissector_add_uint("tcp.port", global_xmcp_tcp_port, xmcp_tcp_handle);
1392 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1394 * Local variables:
1395 * c-basic-offset: 2
1396 * tab-width: 8
1397 * indent-tabs-mode: nil
1398 * End:
1400 * vi: set shiftwidth=2 tabstop=8 expandtab:
1401 * :indentSize=2:tabSize=8:noTabs=true: