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