1 /*
2  * packet-winsrepl.c
3  *
4  * Routines for WINS Replication packet dissection
5  *
6  * Copyright 2005 Stefan Metzmacher <metze@samba.org>
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * SPDX-License-Identifier: GPL-2.0-or-later
13  */
14 
15 #include "config.h"
16 
17 #include <epan/packet.h>
18 #include <epan/expert.h>
19 #include <epan/exceptions.h>
20 #include <epan/prefs.h>
21 #include <epan/to_str.h>
22 
23 #include "packet-netbios.h"
24 
25 #include "packet-tcp.h"
26 
27 void proto_register_winsrepl(void);
28 void proto_reg_handoff_winsrepl(void);
29 
30 static gboolean winsrepl_reassemble = TRUE;
31 
32 static int proto_winsrepl = -1;
33 
34 static int hf_winsrepl_size = -1;
35 static int hf_winsrepl_opcode = -1;
36 static int hf_winsrepl_assoc_ctx = -1;
37 static int hf_winsrepl_mess_type = -1;
38 
39 static int hf_winsrepl_start_minor_version = -1;
40 static int hf_winsrepl_start_major_version = -1;
41 
42 static int hf_winsrepl_stop_reason = -1;
43 
44 static int hf_winsrepl_replication_command = -1;
45 
46 static int hf_winsrepl_owner_address = -1;
47 static int hf_winsrepl_owner_max_version = -1;
48 static int hf_winsrepl_owner_min_version = -1;
49 static int hf_winsrepl_owner_type = -1;
50 
51 static int hf_winsrepl_table_partner_count = -1;
52 static int hf_winsrepl_table_initiator = -1;
53 
54 static int hf_winsrepl_ip_owner = -1;
55 static int hf_winsrepl_ip_ip = -1;
56 static int hf_winsrepl_addr_list_num_ips = -1;
57 
58 static int hf_winsrepl_name_len = -1;
59 static int hf_winsrepl_name_flags = -1;
60 static int hf_winsrepl_name_flags_rectype = -1;
61 static int hf_winsrepl_name_flags_recstate = -1;
62 static int hf_winsrepl_name_flags_local = -1;
63 static int hf_winsrepl_name_flags_hosttype = -1;
64 static int hf_winsrepl_name_flags_static = -1;
65 static int hf_winsrepl_name_group_flag = -1;
66 static int hf_winsrepl_name_version_id = -1;
67 static int hf_winsrepl_name_unknown = -1;
68 
69 static int hf_winsrepl_reply_num_names = -1;
70 
71 static gint ett_winsrepl = -1;
72 
73 static gint ett_winsrepl_start = -1;
74 static gint ett_winsrepl_stop = -1;
75 static gint ett_winsrepl_replication = -1;
76 
77 static gint ett_winsrepl_owner = -1;
78 static gint ett_winsrepl_table_reply = -1;
79 
80 static gint ett_winsrepl_ip = -1;
81 static gint ett_winsrepl_addr_list = -1;
82 
83 static gint ett_winsrepl_name = -1;
84 static gint ett_winsrepl_send_reply = -1;
85 
86 static gint ett_winsrepl_flags = -1;
87 
88 static expert_field ei_winsrepl_name_len = EI_INIT;
89 
90 #define WINS_REPLICATION_PORT	( 42 )
91 #define WREPL_OPCODE_BITS	( 0x7800 )
92 
93 enum wrepl_replication_cmd {
94 	WREPL_REPL_TABLE_QUERY=0,
95 	WREPL_REPL_TABLE_REPLY=1,
96 	WREPL_REPL_SEND_REQUEST=2,
97 	WREPL_REPL_SEND_REPLY=3,
98 	WREPL_REPL_UPDATE=4,
99 	WREPL_REPL_UPDATE2=5,
100 	WREPL_REPL_INFORM=8,
101 	WREPL_REPL_INFORM2=9
102 };
103 
104 enum wrepl_mess_type {
105 	WREPL_START_ASSOCIATION=0,
106 	WREPL_START_ASSOCIATION_REPLY=1,
107 	WREPL_STOP_ASSOCIATION=2,
108 	WREPL_REPLICATION=3
109 };
110 
111 static const value_string replication_cmd_vals[] = {
112 	{WREPL_REPL_TABLE_QUERY,	"WREPL_REPL_TABLE_QUERY"},
113 	{WREPL_REPL_TABLE_REPLY,	"WREPL_REPL_TABLE_REPLY"},
114 	{WREPL_REPL_SEND_REQUEST,	"WREPL_REPL_SEND_REQUEST"},
115 	{WREPL_REPL_SEND_REPLY,		"WREPL_REPL_SEND_REPLY"},
116 	{WREPL_REPL_UPDATE,		"WREPL_REPL_UPDATE"},
117 	{WREPL_REPL_UPDATE2,		"WREPL_REPL_UPDATE2"},
118 	{WREPL_REPL_INFORM,		"WREPL_REPL_INFORM"},
119 	{WREPL_REPL_INFORM2,		"WREPL_REPL_INFORM2"},
120 	{0, NULL}
121 };
122 
123 static const value_string message_type_vals[] = {
124 	{WREPL_START_ASSOCIATION,	"WREPL_START_ASSOCIATION"},
125 	{WREPL_START_ASSOCIATION_REPLY,	"WREPL_START_ASSOCIATION_REPLY"},
126 	{WREPL_STOP_ASSOCIATION,	"WREPL_STOP_ASSOCIATION"},
127 	{WREPL_REPLICATION,		"WREPL_REPLICATION"},
128 	{0, NULL}
129 };
130 
131 #define WREPL_NAME_TYPE_MASK		0x03
132 
133 #define WREPL_NAME_TYPE_UNIQUE		0x00
134 #define WREPL_NAME_TYPE_NORMAL_GROUP	0x01
135 #define WREPL_NAME_TYPE_SPECIAL_GROUP	0x02
136 #define WREPL_NAME_TYPE_MULTIHOMED	0x03
137 
138 static const value_string rectype_vals[] = {
139 	{WREPL_NAME_TYPE_UNIQUE,	"Unique"},
140 	{WREPL_NAME_TYPE_NORMAL_GROUP,	"Normal group"},
141 	{WREPL_NAME_TYPE_SPECIAL_GROUP,	"Special group"},
142 	{WREPL_NAME_TYPE_MULTIHOMED,	"Multihomed"},
143 	{0, NULL}
144 };
145 
146 static const value_string recstate_vals[] = {
147 	{0x00,	"Active"},
148 	{0x01,	"Released"},
149 	{0x02,	"Tombstoned"},
150 	{0x03,	"Deleted"},
151 	{0, NULL}
152 };
153 
154 static const value_string hosttype_vals[] = {
155 	{0x00,	"B-node"},
156 	{0x01,	"P-node"},
157 	{0x02,	"M-node"},
158 	{0x03,	"H-node"},
159 	{0, NULL}
160 };
161 
162 static int
163 dissect_winsrepl_start(tvbuff_t *winsrepl_tvb, _U_ packet_info *pinfo,
164 		       int winsrepl_offset, proto_tree *winsrepl_tree)
165 {
166 	proto_tree *start_tree;
167 
168 	start_tree = proto_tree_add_subtree(winsrepl_tree, winsrepl_tvb, winsrepl_offset, -1,
169 									ett_winsrepl_start, NULL, "WREPL_START_ASSOCIATION");
170 
171 	/* ASSOC_CTX */
172 	proto_tree_add_item(start_tree, hf_winsrepl_assoc_ctx, winsrepl_tvb, winsrepl_offset, 4, ENC_BIG_ENDIAN);
173 	winsrepl_offset += 4;
174 
175 	/* MINOR VERSION */
176 	proto_tree_add_item(start_tree, hf_winsrepl_start_minor_version, winsrepl_tvb, winsrepl_offset, 2, ENC_BIG_ENDIAN);
177 	winsrepl_offset += 2;
178 
179 	/* MAJOR VERSION */
180 	proto_tree_add_item(start_tree, hf_winsrepl_start_major_version, winsrepl_tvb, winsrepl_offset, 2, ENC_BIG_ENDIAN);
181 	winsrepl_offset += 2;
182 
183 	return winsrepl_offset;
184 }
185 
186 static int
187 dissect_winsrepl_stop(tvbuff_t *winsrepl_tvb, _U_ packet_info *pinfo,
188 		      int winsrepl_offset, proto_tree *winsrepl_tree)
189 {
190 	guint32 reason;
191 	proto_item *stop_item;
192 	proto_tree *stop_tree;
193 
194 	stop_tree = proto_tree_add_subtree(winsrepl_tree, winsrepl_tvb, winsrepl_offset, -1,
195 							ett_winsrepl_stop, &stop_item, "WREPL_STOP_ASSOCIATION");
196 
197 	/* REASON */
198 	reason = tvb_get_ntohl(winsrepl_tvb, winsrepl_offset);
199 	proto_tree_add_uint(stop_tree, hf_winsrepl_stop_reason, winsrepl_tvb, winsrepl_offset, 4, reason);
200 	winsrepl_offset += 4;
201 
202 	proto_item_append_text(stop_item, ", Reason: 0x%08X", reason);
203 
204 	return winsrepl_offset;
205 }
206 
207 static int
208 dissect_winsrepl_table_query(tvbuff_t *winsrepl_tvb _U_, packet_info *pinfo _U_,
209 			     int winsrepl_offset, proto_tree *winsrepl_tree _U_)
210 {
211 	/* Nothing to do here */
212 	return winsrepl_offset;
213 }
214 
215 static int
216 dissect_winsrepl_wins_owner(tvbuff_t *winsrepl_tvb, _U_ packet_info *pinfo,
217 			    int winsrepl_offset, proto_tree *winsrepl_tree,
218 			    proto_tree *sub_tree, guint32 idx)
219 {
220 	proto_tree *owner_tree = NULL;
221 
222 	if (sub_tree) {
223 		owner_tree = proto_tree_add_subtree_format(sub_tree, winsrepl_tvb, winsrepl_offset, 24,
224 												ett_winsrepl_owner, NULL, "WINS Owner [%u]", idx);
225 	} else if (winsrepl_tree) {
226 		owner_tree = proto_tree_add_subtree(winsrepl_tree, winsrepl_tvb, winsrepl_offset, 24,
227 												ett_winsrepl_owner, NULL, "WINS Owner");
228 	}
229 
230 	/* ADDRESS */
231 	proto_tree_add_item(owner_tree, hf_winsrepl_owner_address, winsrepl_tvb, winsrepl_offset, 4, ENC_BIG_ENDIAN);
232 	winsrepl_offset += 4;
233 
234 	/* MAX_VERSION */
235 	proto_tree_add_item(owner_tree, hf_winsrepl_owner_max_version, winsrepl_tvb, winsrepl_offset, 8, ENC_BIG_ENDIAN);
236 	winsrepl_offset += 8;
237 
238 	/* MIN_VERSION */
239 	proto_tree_add_item(owner_tree, hf_winsrepl_owner_min_version, winsrepl_tvb, winsrepl_offset, 8, ENC_BIG_ENDIAN);
240 	winsrepl_offset += 8;
241 
242 	/* TYPE */
243 	proto_tree_add_item(owner_tree, hf_winsrepl_owner_type, winsrepl_tvb, winsrepl_offset, 4, ENC_BIG_ENDIAN);
244 	winsrepl_offset += 4;
245 
246 	return winsrepl_offset;
247 }
248 
249 static int
250 dissect_winsrepl_table_reply(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
251 			     int winsrepl_offset, proto_tree *winsrepl_tree)
252 {
253 	proto_tree *table_tree;
254 	guint32 partner_count;
255 	guint32 i;
256 
257 	table_tree = proto_tree_add_subtree(winsrepl_tree, winsrepl_tvb, winsrepl_offset, -1,
258 									ett_winsrepl_table_reply, NULL, "WREPL_REPL_TABLE_REPLY");
259 
260 	/* PARTNER COUNT */
261 	partner_count = tvb_get_ntohl(winsrepl_tvb, winsrepl_offset);
262 	proto_tree_add_uint(table_tree, hf_winsrepl_table_partner_count, winsrepl_tvb, winsrepl_offset, 4, partner_count);
263 	winsrepl_offset += 4;
264 
265 	for (i=0; i < partner_count; i++) {
266 		winsrepl_offset = dissect_winsrepl_wins_owner(winsrepl_tvb, pinfo,
267 							      winsrepl_offset, table_tree,
268 							      table_tree, i);
269 	}
270 
271 	/* INITIATOR */
272 	proto_tree_add_item(table_tree, hf_winsrepl_table_initiator, winsrepl_tvb, winsrepl_offset, 4, ENC_BIG_ENDIAN);
273 	winsrepl_offset += 4;
274 
275 	return winsrepl_offset;
276 }
277 
278 static int
279 dissect_winsrepl_send_request(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
280 			     int winsrepl_offset, proto_tree *winsrepl_tree)
281 {
282 	winsrepl_offset = dissect_winsrepl_wins_owner(winsrepl_tvb, pinfo,
283 						      winsrepl_offset, winsrepl_tree,
284 						      NULL, 0);
285 
286 	return winsrepl_offset;
287 }
288 
289 static int
290 dissect_winsrepl_wins_ip(tvbuff_t *winsrepl_tvb, _U_ packet_info *pinfo,
291 			 int winsrepl_offset, proto_tree *winsrepl_tree,
292 			 guint32 *addr, proto_tree *sub_tree, guint32 idx)
293 {
294 	proto_item *ip_item = NULL;
295 	proto_tree *ip_tree = NULL;
296 
297 	if (sub_tree) {
298 		ip_tree = proto_tree_add_subtree_format(sub_tree, winsrepl_tvb, winsrepl_offset, 8,
299 				ett_winsrepl_ip, &ip_item, "WINS IP [%u]", idx);
300 	} else if (winsrepl_tree) {
301 		ip_tree = proto_tree_add_subtree(winsrepl_tree, winsrepl_tvb, winsrepl_offset, 8,
302 				ett_winsrepl_ip, &ip_item, "WINS IP");
303 	}
304 
305 	/* OWNER */
306 	proto_tree_add_item(ip_tree, hf_winsrepl_ip_owner, winsrepl_tvb, winsrepl_offset, 4, ENC_BIG_ENDIAN);
307 	winsrepl_offset += 4;
308 
309 	/* IP */
310 	*addr = tvb_get_ipv4(winsrepl_tvb, winsrepl_offset);
311 	proto_tree_add_ipv4(ip_tree, hf_winsrepl_ip_ip, winsrepl_tvb, winsrepl_offset, 4, *addr);
312 	proto_item_append_text(ip_item, ": %s", tvb_ip_to_str(pinfo->pool, winsrepl_tvb, winsrepl_offset));
313 	winsrepl_offset += 4;
314 
315 	return winsrepl_offset;
316 }
317 
318 static int
319 dissect_winsrepl_wins_address_list(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
320 				   int winsrepl_offset, proto_tree *winsrepl_tree,
321 				   proto_item *parent_item)
322 {
323 	proto_item *addr_list_item;
324 	proto_tree *addr_list_tree;
325 	int old_offset = winsrepl_offset;
326 	guint32 num_ips;
327 	guint32 ip;
328 	guint32 i;
329 	address addr;
330 	gchar* addr_str;
331 
332 	addr_list_tree = proto_tree_add_subtree(winsrepl_tree, winsrepl_tvb, winsrepl_offset, -1,
333 							ett_winsrepl_addr_list, &addr_list_item, "WINS Address List");
334 
335 	/* NUM_IPS */
336 	num_ips = tvb_get_letohl(winsrepl_tvb, winsrepl_offset);
337 	proto_tree_add_uint(addr_list_tree, hf_winsrepl_addr_list_num_ips, winsrepl_tvb, winsrepl_offset, 4, num_ips);
338 	winsrepl_offset += 4;
339 
340 	for (i=0; i < num_ips; i++) {
341 		winsrepl_offset = dissect_winsrepl_wins_ip(winsrepl_tvb, pinfo,
342 							   winsrepl_offset, addr_list_tree,
343 							   &ip, addr_list_tree, i);
344 		set_address(&addr, AT_IPv4, 4, &ip);
345 		addr_str = address_to_str(pinfo->pool, &addr);
346 		if (i == 0) {
347 			proto_item_append_text(parent_item, ": %s", addr_str);
348 			proto_item_append_text(addr_list_item, ": %s", addr_str);
349 		} else {
350 			proto_item_append_text(parent_item, ", %s", addr_str);
351 			proto_item_append_text(addr_list_item, ", %s", addr_str);
352 		}
353 	}
354 
355 	proto_item_set_len(addr_list_item, winsrepl_offset - old_offset);
356 
357 	return winsrepl_offset;
358 }
359 
360 static int
361 dissect_winsrepl_wins_name(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
362 			   int winsrepl_offset, proto_tree *winsrepl_tree,
363 			   proto_tree *sub_tree, guint32 idx)
364 {
365 	proto_item *name_item = NULL, *ti;
366 	proto_tree *name_tree = NULL;
367 	int old_offset = winsrepl_offset;
368 	tvbuff_t *name_tvb = NULL;
369 	guint32 name_len;
370 	char  name_str[(NETBIOS_NAME_LEN - 1)*4 + 1];
371 	int   name_type;
372 	guint32 flags;
373 	static int * const name_flags[] = {
374 		&hf_winsrepl_name_flags_rectype,
375 		&hf_winsrepl_name_flags_recstate,
376 		&hf_winsrepl_name_flags_local,
377 		&hf_winsrepl_name_flags_hosttype,
378 		&hf_winsrepl_name_flags_static,
379 		NULL
380 	};
381 
382 	if (sub_tree) {
383 		name_tree = proto_tree_add_subtree_format(sub_tree, winsrepl_tvb, winsrepl_offset, -1,
384 										ett_winsrepl_name, &name_item, "WINS Name [%u]", idx);
385 	} else if (winsrepl_tree) {
386 		name_tree = proto_tree_add_subtree(winsrepl_tree, winsrepl_tvb, winsrepl_offset, -1,
387 												ett_winsrepl_name, &name_item, "WINS Name");
388 	}
389 
390 	/* NAME_LEN */
391 	name_len = tvb_get_ntohl(winsrepl_tvb, winsrepl_offset);
392 	ti = proto_tree_add_uint(name_tree, hf_winsrepl_name_len, winsrepl_tvb, winsrepl_offset, 4, name_len);
393 	winsrepl_offset += 4;
394 	if (name_len == 0) {
395 		expert_add_info(pinfo, ti, &ei_winsrepl_name_len);
396 		return winsrepl_offset;
397 	}
398 
399 	/* NAME: TODO! */
400 	/*
401 	 * XXX - apparently, according to the Samba code for handling
402 	 * WINS replication, there's a bug in a lot of versions of Windows,
403 	 * including W2K SP2, wherein the first and last bytes of the
404 	 * name (the last byte being the name type) are swapped if
405 	 * the type is 0x1b.  I think I've seen this in at least
406 	 * one capture.
407 	 */
408 	name_tvb = tvb_new_subset_length(winsrepl_tvb, winsrepl_offset, name_len);
409 	netbios_add_name("Name", name_tvb, 0, name_tree);
410 	name_type = get_netbios_name(name_tvb, 0, name_str, (NETBIOS_NAME_LEN - 1)*4 + 1);
411 	proto_item_append_text(name_item, ": %s<%02x>", name_str, name_type);
412 	winsrepl_offset += name_len;
413 
414 	/* ALIGN to 4 Byte */
415 	/* winsrepl_offset += ((winsrepl_offset & (4-1)) == 0 ? 0 : (4 - (winsrepl_offset & (4-1)))); */
416 	/* Windows including w2k8 add 4 padding bytes, when it's already 4 byte
417 	 * aligned... This happens when the name has a "scope" part
418 	 */
419 	winsrepl_offset += 4 - (winsrepl_offset & (4-1));
420 
421 	/* FLAGS */
422 	/*
423 	 * XXX - there appear to be more flag bits, but I didn't see
424 	 * anything in the Samba code about them.
425 	 */
426 	flags = tvb_get_ntohl(winsrepl_tvb, winsrepl_offset);
427 	proto_tree_add_bitmask(name_tree, winsrepl_tvb, winsrepl_offset, hf_winsrepl_name_flags, ett_winsrepl_flags, name_flags, ENC_BIG_ENDIAN);
428 	winsrepl_offset += 4;
429 
430 	/* GROUP_FLAG */
431 	/* XXX - is this just a Boolean? */
432 	proto_tree_add_item(name_tree, hf_winsrepl_name_group_flag, winsrepl_tvb, winsrepl_offset, 4, ENC_LITTLE_ENDIAN);
433 	winsrepl_offset += 4;
434 
435 	/* Version ID */
436 	proto_tree_add_item(name_tree, hf_winsrepl_name_version_id, winsrepl_tvb, winsrepl_offset, 8, ENC_BIG_ENDIAN);
437 	winsrepl_offset += 8;
438 
439 	switch (flags & WREPL_NAME_TYPE_MASK) {
440 
441 	case WREPL_NAME_TYPE_UNIQUE:
442 	case WREPL_NAME_TYPE_NORMAL_GROUP:
443 		/* Single address */
444 		proto_tree_add_item(name_tree, hf_winsrepl_ip_ip, winsrepl_tvb, winsrepl_offset, 4, ENC_BIG_ENDIAN);
445 		proto_item_append_text(name_item, ": %s", tvb_ip_to_str(pinfo->pool, winsrepl_tvb, winsrepl_offset));
446 		winsrepl_offset += 4;
447 		break;
448 
449 	case WREPL_NAME_TYPE_SPECIAL_GROUP:
450 	case WREPL_NAME_TYPE_MULTIHOMED:
451 		/* Address list */
452 		winsrepl_offset = dissect_winsrepl_wins_address_list(winsrepl_tvb, pinfo,
453 								     winsrepl_offset, name_tree,
454 			 					     name_item);
455 		break;
456 	}
457 
458 	/* UNKNOWN, little or big endian??? */
459 	proto_tree_add_item(name_tree, hf_winsrepl_name_unknown, winsrepl_tvb, winsrepl_offset, 4, ENC_BIG_ENDIAN);
460 	winsrepl_offset += 4;
461 
462 	proto_item_set_len(name_item, winsrepl_offset - old_offset);
463 
464 	return winsrepl_offset;
465 }
466 
467 static int
468 dissect_winsrepl_send_reply(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
469 			    int winsrepl_offset, proto_tree *winsrepl_tree)
470 {
471 	proto_tree *rep_tree;
472 	guint32 num_names;
473 	guint32 i;
474 
475 	rep_tree = proto_tree_add_subtree(winsrepl_tree, winsrepl_tvb, winsrepl_offset, -1,
476 								ett_winsrepl_send_reply, NULL, "WREPL_REPL_SEND_REPLY");
477 
478 	/* NUM NAMES */
479 	num_names = tvb_get_ntohl(winsrepl_tvb, winsrepl_offset);
480 	proto_tree_add_uint(rep_tree, hf_winsrepl_reply_num_names, winsrepl_tvb, winsrepl_offset, 4, num_names);
481 	winsrepl_offset += 4;
482 
483 	for (i=0; i < num_names; i++) {
484 		winsrepl_offset = dissect_winsrepl_wins_name(winsrepl_tvb, pinfo,
485 							     winsrepl_offset, rep_tree,
486 							     rep_tree, i);
487 	}
488 
489 	return winsrepl_offset;
490 }
491 
492 static int
493 dissect_winsrepl_update(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
494 			int winsrepl_offset, proto_tree *winsrepl_tree)
495 {
496 	winsrepl_offset = dissect_winsrepl_table_reply(winsrepl_tvb, pinfo,
497 						       winsrepl_offset, winsrepl_tree);
498 	return winsrepl_offset;
499 }
500 
501 static int
502 dissect_winsrepl_update2(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
503 			 int winsrepl_offset, proto_tree *winsrepl_tree)
504 {
505 	winsrepl_offset = dissect_winsrepl_table_reply(winsrepl_tvb, pinfo,
506 						       winsrepl_offset, winsrepl_tree);
507 	return winsrepl_offset;
508 }
509 
510 static int
511 dissect_winsrepl_inform(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
512 			int winsrepl_offset, proto_tree *winsrepl_tree)
513 {
514 	winsrepl_offset = dissect_winsrepl_table_reply(winsrepl_tvb, pinfo,
515 						       winsrepl_offset, winsrepl_tree);
516 	return winsrepl_offset;
517 }
518 
519 static int
520 dissect_winsrepl_inform2(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
521 			 int winsrepl_offset, proto_tree *winsrepl_tree)
522 {
523 	winsrepl_offset = dissect_winsrepl_table_reply(winsrepl_tvb, pinfo,
524 						       winsrepl_offset, winsrepl_tree);
525 	return winsrepl_offset;
526 }
527 
528 static int
529 dissect_winsrepl_replication(tvbuff_t *winsrepl_tvb, packet_info *pinfo,
530 			     int winsrepl_offset, proto_item *winsrepl_item, proto_tree *winsrepl_tree)
531 {
532 	proto_item *repl_item;
533 	proto_tree *repl_tree;
534 	enum wrepl_replication_cmd command;
535 
536 	repl_tree = proto_tree_add_subtree(winsrepl_tree, winsrepl_tvb, winsrepl_offset, -1,
537 							ett_winsrepl_replication, &repl_item, "WREPL_REPLICATION");
538 
539 	/* REPLICATION_CMD */
540 	command = (enum wrepl_replication_cmd)tvb_get_ntohl(winsrepl_tvb, winsrepl_offset);
541 	proto_tree_add_uint(repl_tree, hf_winsrepl_replication_command, winsrepl_tvb, winsrepl_offset, 4, command);
542 	winsrepl_offset += 4;
543 
544 	switch (command) {
545 		case WREPL_REPL_TABLE_QUERY:
546 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_REPL_TABLE_QUERY");
547 			proto_item_append_text(winsrepl_item, ", WREPL_REPL_TABLE_QUERY");
548 			proto_item_append_text(repl_item, ", WREPL_REPL_TABLE_QUERY");
549 			winsrepl_offset = dissect_winsrepl_table_query(winsrepl_tvb, pinfo,
550 								       winsrepl_offset, repl_tree);
551 			break;
552 		case WREPL_REPL_TABLE_REPLY:
553 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_REPL_TABLE_REPLY");
554 			proto_item_append_text(winsrepl_item, ", WREPL_REPL_TABLE_REPLY");
555 			proto_item_append_text(repl_item, ", WREPL_REPL_TABLE_REPLY");
556 			winsrepl_offset = dissect_winsrepl_table_reply(winsrepl_tvb, pinfo,
557 								       winsrepl_offset, repl_tree);
558 			break;
559 		case WREPL_REPL_SEND_REQUEST:
560 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_REPL_SEND_REQUEST");
561 			proto_item_append_text(winsrepl_item, ", WREPL_REPL_SEND_REQUEST");
562 			proto_item_append_text(repl_item, ", WREPL_REPL_SEND_REQUEST");
563 			winsrepl_offset = dissect_winsrepl_send_request(winsrepl_tvb, pinfo,
564 									winsrepl_offset, repl_tree);
565 			break;
566 		case WREPL_REPL_SEND_REPLY:
567 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_REPL_SEND_REPLY");
568 			proto_item_append_text(winsrepl_item, ", WREPL_REPL_SEND_REPLY");
569 			proto_item_append_text(repl_item, ", WREPL_REPL_SEND_REPLY");
570 			winsrepl_offset = dissect_winsrepl_send_reply(winsrepl_tvb, pinfo,
571 								      winsrepl_offset, repl_tree);
572 			break;
573 		case WREPL_REPL_UPDATE:
574 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_REPL_UPDATE");
575 			proto_item_append_text(winsrepl_item, ", WREPL_REPL_UPDATE");
576 			proto_item_append_text(repl_item, ", WREPL_REPL_UPDATE");
577 			winsrepl_offset = dissect_winsrepl_update(winsrepl_tvb, pinfo,
578 								  winsrepl_offset, repl_tree);
579 			break;
580 		case WREPL_REPL_UPDATE2:
581 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_REPL_UPDATE2");
582 			proto_item_append_text(winsrepl_item, ",WREPL_REPL_UPDATE2");
583 			proto_item_append_text(repl_item, ",WREPL_REPL_UPDATE2");
584 			winsrepl_offset = dissect_winsrepl_update2(winsrepl_tvb, pinfo,
585 								   winsrepl_offset, repl_tree);
586 			break;
587 		case WREPL_REPL_INFORM:
588 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_REPL_INFORM");
589 			proto_item_append_text(winsrepl_item, ", WREPL_REPL_INFORM");
590 			proto_item_append_text(repl_item, ", WREPL_REPL_INFORM");
591 			winsrepl_offset = dissect_winsrepl_inform(winsrepl_tvb, pinfo,
592 								  winsrepl_offset, repl_tree);
593 			break;
594 		case WREPL_REPL_INFORM2:
595 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_REPL_INFORM2");
596 			proto_item_append_text(winsrepl_item, ", WREPL_REPL_INFORM2");
597 			proto_item_append_text(repl_item, ", WREPL_REPL_INFORM2");
598 			winsrepl_offset = dissect_winsrepl_inform2(winsrepl_tvb, pinfo,
599 								   winsrepl_offset, repl_tree);
600 			break;
601 	}
602 
603 	return winsrepl_offset;
604 }
605 
606 static int
607 dissect_winsrepl_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
608 {
609 	int offset = 0;
610 	proto_item *winsrepl_item;
611 	proto_tree *winsrepl_tree;
612 	enum wrepl_mess_type mess_type;
613 
614 	col_set_str(pinfo->cinfo, COL_PROTOCOL, "WINS-Replication");
615 	col_clear(pinfo->cinfo, COL_INFO);
616 
617 	winsrepl_item = proto_tree_add_item(parent_tree, proto_winsrepl, tvb, offset, -1, ENC_NA);
618 	winsrepl_tree = proto_item_add_subtree(winsrepl_item, ett_winsrepl);
619 
620 	/* SIZE */
621 	proto_tree_add_item(winsrepl_tree, hf_winsrepl_size, tvb, offset, 4, ENC_BIG_ENDIAN);
622 	offset += 4;
623 
624 	/* OPCODE */
625 	proto_tree_add_item(winsrepl_tree, hf_winsrepl_opcode, tvb, offset, 4, ENC_BIG_ENDIAN);
626 	offset += 4;
627 
628 	/* ASSOC_CTX */
629 	proto_tree_add_item(winsrepl_tree, hf_winsrepl_assoc_ctx, tvb, offset, 4, ENC_BIG_ENDIAN);
630 	offset += 4;
631 
632 	/* MESSAGE_TYPE */
633 	mess_type = (enum wrepl_mess_type)tvb_get_ntohl(tvb, offset);
634 	proto_tree_add_uint(winsrepl_tree, hf_winsrepl_mess_type, tvb, offset, 4, mess_type);
635 	offset += 4;
636 
637 	switch (mess_type) {
638 		case WREPL_START_ASSOCIATION:
639 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_START_ASSOCIATION");
640 			proto_item_append_text(winsrepl_item, ", WREPL_START_ASSOCIATION");
641 			dissect_winsrepl_start(tvb, pinfo,
642 							offset, winsrepl_tree);
643 			break;
644 		case WREPL_START_ASSOCIATION_REPLY:
645 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_START_ASSOCIATION_REPLY");
646 			proto_item_append_text(winsrepl_item, ", WREPL_START_ASSOCIATION_REPLY");
647 			dissect_winsrepl_start(tvb, pinfo,
648 							offset, winsrepl_tree);
649 			break;
650 		case WREPL_STOP_ASSOCIATION:
651 			col_set_str(pinfo->cinfo, COL_INFO, "WREPL_STOP_ASSOCIATION");
652 			proto_item_append_text(winsrepl_item, ", WREPL_STOP_ASSOCIATION");
653 			dissect_winsrepl_stop(tvb, pinfo,
654 						       offset, winsrepl_tree);
655 			break;
656 		case WREPL_REPLICATION:
657 			dissect_winsrepl_replication(tvb, pinfo,
658 							      offset, winsrepl_item, winsrepl_tree);
659 			break;
660 	}
661 
662 	return tvb_captured_length(tvb);
663 }
664 
665 static guint
666 get_winsrepl_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb,
667                      int offset, void *data _U_)
668 {
669 	guint pdu_len;
670 
671 	pdu_len=tvb_get_ntohl(tvb, offset);
672 	return pdu_len+4;
673 }
674 
675 static int
676 dissect_winsrepl(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data)
677 {
678 	tcp_dissect_pdus(tvb, pinfo, parent_tree, winsrepl_reassemble, 4, get_winsrepl_pdu_len, dissect_winsrepl_pdu, data);
679 	return tvb_captured_length(tvb);
680 }
681 
682 void
683 proto_register_winsrepl(void)
684 {
685 	static hf_register_info hf[] = {
686 		{ &hf_winsrepl_size, {
687 			"Packet Size", "winsrepl.size",
688 			FT_UINT32, BASE_DEC, NULL, 0x0,
689 			"WINS Replication Packet Size", HFILL }},
690 
691 		{ &hf_winsrepl_opcode, {
692 			"Opcode", "winsrepl.opcode",
693 			FT_UINT32, BASE_HEX, NULL, 0x0,
694 			"WINS Replication Opcode", HFILL }},
695 
696 		{ &hf_winsrepl_assoc_ctx, {
697 			"Assoc_Ctx", "winsrepl.assoc_ctx",
698 			FT_UINT32, BASE_HEX, NULL, 0x0,
699 			"WINS Replication Assoc_Ctx", HFILL }},
700 
701 		{ &hf_winsrepl_mess_type, {
702 			"Message_Type", "winsrepl.message_type",
703 			FT_UINT32, BASE_DEC, VALS(message_type_vals), 0x0,
704 			"WINS Replication Message_Type", HFILL }},
705 
706 		{ &hf_winsrepl_start_minor_version, {
707 			"Minor Version", "winsrepl.minor_version",
708 			FT_UINT16, BASE_DEC, NULL, 0x0,
709 			"WINS Replication Minor Version", HFILL }},
710 
711 		{ &hf_winsrepl_start_major_version, {
712 			"Major Version", "winsrepl.major_version",
713 			FT_UINT16, BASE_DEC, NULL, 0x0,
714 			"WINS Replication Major Version", HFILL }},
715 
716 		{ &hf_winsrepl_stop_reason, {
717 			"Reason", "winsrepl.reason",
718 			FT_UINT32, BASE_HEX, NULL, 0x0,
719 			"WINS Replication Reason", HFILL }},
720 
721 		{ &hf_winsrepl_replication_command, {
722 			"Replication Command", "winsrepl.repl_cmd",
723 			FT_UINT32, BASE_HEX, VALS(replication_cmd_vals), 0x0,
724 			"WINS Replication Command", HFILL }},
725 
726 		{ &hf_winsrepl_owner_address, {
727 			"Owner Address", "winsrepl.owner_address",
728 			FT_IPv4, BASE_NONE, NULL, 0x0,
729 			"WINS Replication Owner Address", HFILL }},
730 
731 		{ &hf_winsrepl_owner_max_version, {
732 			"Max Version", "winsrepl.max_version",
733 			FT_UINT64, BASE_DEC, NULL, 0x0,
734 			"WINS Replication Max Version", HFILL }},
735 
736 		{ &hf_winsrepl_owner_min_version, {
737 			"Min Version", "winsrepl.min_version",
738 			FT_UINT64, BASE_DEC, NULL, 0x0,
739 			"WINS Replication Min Version", HFILL }},
740 
741 		{ &hf_winsrepl_owner_type, {
742 			"Owner Type", "winsrepl.owner_type",
743 			FT_UINT32, BASE_DEC, NULL, 0x0,
744 			"WINS Replication Owner Type", HFILL }},
745 
746 		{ &hf_winsrepl_table_partner_count, {
747 			"Partner Count", "winsrepl.partner_count",
748 			FT_UINT32, BASE_DEC, NULL, 0x0,
749 			"WINS Replication Partner Count", HFILL }},
750 
751 		{ &hf_winsrepl_table_initiator, {
752 			"Initiator", "winsrepl.initiator",
753 			FT_IPv4, BASE_NONE, NULL, 0x0,
754 			"WINS Replication Initiator", HFILL }},
755 
756 		{ &hf_winsrepl_ip_owner, {
757 			"IP Owner", "winsrepl.ip_owner",
758 			FT_IPv4, BASE_NONE, NULL, 0x0,
759 			"WINS Replication IP Owner", HFILL }},
760 
761 		{ &hf_winsrepl_ip_ip, {
762 			"IP Address", "winsrepl.ip_address",
763 			FT_IPv4, BASE_NONE, NULL, 0x0,
764 			"WINS Replication IP Address", HFILL }},
765 
766 		{ &hf_winsrepl_addr_list_num_ips, {
767 			"Num IPs", "winsrepl.num_ips",
768 			FT_UINT32, BASE_DEC, NULL, 0x0,
769 			"WINS Replication Num IPs", HFILL }},
770 
771 		{ &hf_winsrepl_name_len, {
772 			"Name Len", "winsrepl.name_len",
773 			FT_UINT32, BASE_DEC, NULL, 0x0,
774 			"WINS Replication Name Len", HFILL }},
775 
776 		{ &hf_winsrepl_name_flags, {
777 			"Name Flags", "winsrepl.name_flags",
778 			FT_UINT32, BASE_HEX, NULL, 0x0,
779 			"WINS Replication Name Flags", HFILL }},
780 
781 		{ &hf_winsrepl_name_flags_rectype, {
782 			"Record Type", "winsrepl.name_flags.rectype",
783 			FT_UINT32, BASE_HEX, VALS(rectype_vals), 0x00000003,
784 			"WINS Replication Name Flags Record Type", HFILL }},
785 
786 		{ &hf_winsrepl_name_flags_recstate, {
787 			"Record State", "winsrepl.name_flags.recstate",
788 			FT_UINT32, BASE_HEX, VALS(recstate_vals), 0x0000000C,
789 			"WINS Replication Name Flags Record State", HFILL }},
790 
791 		{ &hf_winsrepl_name_flags_local, {
792 			"Local", "winsrepl.name_flags.local",
793 			FT_BOOLEAN, 32, NULL, 0x00000010,
794 			"WINS Replication Name Flags Local Flag", HFILL }},
795 
796 		{ &hf_winsrepl_name_flags_hosttype, {
797 			"Host Type", "winsrepl.name_flags.hosttype",
798 			FT_UINT32, BASE_HEX, VALS(hosttype_vals), 0x00000060,
799 			"WINS Replication Name Flags Host Type", HFILL }},
800 
801 		{ &hf_winsrepl_name_flags_static, {
802 			"Static", "winsrepl.name_flags.static",
803 			FT_BOOLEAN, 32, NULL, 0x00000080,
804 			"WINS Replication Name Flags Static Flag", HFILL }},
805 
806 		{ &hf_winsrepl_name_group_flag, {
807 			"Name Group Flag", "winsrepl.name_group_flag",
808 			FT_UINT32, BASE_HEX, NULL, 0x0,
809 			"WINS Replication Name Group Flag", HFILL }},
810 
811 		{ &hf_winsrepl_name_version_id, {
812 			"Name Version Id", "winsrepl.name_version_id",
813 			FT_UINT64, BASE_DEC, NULL, 0x0,
814 			"WINS Replication Name Version Id", HFILL }},
815 
816 		{ &hf_winsrepl_name_unknown, {
817 			"Unknown IP", "winsrepl.unknown",
818 			FT_IPv4, BASE_NONE, NULL, 0x0,
819 			"WINS Replication Unknown IP", HFILL }},
820 
821 		{ &hf_winsrepl_reply_num_names, {
822 			"Num Names", "winsrepl.num_names",
823 			FT_UINT32, BASE_DEC, NULL, 0x0,
824 			"WINS Replication Num Names", HFILL }},
825 	};
826 
827 	static gint *ett[] = {
828 		&ett_winsrepl,
829 		&ett_winsrepl_start,
830 		&ett_winsrepl_stop,
831 		&ett_winsrepl_replication,
832 		&ett_winsrepl_owner,
833 		&ett_winsrepl_table_reply,
834 		&ett_winsrepl_ip,
835 		&ett_winsrepl_addr_list,
836 		&ett_winsrepl_name,
837 		&ett_winsrepl_send_reply,
838 		&ett_winsrepl_flags,
839 	};
840 
841 	static ei_register_info ei[] = {
842 		{ &ei_winsrepl_name_len, { "winsrepl.name_len.invalid", PI_MALFORMED, PI_ERROR, "Bad name length", EXPFILL }},
843 	};
844 
845 	module_t *winsrepl_module;
846 	expert_module_t* expert_winsrepl;
847 
848 	proto_winsrepl = proto_register_protocol("WINS (Windows Internet Name Service) Replication",
849 						 "WINS-Replication", "winsrepl");
850 	proto_register_subtree_array(ett, array_length(ett));
851 	proto_register_field_array(proto_winsrepl, hf, array_length(hf));
852 	expert_winsrepl = expert_register_protocol(proto_winsrepl);
853 	expert_register_field_array(expert_winsrepl, ei, array_length(ei));
854 
855 	winsrepl_module = prefs_register_protocol(proto_winsrepl, NULL);
856 	prefs_register_bool_preference(winsrepl_module, "reassemble",
857 		"Reassemble WINS-Replication messages spanning multiple TCP segments",
858 		"Whether the WINS-Replication dissector should reassemble messages spanning multiple TCP segments."
859 		" To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
860 		&winsrepl_reassemble);
861 }
862 
863 void
864 proto_reg_handoff_winsrepl(void)
865 {
866 	dissector_handle_t winsrepl_handle;
867 
868 	winsrepl_handle = create_dissector_handle(dissect_winsrepl, proto_winsrepl);
869 	dissector_add_uint_with_preference("tcp.port", WINS_REPLICATION_PORT, winsrepl_handle);
870 }
871 
872 /*
873  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
874  *
875  * Local variables:
876  * c-basic-offset: 8
877  * tab-width: 8
878  * indent-tabs-mode: t
879  * End:
880  *
881  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
882  * :indentSize=8:tabSize=8:noTabs=false:
883  */
884