4 * Routines for [MC-NMF] .NET Message Framing Protocol
6 * Copyright 2017 Stefan Metzmacher <metze@samba.org>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <wsutil/str_util.h>
26 #include <epan/packet.h>
27 #include <epan/prefs.h>
28 #include <epan/expert.h>
29 #include <epan/proto_data.h>
30 #include "packet-tcp.h"
31 #include "packet-windows-common.h"
32 #include "packet-gssapi.h"
36 void proto_register_nmf(void);
37 void proto_reg_handoff_nmf(void);
39 static dissector_handle_t gssapi_handle
;
40 static dissector_handle_t gssapi_wrap_handle
;
42 static dissector_handle_t xml_handle
;
47 static int ett_nmf_payload
;
49 static int hf_nmf_record
;
50 static int hf_nmf_record_type
;
51 static int hf_nmf_version_major
;
52 static int hf_nmf_version_minor
;
53 static int hf_nmf_mode_value
;
54 static int hf_nmf_via_length
;
55 static int hf_nmf_via_value
;
56 static int hf_nmf_known_mode_value
;
57 static int hf_nmf_sized_envelope_length
;
58 static int hf_nmf_upgrade_length
;
59 static int hf_nmf_upgrade_protocol
;
60 static int hf_nmf_negotiate_type
;
61 static int hf_nmf_negotiate_length
;
62 static int hf_nmf_protect_length
;
64 static expert_field ei_nmf_bad_record_size
;
66 static bool nmf_reassemble
= true;
68 enum nmf_record_type
{
69 NMF_VERSION_RECORD
= 0x00,
70 NMF_MODE_RECORD
= 0x01,
71 NMF_VIA_RECORD
= 0x02,
72 NMF_KNOWN_ENCODING_RECORD
= 0x03,
73 NMF_EXTENSIBLE_ENCODING_RECORD
= 0x04,
74 NMF_UNSIZED_ENVELOPE_RECORD
= 0x05,
75 NMF_SIZED_ENVELOPE_RECORD
= 0x06,
76 NMF_END_RECORD
= 0x07,
77 NMF_FAULT_RECORD
= 0x08,
78 NMF_UPGRADE_REQUEST_RECORD
= 0x09,
79 NMF_UPGRADE_RESPONSE_RECORD
= 0x0A,
80 NMF_PREAMBLE_ACK_RECORD
= 0x0B,
81 NMF_PREAMBLE_END_RECORD
= 0x0C
84 static const value_string record_types
[] = {
85 { NMF_VERSION_RECORD
, "Version Record"},
86 { NMF_MODE_RECORD
, "Mode Record"},
87 { NMF_VIA_RECORD
, "Via Record"},
88 { NMF_KNOWN_ENCODING_RECORD
, "Known Encoding Record"},
89 { NMF_EXTENSIBLE_ENCODING_RECORD
, "Extensible Encoding Record"},
90 { NMF_UNSIZED_ENVELOPE_RECORD
, "Unsized Envelope Record"},
91 { NMF_SIZED_ENVELOPE_RECORD
, "Sized Envelope Record"},
92 { NMF_END_RECORD
, "End Record"},
93 { NMF_FAULT_RECORD
, "Fault Record"},
94 { NMF_UPGRADE_REQUEST_RECORD
, "Upgrade Request Record"},
95 { NMF_UPGRADE_RESPONSE_RECORD
, "Upgrade Response Record"},
96 { NMF_PREAMBLE_ACK_RECORD
, "Preamble Ack Record"},
97 { NMF_PREAMBLE_END_RECORD
, "Preamble End Record"},
101 static const value_string mode_values
[] = {
102 { 0x01, "Singleton-Unsized"},
108 static const value_string known_mode_values
[] = {
109 { 0x00, "SOAP 1.1 UTF-8"},
110 { 0x01, "SOAP 1.1 UTF-16"},
111 { 0x02, "SOAP 1.1 Unicode Little-Endian"},
112 { 0x03, "SOAP 1.2 UTF-8"},
113 { 0x04, "SOAP 1.2 UTF-16"},
114 { 0x05, "SOAP 1.2 Unicode Little-Endian"},
115 { 0x06, "SOAP 1.2 MOTM"},
116 { 0x07, "SOAP 1.2 Binary"},
117 { 0x08, "SOAP 1.2 Binary with in-band dictionary"},
121 typedef struct nmf_conv_info_t
{
122 uint32_t fnum_upgraded
;
123 uint32_t fnum_negotiated
;
126 /* Read the the varint holding the record size.
127 * Callers MUST check for a 0 return value, which indicates that
128 * parsing the varint failed, to avoid infinite loops.
131 dissect_nmf_record_size(tvbuff_t
*tvb
, proto_tree
*tree
,
132 int hf_index
, int offset
, uint32_t *_size
)
135 int start_offset
= offset
;
137 /* 5 is the encoded size for UINT32_MAX (but can also contain larger
140 unsigned len
= tvb_get_varint(tvb
, offset
, 5, &size
, ENC_VARINT_PROTOBUF
);
142 proto_tree_add_expert_format(tree
, NULL
, &ei_nmf_bad_record_size
, tvb
, offset
, 5,
143 "Invalid record size; varint does not end in five bytes");
146 if (size
> UINT32_MAX
) {
147 proto_tree_add_expert_format(tree
, NULL
, &ei_nmf_bad_record_size
, tvb
, offset
, len
,
148 "Invalid record size %" PRIu64
, size
);
153 *_size
= (uint32_t)size
;
156 if (tree
!= NULL
&& hf_index
!= -1) {
157 proto_item
*item
= NULL
;
158 item
= proto_tree_add_item(tree
, hf_index
, tvb
,
159 start_offset
, -1, ENC_NA
);
160 proto_item_set_len(item
, (int)len
);
161 proto_item_append_text(item
, ": %u (0x%x)",
162 (unsigned)size
, (unsigned)size
);
169 dissect_nmf_record(tvbuff_t
*tvb
, packet_info
*pinfo
,
170 nmf_conv_info_t
*nmf_info
,
171 proto_tree
*tree
, int offset
)
173 proto_item
*record_item
= NULL
;
174 proto_tree
*record_tree
= NULL
;
175 const char *record_name
= NULL
;
176 enum nmf_record_type record_type
;
178 const uint8_t *str
= NULL
;
179 tvbuff_t
*payload_tvb
= NULL
;
180 tvbuff_t
*xml_tvb
= NULL
;
182 record_item
= proto_tree_add_item(tree
, hf_nmf_record
, tvb
, offset
, -1, ENC_NA
);
183 proto_item_append_text(record_item
, ", start_offset=0x%x, ", (unsigned)offset
);
184 record_tree
= proto_item_add_subtree(record_item
, ett_nmf
);
186 record_type
= (enum nmf_record_type
)tvb_get_uint8(tvb
, offset
);
187 record_name
= val_to_str_const((uint32_t)record_type
, record_types
,
189 proto_tree_add_item(record_tree
, hf_nmf_record_type
,
190 tvb
, offset
, 1, ENC_NA
);
193 col_append_str(pinfo
->cinfo
, COL_INFO
, record_name
);
194 proto_item_append_text(record_item
, "%s", record_name
);
196 switch (record_type
) {
197 case NMF_VERSION_RECORD
:
198 proto_tree_add_item(record_tree
, hf_nmf_version_major
,
199 tvb
, offset
, 1, ENC_NA
);
201 proto_tree_add_item(record_tree
, hf_nmf_version_minor
,
202 tvb
, offset
, 1, ENC_NA
);
205 case NMF_MODE_RECORD
:
206 proto_tree_add_item(record_tree
, hf_nmf_mode_value
,
207 tvb
, offset
, 1, ENC_NA
);
211 offset
= dissect_nmf_record_size(tvb
, record_tree
,
218 proto_tree_add_item_ret_string(record_tree
, hf_nmf_via_value
,
219 tvb
, offset
, size
, ENC_UTF_8
,
220 wmem_packet_scope(), &str
);
222 proto_item_append_text(record_item
, ": %s", (const char *)str
);
224 case NMF_KNOWN_ENCODING_RECORD
:
225 proto_tree_add_item(record_tree
, hf_nmf_known_mode_value
,
226 tvb
, offset
, 1, ENC_NA
);
229 case NMF_EXTENSIBLE_ENCODING_RECORD
:
232 case NMF_UNSIZED_ENVELOPE_RECORD
:
235 case NMF_SIZED_ENVELOPE_RECORD
:
236 offset
= dissect_nmf_record_size(tvb
, record_tree
,
237 hf_nmf_sized_envelope_length
,
243 payload_tvb
= tvb_new_subset_length(tvb
, offset
, size
);
245 proto_item_append_text(record_item
, ": Payload (%u byte%s)",
246 size
, plurality(size
, "", "s"));
247 proto_tree_add_format_text(record_tree
, payload_tvb
, 0, size
);
252 * 1. reassemble payload
254 * [MC-NBFSE] .NET Binary Format: SOAP Extension
255 * [MC-NBFS] .NET Binary Format: SOAP Data Structure
256 * [MC-NBFX] .NET Binary Format: XML Data Structure
258 * 3. call the XML dissector.
260 if (payload_tvb
!= NULL
) {
265 if (xml_tvb
!= NULL
) {
266 call_dissector_with_data(xml_handle
, xml_tvb
, pinfo
,
273 case NMF_FAULT_RECORD
:
276 case NMF_UPGRADE_REQUEST_RECORD
:
277 offset
= dissect_nmf_record_size(tvb
, record_tree
,
278 hf_nmf_upgrade_length
,
284 proto_tree_add_item_ret_string(record_tree
, hf_nmf_upgrade_protocol
,
285 tvb
, offset
, size
, ENC_UTF_8
,
286 wmem_packet_scope(), &str
);
288 proto_item_append_text(record_item
, ": %s", (const char *)str
);
291 case NMF_UPGRADE_RESPONSE_RECORD
:
292 nmf_info
->fnum_upgraded
= pinfo
->fd
->num
;
294 case NMF_PREAMBLE_ACK_RECORD
:
297 case NMF_PREAMBLE_END_RECORD
:
302 proto_item_append_text(record_item
, ", end_offset=0x%x", (unsigned)offset
);
303 proto_item_set_end(record_item
, tvb
, offset
);
309 nmf_get_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *_info
)
311 nmf_conv_info_t
*nmf_info
= (nmf_conv_info_t
*)_info
;
312 enum nmf_record_type record_type
;
313 int start_offset
= offset
;
315 if (pinfo
->fd
->num
> nmf_info
->fnum_negotiated
) {
316 unsigned remaining
= tvb_captured_length_remaining(tvb
, offset
);
324 len
= tvb_get_uint32(tvb
, offset
, ENC_LITTLE_ENDIAN
);
330 if (pinfo
->fd
->num
> nmf_info
->fnum_upgraded
) {
331 unsigned remaining
= tvb_captured_length_remaining(tvb
, offset
);
341 len
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
);
347 record_type
= (enum nmf_record_type
)tvb_get_uint8(tvb
, offset
);
350 switch (record_type
) {
352 case NMF_SIZED_ENVELOPE_RECORD
:
353 case NMF_UPGRADE_REQUEST_RECORD
:
355 /* Variable sized record. We must not throw an exception. */
357 unsigned len
= tvb_get_varint(tvb
, offset
,
358 tvb_captured_length_remaining(tvb
, offset
),
359 &size
, ENC_VARINT_PROTOBUF
);
360 /* [MC-NMF] 2.2.2 The record length can be up to UINT32_MAX,
361 * with an encoded size of 5 bytes.
364 /* Parsing failed. */
365 if (tvb_captured_length_remaining(tvb
, offset
) < 5) {
366 /* Fewer than five bytes, so ask for one more segment. */
370 /* We had at least 5 bytes, so the length is invalid.
371 * Just take the rest of this segment.
372 * The expert info will be handled in the main dissection
374 return tvb_reported_length_remaining(tvb
, start_offset
);
376 if (size
> UINT32_MAX
) {
378 return tvb_reported_length_remaining(tvb
, start_offset
);
384 case NMF_VERSION_RECORD
:
387 case NMF_MODE_RECORD
:
390 case NMF_KNOWN_ENCODING_RECORD
:
393 case NMF_EXTENSIBLE_ENCODING_RECORD
:
396 case NMF_UNSIZED_ENVELOPE_RECORD
:
402 case NMF_FAULT_RECORD
:
405 case NMF_UPGRADE_RESPONSE_RECORD
:
407 case NMF_PREAMBLE_ACK_RECORD
:
410 case NMF_PREAMBLE_END_RECORD
:
415 return offset
- start_offset
;
419 dissect_nmf_payload(tvbuff_t
*tvb
, packet_info
*pinfo
,
420 proto_tree
*tree
, nmf_conv_info_t
*nmf_info
)
424 while (tvb_reported_length_remaining(tvb
, offset
) > 0) {
427 ret
= dissect_nmf_record(tvb
, pinfo
, nmf_info
, tree
, offset
);
438 dissect_nmf_pdu(tvbuff_t
*tvb
, packet_info
*pinfo
,
439 proto_tree
*tree
, void *_info
)
441 nmf_conv_info_t
*nmf_info
= (nmf_conv_info_t
*)_info
;
443 pinfo
->fragmented
= true;
445 if (pinfo
->fd
->num
> nmf_info
->fnum_negotiated
) {
446 proto_item
*item
= proto_tree_get_parent(tree
);
449 tvbuff_t
*gssapi_tvb
= NULL
;
450 tvbuff_t
*plain_tvb
= NULL
, *decr_tvb
= NULL
;
452 gssapi_encrypt_info_t gssapi_encrypt
;
454 len
= tvb_get_uint32(tvb
, offset
, ENC_LITTLE_ENDIAN
);
455 proto_tree_add_item(tree
, hf_nmf_negotiate_length
,
456 tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
459 col_set_str(pinfo
->cinfo
, COL_INFO
, "NMF GSSAPI");
460 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
461 "Protected Packet len: %u (0x%x)",
462 (unsigned)len
, (unsigned)len
);
463 proto_item_append_text(item
, ", Protected Packet len: %u (0x%x)",
464 (unsigned)len
, (unsigned)len
);
466 gssapi_tvb
= tvb_new_subset_length(tvb
, offset
, len
);
469 /* Attempt decryption of the GSSAPI wrapped data if possible */
470 memset(&gssapi_encrypt
, 0, sizeof(gssapi_encrypt
));
471 gssapi_encrypt
.decrypt_gssapi_tvb
=DECRYPT_GSSAPI_NORMAL
;
473 ver_len
= call_dissector_with_data(gssapi_wrap_handle
, gssapi_tvb
,
474 pinfo
, tree
, &gssapi_encrypt
);
475 /* if we could unwrap, do a tvb shuffle */
476 if (gssapi_encrypt
.gssapi_decrypted_tvb
) {
477 decr_tvb
=gssapi_encrypt
.gssapi_decrypted_tvb
;
478 } else if (gssapi_encrypt
.gssapi_wrap_tvb
) {
479 plain_tvb
=gssapi_encrypt
.gssapi_wrap_tvb
;
483 * if we don't have unwrapped data,
484 * see if the wrapping involved encryption of the
485 * data; if not, just use the plaintext data.
487 if (!decr_tvb
&& !plain_tvb
) {
488 if(!gssapi_encrypt
.gssapi_data_encrypted
){
489 plain_tvb
= tvb_new_subset_remaining(gssapi_tvb
, ver_len
);
494 proto_tree
*enc_tree
= NULL
;
495 unsigned decr_len
= tvb_reported_length(decr_tvb
);
497 col_set_str(pinfo
->cinfo
, COL_INFO
, "NMF GSS-API Privacy (decrypted): ");
500 enc_tree
= proto_tree_add_subtree_format(tree
, decr_tvb
, 0, -1,
501 ett_nmf_payload
, NULL
,
502 "GSS-API Encrypted payload (%d byte%s)",
504 plurality(decr_len
, "", "s"));
506 dissect_nmf_payload(decr_tvb
, pinfo
, enc_tree
, nmf_info
);
507 } else if (plain_tvb
) {
508 proto_tree
*plain_tree
= NULL
;
509 unsigned plain_len
= tvb_reported_length(plain_tvb
);
511 col_set_str(pinfo
->cinfo
, COL_INFO
, "NMF GSS-API Integrity: ");
514 plain_tree
= proto_tree_add_subtree_format(tree
, plain_tvb
, 0, -1,
515 ett_nmf_payload
, NULL
,
516 "GSS-API payload (%d byte%s)",
518 plurality(plain_len
, "", "s"));
521 dissect_nmf_payload(plain_tvb
, pinfo
, plain_tree
, nmf_info
);
523 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "NMF GSS-API Privacy: payload (%d byte%s)",
524 len
, plurality(len
, "", "s"));
526 proto_tree_add_format_text(tree
, gssapi_tvb
, 0, len
);
531 if (pinfo
->fd
->num
> nmf_info
->fnum_upgraded
) {
532 proto_item
*item
= proto_tree_get_parent(tree
);
533 unsigned rlen
= tvb_reported_length(tvb
);
537 tvbuff_t
*negotiate_tvb
= NULL
;
539 col_set_str(pinfo
->cinfo
, COL_INFO
, "NMF Upgrade");
541 type
= tvb_get_uint8(tvb
, offset
);
542 proto_tree_add_item(tree
, hf_nmf_negotiate_type
,
543 tvb
, offset
, 1, ENC_NA
);
546 nmf_info
->fnum_negotiated
= pinfo
->fd
->num
;
551 len
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
);
552 proto_tree_add_item(tree
, hf_nmf_negotiate_length
,
553 tvb
, offset
, 2, ENC_BIG_ENDIAN
);
556 col_add_fstr(pinfo
->cinfo
, COL_INFO
,
557 "Upgraded Packet rlen: %u (0x%x)",
558 (unsigned)rlen
, (unsigned)rlen
);
559 proto_item_append_text(item
, ", Upgraded Packet rlen: %u (0x%x) len: %u (0x%x) type: 0x%02x",
560 (unsigned)rlen
, (unsigned)rlen
,
561 (unsigned)len
, (unsigned)len
,
563 negotiate_tvb
= tvb_new_subset_length(tvb
, offset
, len
);
565 call_dissector(gssapi_handle
, negotiate_tvb
, pinfo
, tree
);
570 return dissect_nmf_record(tvb
, pinfo
, nmf_info
, tree
, 0);
574 dissect_nmf(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, _U_
void *_unused
)
576 conversation_t
*conv
= NULL
;
577 nmf_conv_info_t
*nmf_info
= NULL
;
578 proto_tree
*tree
= NULL
;
579 proto_item
*item
= NULL
;
581 conv
= find_or_create_conversation(pinfo
);
582 nmf_info
= (nmf_conv_info_t
*)conversation_get_proto_data(conv
,
584 if (nmf_info
== NULL
) {
585 nmf_info
= wmem_new0(wmem_file_scope(), nmf_conv_info_t
);
586 nmf_info
->fnum_upgraded
= 0xffffffff;
587 nmf_info
->fnum_negotiated
= 0xffffffff;
588 conversation_add_proto_data(conv
, proto_nmf
, nmf_info
);
591 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "NMF");
592 col_set_str(pinfo
->cinfo
, COL_INFO
, "NMF...");
594 if (parent_tree
!= NULL
) {
595 item
= proto_tree_add_item(parent_tree
, proto_nmf
, tvb
, 0, -1, ENC_NA
);
596 tree
= proto_item_add_subtree(item
, ett_nmf
);
599 tcp_dissect_pdus(tvb
, pinfo
, tree
, nmf_reassemble
,
600 1, /* fixed_length */
604 return tvb_captured_length(tvb
);
607 void proto_register_nmf(void)
609 static int *ett
[] = {
613 static hf_register_info hf
[] = {
615 { "Record", "nmf.record",
616 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
617 { &hf_nmf_record_type
,
618 { "Type", "nmf.type",
619 FT_UINT8
, BASE_DEC
, VALS(record_types
), 0, NULL
, HFILL
}},
620 { &hf_nmf_version_major
,
621 { "Version Major", "nmf.version.major",
622 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}},
623 { &hf_nmf_version_minor
,
624 { "Version minor", "nmf.version.minor",
625 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}},
626 { &hf_nmf_mode_value
,
627 { "Mode", "nmf.mode.value",
628 FT_UINT8
, BASE_DEC
, VALS(mode_values
), 0, NULL
, HFILL
}},
629 { &hf_nmf_via_length
,
630 { "Length", "nmf.via.length",
631 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
633 { "URI", "nmf.via.uri",
634 FT_STRING
, BASE_NONE
, NULL
, 0x0, "Via URI", HFILL
}},
635 { &hf_nmf_known_mode_value
,
636 { "Mode", "nmf.known_mode.value",
637 FT_UINT8
, BASE_DEC
, VALS(known_mode_values
), 0, NULL
, HFILL
}},
638 { &hf_nmf_sized_envelope_length
,
639 { "Length", "nmf.sized_envelope.length",
640 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
641 { &hf_nmf_upgrade_length
,
642 { "Length", "nmf.upgrade.length",
643 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
644 { &hf_nmf_upgrade_protocol
,
645 { "Upgrade Protocol", "nmf.upgrade.protocol",
646 FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
647 { &hf_nmf_negotiate_type
,
648 { "Negotiate Type", "nmf.negotiate.type",
649 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}},
650 { &hf_nmf_negotiate_length
,
651 { "Negotiate Length", "nmf.negotiate.length",
652 FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
653 { &hf_nmf_protect_length
,
654 { "Protect Length", "nmf.protect.length",
655 FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
657 module_t
*nmf_module
= NULL
;
659 proto_nmf
= proto_register_protocol("NMF (.NET Message Framing Protocol)",
661 proto_register_subtree_array(ett
, array_length(ett
));
662 proto_register_field_array(proto_nmf
, hf
, array_length(hf
));
664 nmf_module
= prefs_register_protocol(proto_nmf
, NULL
);
666 expert_module_t
*expert_nmf
;
667 static ei_register_info ei
[] = {
668 { &ei_nmf_bad_record_size
,
669 { "nmf.bad_record_size", PI_MALFORMED
, PI_WARN
, "Invalid record size varint", EXPFILL
}},
672 expert_nmf
= expert_register_protocol(proto_nmf
);
673 expert_register_field_array(expert_nmf
, ei
, array_length(ei
));
675 prefs_register_bool_preference(nmf_module
,
677 "Reassemble NMF fragments",
678 "Whether the NMF dissector should reassemble fragmented payloads",
684 proto_reg_handoff_nmf(void)
686 dissector_handle_t nmf_handle
;
688 nmf_handle
= create_dissector_handle(dissect_nmf
, proto_nmf
);
689 dissector_add_uint_with_preference("tcp.port", NMF_PORT
, nmf_handle
);
691 gssapi_handle
= find_dissector_add_dependency("gssapi", proto_nmf
);
692 gssapi_wrap_handle
= find_dissector_add_dependency("gssapi_verf", proto_nmf
);
694 xml_handle
= find_dissector_add_dependency("xml", proto_nmf
);