• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt/router/samba-3.5.8/source4/wrepl_server/
1/*
2   Unix SMB/CIFS implementation.
3
4   WINS Replication server
5
6   Copyright (C) Stefan Metzmacher	2005
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
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.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "includes.h"
23#include "smbd/service_task.h"
24#include "lib/messaging/irpc.h"
25#include "librpc/gen_ndr/ndr_irpc.h"
26#include "librpc/gen_ndr/ndr_winsrepl.h"
27#include "wrepl_server/wrepl_server.h"
28#include "nbt_server/wins/winsdb.h"
29#include "libcli/wrepl/winsrepl.h"
30#include "system/time.h"
31#include "librpc/gen_ndr/ndr_nbt.h"
32#include "param/param.h"
33
34enum _R_ACTION {
35	R_INVALID,
36	R_DO_REPLACE,
37	R_NOT_REPLACE,
38	R_DO_PROPAGATE,
39	R_DO_CHALLENGE,
40	R_DO_RELEASE_DEMAND,
41	R_DO_SGROUP_MERGE
42};
43
44static const char *_R_ACTION_enum_string(enum _R_ACTION action)
45{
46	switch (action) {
47	case R_INVALID:			return "INVALID";
48	case R_DO_REPLACE:		return "REPLACE";
49	case R_NOT_REPLACE:		return "NOT_REPLACE";
50	case R_DO_PROPAGATE:		return "PROPAGATE";
51	case R_DO_CHALLENGE:		return "CHALLEGNE";
52	case R_DO_RELEASE_DEMAND:	return "RELEASE_DEMAND";
53	case R_DO_SGROUP_MERGE:		return "SGROUP_MERGE";
54	}
55
56	return "enum _R_ACTION unknown";
57}
58
59#define R_IS_ACTIVE(r) ((r)->state == WREPL_STATE_ACTIVE)
60#if 0 /* unused */
61#define R_IS_RELEASED(r) ((r)->state == WREPL_STATE_RELEASED)
62#endif
63#define R_IS_TOMBSTONE(r) ((r)->state == WREPL_STATE_TOMBSTONE)
64
65#define R_IS_UNIQUE(r) ((r)->type == WREPL_TYPE_UNIQUE)
66#define R_IS_GROUP(r) ((r)->type == WREPL_TYPE_GROUP)
67#define R_IS_SGROUP(r) ((r)->type == WREPL_TYPE_SGROUP)
68#if 0 /* unused */
69#define R_IS_MHOMED(r) ((r)->type == WREPL_TYPE_MHOMED)
70#endif
71
72/* blindly overwrite records from the same owner in all cases */
73static enum _R_ACTION replace_same_owner(struct winsdb_record *r1, struct wrepl_name *r2)
74{
75	/* REPLACE */
76	return R_DO_REPLACE;
77}
78
79static bool r_1_is_subset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners)
80{
81	uint32_t i,j;
82	size_t len = winsdb_addr_list_length(r1->addresses);
83
84	for (i=0; i < len; i++) {
85		bool found = false;
86		for (j=0; j < r2->num_addresses; j++) {
87			if (strcmp(r1->addresses[i]->address, r2->addresses[j].address) != 0) {
88				continue;
89			}
90
91			if (check_owners && strcmp(r1->addresses[i]->wins_owner, r2->addresses[j].owner) != 0) {
92				return false;
93			}
94			found = true;
95			break;
96		}
97		if (!found) return false;
98	}
99
100	return true;
101}
102
103static bool r_1_is_superset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners)
104{
105	uint32_t i,j;
106	size_t len = winsdb_addr_list_length(r1->addresses);
107
108	for (i=0; i < r2->num_addresses; i++) {
109		bool found = false;
110		for (j=0; j < len; j++) {
111			if (strcmp(r2->addresses[i].address, r1->addresses[j]->address) != 0) {
112				continue;
113			}
114
115			if (check_owners && strcmp(r2->addresses[i].owner, r1->addresses[j]->wins_owner) != 0) {
116				return false;
117			}
118			found = true;
119			break;
120		}
121		if (!found) return false;
122	}
123
124	return true;
125}
126
127static bool r_1_is_same_as_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners)
128{
129	size_t len = winsdb_addr_list_length(r1->addresses);
130
131	if (len != r2->num_addresses) {
132		return false;
133	}
134
135	return r_1_is_superset_of_2_address_list(r1, r2, check_owners);
136}
137
138static bool r_contains_addrs_from_owner(struct winsdb_record *r1, const char *owner)
139{
140	uint32_t i;
141	size_t len = winsdb_addr_list_length(r1->addresses);
142
143	for (i=0; i < len; i++) {
144		if (strcmp(r1->addresses[i]->wins_owner, owner) == 0) {
145			return true;
146		}
147	}
148
149	return false;
150}
151
152/*
153UNIQUE,ACTIVE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
154UNIQUE,ACTIVE vs. UNIQUE,TOMBSTONE with different ip(s) => NOT REPLACE
155UNIQUE,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
156UNIQUE,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
157UNIQUE,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
158UNIQUE,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
159UNIQUE,ACTIVE vs. GROUP,ACTIVE with different ip(s) => REPLACE
160UNIQUE,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
161UNIQUE,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
162UNIQUE,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
163UNIQUE,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
164UNIQUE,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
165UNIQUE,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE
166UNIQUE,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
167UNIQUE,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
168UNIQUE,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
169UNIQUE,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
170UNIQUE,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
171UNIQUE,ACTIVE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
172UNIQUE,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
173UNIQUE,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE
174UNIQUE,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
175UNIQUE,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
176UNIQUE,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
177*/
178static enum _R_ACTION replace_unique_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
179{
180	if (!R_IS_ACTIVE(r1)) {
181		/* REPLACE */
182		return R_DO_REPLACE;
183	}
184
185	if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) {
186		/* REPLACE */
187		return R_DO_REPLACE;
188	}
189
190	/* NOT REPLACE */
191	return R_NOT_REPLACE;
192}
193
194/*
195GROUP,ACTIVE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
196GROUP,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
197GROUP,RELEASED vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
198GROUP,RELEASED vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
199GROUP,TOMBSTONE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
200GROUP,TOMBSTONE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
201GROUP,ACTIVE vs. GROUP,ACTIVE with same ip(s) => NOT REPLACE
202GROUP,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
203GROUP,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
204GROUP,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
205GROUP,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
206GROUP,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
207GROUP,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE
208GROUP,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
209GROUP,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
210GROUP,RELEASED vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
211GROUP,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
212GROUP,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
213GROUP,ACTIVE vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE
214GROUP,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
215GROUP,RELEASED vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE
216GROUP,RELEASED vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
217GROUP,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
218GROUP,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
219*/
220static enum _R_ACTION replace_group_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
221{
222	if (!R_IS_ACTIVE(r1) && R_IS_GROUP(r2)) {
223		/* REPLACE */
224		return R_DO_REPLACE;
225	}
226
227	if (R_IS_TOMBSTONE(r1) && !R_IS_UNIQUE(r2)) {
228		/* REPLACE */
229		return R_DO_REPLACE;
230	}
231
232	/* NOT REPLACE */
233	return R_NOT_REPLACE;
234}
235
236/*
237SGROUP,ACTIVE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE
238SGROUP,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
239SGROUP,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
240SGROUP,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
241SGROUP,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
242SGROUP,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
243SGROUP,ACTIVE vs. GROUP,ACTIVE with same ip(s) => NOT REPLACE
244SGROUP,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
245SGROUP,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
246SGROUP,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
247SGROUP,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
248SGROUP,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
249SGROUP,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
250SGROUP,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
251SGROUP,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
252SGROUP,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
253SGROUP,ACTIVE vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE
254SGROUP,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
255SGROUP,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE
256SGROUP,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
257SGROUP,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
258SGROUP,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
259
260SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4 => NOT REPLACE
261SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:NULL => NOT REPLACE
262SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4 => NOT REPLACE
263SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4 vs. B:A_3_4 => REPLACE
264SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4_OWNER_B => REPLACE
265SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_OWNER_B vs. B:A_3_4 => REPLACE
266
267SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4 => SGROUP_MERGE
268SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4_X_3_4 => SGROUP_MERGE
269SGROUP,ACTIVE vs. SGROUP,ACTIVE A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4 => SGROUP_MERGE
270SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4_OWNER_B => B:A_3_4_OWNER_B_X_3_4 => SGROUP_MERGE
271SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:B_3_4_X_1_2 => C:B_3_4_X_1_2_3_4 => SGROUP_MERGE
272SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4 => SGROUP_MERGE
273
274
275this is a bit strange, incoming tombstone replicas always replace old replicas:
276
277SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:NULL => B:NULL => REPLACE
278SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4 => REPLACE
279SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:B_3_4 => B:B_3_4 => REPLACE
280SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:B_3_4_X_3_4 => B:B_3_4_X_3_4 => REPLACE
281*/
282static enum _R_ACTION replace_sgroup_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
283{
284	if (!R_IS_ACTIVE(r1)) {
285		/* REPLACE */
286		return R_DO_REPLACE;
287	}
288
289	if (!R_IS_SGROUP(r2)) {
290		/* NOT REPLACE */
291		return R_NOT_REPLACE;
292	}
293
294	/*
295	 * this is strange, but correct
296	 * the incoming tombstone replace the current active
297	 * record
298	 */
299	if (!R_IS_ACTIVE(r2)) {
300		/* REPLACE */
301		return R_DO_REPLACE;
302	}
303
304	if (r2->num_addresses == 0) {
305		if (r_contains_addrs_from_owner(r1, r2->owner)) {
306			/* not handled here: MERGE */
307			return R_DO_SGROUP_MERGE;
308		}
309
310		/* NOT REPLACE */
311		return R_NOT_REPLACE;
312	}
313
314	if (r_1_is_superset_of_2_address_list(r1, r2, true)) {
315		/* NOT REPLACE */
316		return R_NOT_REPLACE;
317	}
318
319	if (r_1_is_same_as_2_address_list(r1, r2, false)) {
320		/* REPLACE */
321		return R_DO_REPLACE;
322	}
323
324	/* not handled here: MERGE */
325	return R_DO_SGROUP_MERGE;
326}
327
328/*
329MHOMED,ACTIVE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
330MHOMED,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE
331MHOMED,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
332MHOMED,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
333MHOMED,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE
334MHOMED,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE
335MHOMED,ACTIVE vs. GROUP,ACTIVE with different ip(s) => REPLACE
336MHOMED,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE
337MHOMED,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE
338MHOMED,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
339MHOMED,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE
340MHOMED,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE
341MHOMED,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE
342MHOMED,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE
343MHOMED,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE
344MHOMED,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
345MHOMED,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE
346MHOMED,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE
347MHOMED,ACTIVE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
348MHOMED,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE
349MHOMED,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE
350MHOMED,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
351MHOMED,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE
352MHOMED,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE
353*/
354static enum _R_ACTION replace_mhomed_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
355{
356	if (!R_IS_ACTIVE(r1)) {
357		/* REPLACE */
358		return R_DO_REPLACE;
359	}
360
361	if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) {
362		/* REPLACE */
363		return R_DO_REPLACE;
364	}
365
366	/* NOT REPLACE */
367	return R_NOT_REPLACE;
368}
369
370/*
371active:
372_UA_UA_SI_U<00> => REPLACE
373_UA_UA_DI_P<00> => NOT REPLACE
374_UA_UA_DI_O<00> => NOT REPLACE
375_UA_UA_DI_N<00> => REPLACE
376_UA_UT_SI_U<00> => NOT REPLACE
377_UA_UT_DI_U<00> => NOT REPLACE
378_UA_GA_SI_R<00> => REPLACE
379_UA_GA_DI_R<00> => REPLACE
380_UA_GT_SI_U<00> => NOT REPLACE
381_UA_GT_DI_U<00> => NOT REPLACE
382_UA_SA_SI_R<00> => REPLACE
383_UA_SA_DI_R<00> => REPLACE
384_UA_ST_SI_U<00> => NOT REPLACE
385_UA_ST_DI_U<00> => NOT REPLACE
386_UA_MA_SI_U<00> => REPLACE
387_UA_MA_SP_U<00> => REPLACE
388_UA_MA_DI_P<00> => NOT REPLACE
389_UA_MA_DI_O<00> => NOT REPLACE
390_UA_MA_DI_N<00> => REPLACE
391_UA_MT_SI_U<00> => NOT REPLACE
392_UA_MT_DI_U<00> => NOT REPLACE
393Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
394_UA_UA_DI_A<00> => MHOMED_MERGE
395_UA_MA_DI_A<00> => MHOMED_MERGE
396
397released:
398_UR_UA_SI<00> => REPLACE
399_UR_UA_DI<00> => REPLACE
400_UR_UT_SI<00> => REPLACE
401_UR_UT_DI<00> => REPLACE
402_UR_GA_SI<00> => REPLACE
403_UR_GA_DI<00> => REPLACE
404_UR_GT_SI<00> => REPLACE
405_UR_GT_DI<00> => REPLACE
406_UR_SA_SI<00> => REPLACE
407_UR_SA_DI<00> => REPLACE
408_UR_ST_SI<00> => REPLACE
409_UR_ST_DI<00> => REPLACE
410_UR_MA_SI<00> => REPLACE
411_UR_MA_DI<00> => REPLACE
412_UR_MT_SI<00> => REPLACE
413_UR_MT_DI<00> => REPLACE
414*/
415static enum _R_ACTION replace_unique_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
416{
417	if (!R_IS_ACTIVE(r1)) {
418		/* REPLACE */
419		return R_DO_REPLACE;
420	}
421
422	if (!R_IS_ACTIVE(r2)) {
423		/* NOT REPLACE, and PROPAGATE */
424		return R_DO_PROPAGATE;
425	}
426
427	if (R_IS_GROUP(r2) || R_IS_SGROUP(r2)) {
428		/* REPLACE and send a release demand to the old name owner */
429		return R_DO_RELEASE_DEMAND;
430	}
431
432	/*
433	 * here we only have unique,active,owned vs.
434	 * is unique,active,replica or mhomed,active,replica
435	 */
436
437	if (r_1_is_subset_of_2_address_list(r1, r2, false)) {
438		/*
439		 * if r1 has a subset(or same) of the addresses of r2
440		 * <=>
441		 * if r2 has a superset(or same) of the addresses of r1
442		 *
443		 * then replace the record
444		 */
445		return R_DO_REPLACE;
446	}
447
448	/*
449	 * in any other case, we need to do
450	 * a name request to the old name holder
451	 * to see if it's still there...
452	 */
453	return R_DO_CHALLENGE;
454}
455
456/*
457active:
458_GA_UA_SI_U<00> => NOT REPLACE
459_GA_UA_DI_U<00> => NOT REPLACE
460_GA_UT_SI_U<00> => NOT REPLACE
461_GA_UT_DI_U<00> => NOT REPLACE
462_GA_GA_SI_U<00> => REPLACE
463_GA_GA_DI_U<00> => REPLACE
464_GA_GT_SI_U<00> => NOT REPLACE
465_GA_GT_DI_U<00> => NOT REPLACE
466_GA_SA_SI_U<00> => NOT REPLACE
467_GA_SA_DI_U<00> => NOT REPLACE
468_GA_ST_SI_U<00> => NOT REPLACE
469_GA_ST_DI_U<00> => NOT REPLACE
470_GA_MA_SI_U<00> => NOT REPLACE
471_GA_MA_DI_U<00> => NOT REPLACE
472_GA_MT_SI_U<00> => NOT REPLACE
473_GA_MT_DI_U<00> => NOT REPLACE
474
475released:
476_GR_UA_SI<00> => NOT REPLACE
477_GR_UA_DI<00> => NOT REPLACE
478_GR_UT_SI<00> => NOT REPLACE
479_GR_UT_DI<00> => NOT REPLACE
480_GR_GA_SI<00> => REPLACE
481_GR_GA_DI<00> => REPLACE
482_GR_GT_SI<00> => REPLACE
483_GR_GT_DI<00> => REPLACE
484_GR_SA_SI<00> => NOT REPLACE
485_GR_SA_DI<00> => NOT REPLACE
486_GR_ST_SI<00> => NOT REPLACE
487_GR_ST_DI<00> => NOT REPLACE
488_GR_MA_SI<00> => NOT REPLACE
489_GR_MA_DI<00> => NOT REPLACE
490_GR_MT_SI<00> => NOT REPLACE
491_GR_MT_DI<00> => NOT REPLACE
492*/
493static enum _R_ACTION replace_group_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
494{
495	if (R_IS_GROUP(r1) && R_IS_GROUP(r2)) {
496		if (!R_IS_ACTIVE(r1) || R_IS_ACTIVE(r2)) {
497			/* REPLACE */
498			return R_DO_REPLACE;
499		}
500	}
501
502	/* NOT REPLACE, but PROPAGATE */
503	return R_DO_PROPAGATE;
504}
505
506/*
507active (not sgroup vs. sgroup yet!):
508_SA_UA_SI_U<1c> => NOT REPLACE
509_SA_UA_DI_U<1c> => NOT REPLACE
510_SA_UT_SI_U<1c> => NOT REPLACE
511_SA_UT_DI_U<1c> => NOT REPLACE
512_SA_GA_SI_U<1c> => NOT REPLACE
513_SA_GA_DI_U<1c> => NOT REPLACE
514_SA_GT_SI_U<1c> => NOT REPLACE
515_SA_GT_DI_U<1c> => NOT REPLACE
516_SA_MA_SI_U<1c> => NOT REPLACE
517_SA_MA_DI_U<1c> => NOT REPLACE
518_SA_MT_SI_U<1c> => NOT REPLACE
519_SA_MT_DI_U<1c> => NOT REPLACE
520
521Test Replica vs. owned active: SGROUP vs. SGROUP tests
522_SA_SA_DI_U<1c> => SGROUP_MERGE
523_SA_SA_SI_U<1c> => SGROUP_MERGE
524_SA_SA_SP_U<1c> => SGROUP_MERGE
525_SA_SA_SB_U<1c> => SGROUP_MERGE
526_SA_ST_DI_U<1c> => NOT REPLACE
527_SA_ST_SI_U<1c> => NOT REPLACE
528_SA_ST_SP_U<1c> => NOT REPLACE
529_SA_ST_SB_U<1c> => NOT REPLACE
530
531SGROUP,ACTIVE vs. SGROUP,* is not handled here!
532
533released:
534_SR_UA_SI<1c> => REPLACE
535_SR_UA_DI<1c> => REPLACE
536_SR_UT_SI<1c> => REPLACE
537_SR_UT_DI<1c> => REPLACE
538_SR_GA_SI<1c> => REPLACE
539_SR_GA_DI<1c> => REPLACE
540_SR_GT_SI<1c> => REPLACE
541_SR_GT_DI<1c> => REPLACE
542_SR_SA_SI<1c> => REPLACE
543_SR_SA_DI<1c> => REPLACE
544_SR_ST_SI<1c> => REPLACE
545_SR_ST_DI<1c> => REPLACE
546_SR_MA_SI<1c> => REPLACE
547_SR_MA_DI<1c> => REPLACE
548_SR_MT_SI<1c> => REPLACE
549_SR_MT_DI<1c> => REPLACE
550*/
551static enum _R_ACTION replace_sgroup_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
552{
553	if (!R_IS_ACTIVE(r1)) {
554		/* REPLACE */
555		return R_DO_REPLACE;
556	}
557
558	if (!R_IS_SGROUP(r2) || !R_IS_ACTIVE(r2)) {
559		/* NOT REPLACE, but PROPAGATE */
560		return R_DO_PROPAGATE;
561	}
562
563	if (r_1_is_same_as_2_address_list(r1, r2, true)) {
564		/*
565		 * as we're the old owner and the addresses and their
566		 * owners are identical
567		 */
568		return R_NOT_REPLACE;
569	}
570
571	/* not handled here: MERGE */
572	return R_DO_SGROUP_MERGE;
573}
574
575/*
576active:
577_MA_UA_SI_U<00> => REPLACE
578_MA_UA_DI_P<00> => NOT REPLACE
579_MA_UA_DI_O<00> => NOT REPLACE
580_MA_UA_DI_N<00> => REPLACE
581_MA_UT_SI_U<00> => NOT REPLACE
582_MA_UT_DI_U<00> => NOT REPLACE
583_MA_GA_SI_R<00> => REPLACE
584_MA_GA_DI_R<00> => REPLACE
585_MA_GT_SI_U<00> => NOT REPLACE
586_MA_GT_DI_U<00> => NOT REPLACE
587_MA_SA_SI_R<00> => REPLACE
588_MA_SA_DI_R<00> => REPLACE
589_MA_ST_SI_U<00> => NOT REPLACE
590_MA_ST_DI_U<00> => NOT REPLACE
591_MA_MA_SI_U<00> => REPLACE
592_MA_MA_SP_U<00> => REPLACE
593_MA_MA_DI_P<00> => NOT REPLACE
594_MA_MA_DI_O<00> => NOT REPLACE
595_MA_MA_DI_N<00> => REPLACE
596_MA_MT_SI_U<00> => NOT REPLACE
597_MA_MT_DI_U<00> => NOT REPLACE
598Test Replica vs. owned active: some more MHOMED combinations
599_MA_MA_SP_U<00> => REPLACE
600_MA_MA_SM_U<00> => REPLACE
601_MA_MA_SB_P<00> => MHOMED_MERGE
602_MA_MA_SB_A<00> => MHOMED_MERGE
603_MA_MA_SB_PRA<00> => NOT REPLACE
604_MA_MA_SB_O<00> => NOT REPLACE
605_MA_MA_SB_N<00> => REPLACE
606Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
607_MA_UA_SB_P<00> => MHOMED_MERGE
608
609released:
610_MR_UA_SI<00> => REPLACE
611_MR_UA_DI<00> => REPLACE
612_MR_UT_SI<00> => REPLACE
613_MR_UT_DI<00> => REPLACE
614_MR_GA_SI<00> => REPLACE
615_MR_GA_DI<00> => REPLACE
616_MR_GT_SI<00> => REPLACE
617_MR_GT_DI<00> => REPLACE
618_MR_SA_SI<00> => REPLACE
619_MR_SA_DI<00> => REPLACE
620_MR_ST_SI<00> => REPLACE
621_MR_ST_DI<00> => REPLACE
622_MR_MA_SI<00> => REPLACE
623_MR_MA_DI<00> => REPLACE
624_MR_MT_SI<00> => REPLACE
625_MR_MT_DI<00> => REPLACE
626*/
627static enum _R_ACTION replace_mhomed_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
628{
629	if (!R_IS_ACTIVE(r1)) {
630		/* REPLACE */
631		return R_DO_REPLACE;
632	}
633
634	if (!R_IS_ACTIVE(r2)) {
635		/* NOT REPLACE, but PROPAGATE */
636		return R_DO_PROPAGATE;
637	}
638
639	if (R_IS_GROUP(r2) || R_IS_SGROUP(r2)) {
640		/* REPLACE and send a release demand to the old name owner */
641		return R_DO_RELEASE_DEMAND;
642	}
643
644	/*
645	 * here we only have mhomed,active,owned vs.
646	 * is unique,active,replica or mhomed,active,replica
647	 */
648
649	if (r_1_is_subset_of_2_address_list(r1, r2, false)) {
650		/*
651		 * if r1 has a subset(or same) of the addresses of r2
652		 * <=>
653		 * if r2 has a superset(or same) of the addresses of r1
654		 *
655		 * then replace the record
656		 */
657		return R_DO_REPLACE;
658	}
659
660	/*
661	 * in any other case, we need to do
662	 * a name request to the old name holder
663	 * to see if it's still there...
664	 */
665	return R_DO_CHALLENGE;
666}
667
668static NTSTATUS r_do_add(struct wreplsrv_partner *partner,
669			 TALLOC_CTX *mem_ctx,
670			 struct wrepl_wins_owner *owner,
671			 struct wrepl_name *replica)
672{
673	struct winsdb_record *rec;
674	uint32_t i;
675	uint8_t ret;
676
677	rec = talloc(mem_ctx, struct winsdb_record);
678	NT_STATUS_HAVE_NO_MEMORY(rec);
679
680	rec->name	= &replica->name;
681	rec->type	= replica->type;
682	rec->state	= replica->state;
683	rec->node	= replica->node;
684	rec->is_static	= replica->is_static;
685	rec->expire_time= time(NULL) + partner->service->config.verify_interval;
686	rec->version	= replica->version_id;
687	rec->wins_owner	= replica->owner;
688	rec->addresses	= winsdb_addr_list_make(rec);
689	NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
690	rec->registered_by = NULL;
691
692	for (i=0; i < replica->num_addresses; i++) {
693		/* TODO: find out if rec->expire_time is correct here */
694		rec->addresses = winsdb_addr_list_add(partner->service->wins_db,
695						      rec, rec->addresses,
696						      replica->addresses[i].address,
697						      replica->addresses[i].owner,
698						      rec->expire_time,
699						      false);
700		NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
701	}
702
703	ret = winsdb_add(partner->service->wins_db, rec, 0);
704	if (ret != NBT_RCODE_OK) {
705		DEBUG(0,("Failed to add record %s: %u\n",
706			nbt_name_string(mem_ctx, &replica->name), ret));
707		return NT_STATUS_FOOBAR;
708	}
709
710	DEBUG(4,("added record %s\n",
711		nbt_name_string(mem_ctx, &replica->name)));
712
713	return NT_STATUS_OK;
714}
715
716static NTSTATUS r_do_replace(struct wreplsrv_partner *partner,
717			     TALLOC_CTX *mem_ctx,
718			     struct winsdb_record *rec,
719			     struct wrepl_wins_owner *owner,
720			     struct wrepl_name *replica)
721{
722	uint32_t i;
723	uint8_t ret;
724
725	rec->name	= &replica->name;
726	rec->type	= replica->type;
727	rec->state	= replica->state;
728	rec->node	= replica->node;
729	rec->is_static	= replica->is_static;
730	rec->expire_time= time(NULL) + partner->service->config.verify_interval;
731	rec->version	= replica->version_id;
732	rec->wins_owner	= replica->owner;
733	rec->addresses	= winsdb_addr_list_make(rec);
734	NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
735	rec->registered_by = NULL;
736
737	for (i=0; i < replica->num_addresses; i++) {
738		/* TODO: find out if rec->expire_time is correct here */
739		rec->addresses = winsdb_addr_list_add(partner->service->wins_db,
740						      rec, rec->addresses,
741						      replica->addresses[i].address,
742						      replica->addresses[i].owner,
743						      rec->expire_time,
744						      false);
745		NT_STATUS_HAVE_NO_MEMORY(rec->addresses);
746	}
747
748	ret = winsdb_modify(partner->service->wins_db, rec, 0);
749	if (ret != NBT_RCODE_OK) {
750		DEBUG(0,("Failed to replace record %s: %u\n",
751			nbt_name_string(mem_ctx, &replica->name), ret));
752		return NT_STATUS_FOOBAR;
753	}
754
755	DEBUG(4,("replaced record %s\n",
756		nbt_name_string(mem_ctx, &replica->name)));
757
758	return NT_STATUS_OK;
759}
760
761static NTSTATUS r_not_replace(struct wreplsrv_partner *partner,
762			      TALLOC_CTX *mem_ctx,
763			      struct winsdb_record *rec,
764			      struct wrepl_wins_owner *owner,
765			      struct wrepl_name *replica)
766{
767	DEBUG(4,("not replace record %s\n",
768		 nbt_name_string(mem_ctx, &replica->name)));
769	return NT_STATUS_OK;
770}
771
772static NTSTATUS r_do_propagate(struct wreplsrv_partner *partner,
773			       TALLOC_CTX *mem_ctx,
774			       struct winsdb_record *rec,
775			       struct wrepl_wins_owner *owner,
776			       struct wrepl_name *replica)
777{
778	uint8_t ret;
779	uint32_t modify_flags;
780
781	/*
782	 * allocate a new version id for the record to that it'll be replicated
783	 */
784	modify_flags	= WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
785
786	ret = winsdb_modify(partner->service->wins_db, rec, modify_flags);
787	if (ret != NBT_RCODE_OK) {
788		DEBUG(0,("Failed to replace record %s: %u\n",
789			nbt_name_string(mem_ctx, &replica->name), ret));
790		return NT_STATUS_FOOBAR;
791	}
792
793	DEBUG(4,("propagated record %s\n",
794		 nbt_name_string(mem_ctx, &replica->name)));
795
796	return NT_STATUS_OK;
797}
798
799/*
800Test Replica vs. owned active: some more MHOMED combinations
801_MA_MA_SP_U<00>: C:MHOMED vs. B:ALL => B:ALL => REPLACE
802_MA_MA_SM_U<00>: C:MHOMED vs. B:MHOMED => B:MHOMED => REPLACE
803_MA_MA_SB_P<00>: C:MHOMED vs. B:BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
804_MA_MA_SB_A<00>: C:MHOMED vs. B:BEST (C:ALL) => B:MHOMED => MHOMED_MERGE
805_MA_MA_SB_PRA<00>: C:MHOMED vs. B:BEST (C:BEST) => C:MHOMED => NOT REPLACE
806_MA_MA_SB_O<00>: C:MHOMED vs. B:BEST (B:B_3_4) =>C:MHOMED => NOT REPLACE
807_MA_MA_SB_N<00>: C:MHOMED vs. B:BEST (NEGATIVE) => B:BEST => REPLACE
808Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
809_MA_UA_SB_P<00>: C:MHOMED vs. B:UNIQUE,BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
810_UA_UA_DI_PRA<00>: C:BEST vs. B:BEST2 (C:BEST2,LR:BEST2) => C:BEST => NOT REPLACE
811_UA_UA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
812_UA_MA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
813*/
814static NTSTATUS r_do_mhomed_merge(struct wreplsrv_partner *partner,
815				  TALLOC_CTX *mem_ctx,
816				  struct winsdb_record *rec,
817				  struct wrepl_wins_owner *owner,
818				  struct wrepl_name *replica)
819{
820	struct winsdb_record *merge;
821	uint32_t i,j;
822	uint8_t ret;
823	size_t len;
824
825	merge = talloc(mem_ctx, struct winsdb_record);
826	NT_STATUS_HAVE_NO_MEMORY(merge);
827
828	merge->name		= &replica->name;
829	merge->type		= WREPL_TYPE_MHOMED;
830	merge->state		= replica->state;
831	merge->node		= replica->node;
832	merge->is_static	= replica->is_static;
833	merge->expire_time	= time(NULL) + partner->service->config.verify_interval;
834	merge->version		= replica->version_id;
835	merge->wins_owner	= replica->owner;
836	merge->addresses	= winsdb_addr_list_make(merge);
837	NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
838	merge->registered_by = NULL;
839
840	for (i=0; i < replica->num_addresses; i++) {
841		merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
842							merge, merge->addresses,
843							replica->addresses[i].address,
844							replica->addresses[i].owner,
845							merge->expire_time,
846							false);
847		NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
848	}
849
850	len = winsdb_addr_list_length(rec->addresses);
851
852	for (i=0; i < len; i++) {
853		bool found = false;
854		for (j=0; j < replica->num_addresses; j++) {
855			if (strcmp(replica->addresses[j].address, rec->addresses[i]->address) == 0) {
856				found = true;
857				break;
858			}
859		}
860		if (found) continue;
861
862		merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
863							merge, merge->addresses,
864							rec->addresses[i]->address,
865							rec->addresses[i]->wins_owner,
866							rec->addresses[i]->expire_time,
867							false);
868		NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
869	}
870
871	ret = winsdb_modify(partner->service->wins_db, merge, 0);
872	if (ret != NBT_RCODE_OK) {
873		DEBUG(0,("Failed to modify mhomed merge record %s: %u\n",
874			nbt_name_string(mem_ctx, &replica->name), ret));
875		return NT_STATUS_FOOBAR;
876	}
877
878	DEBUG(4,("mhomed merge record %s\n",
879		nbt_name_string(mem_ctx, &replica->name)));
880
881	return NT_STATUS_OK;
882}
883
884struct r_do_challenge_state {
885	struct messaging_context *msg_ctx;
886	struct wreplsrv_partner *partner;
887	struct winsdb_record *rec;
888	struct wrepl_wins_owner owner;
889	struct wrepl_name replica;
890	struct nbtd_proxy_wins_challenge r;
891};
892
893static void r_do_late_release_demand_handler(struct irpc_request *ireq)
894{
895	NTSTATUS status;
896	struct r_do_challenge_state *state = talloc_get_type(ireq->async.private_data,
897							     struct r_do_challenge_state);
898
899	status = irpc_call_recv(ireq);
900	/* don't care about the result */
901	talloc_free(state);
902}
903
904static NTSTATUS r_do_late_release_demand(struct r_do_challenge_state *state)
905{
906	struct irpc_request *ireq;
907	struct server_id *nbt_servers;
908	struct nbtd_proxy_wins_release_demand r;
909	uint32_t i;
910
911	DEBUG(4,("late release demand record %s\n",
912		 nbt_name_string(state, &state->replica.name)));
913
914	nbt_servers = irpc_servers_byname(state->msg_ctx, state, "nbt_server");
915	if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
916		return NT_STATUS_INTERNAL_ERROR;
917	}
918
919	r.in.name	= state->replica.name;
920	r.in.num_addrs	= state->r.out.num_addrs;
921	r.in.addrs	= talloc_array(state, struct nbtd_proxy_wins_addr, r.in.num_addrs);
922	NT_STATUS_HAVE_NO_MEMORY(r.in.addrs);
923	/* TODO: fix pidl to handle inline ipv4address arrays */
924	for (i=0; i < r.in.num_addrs; i++) {
925		r.in.addrs[i].addr = state->r.out.addrs[i].addr;
926	}
927
928	ireq = IRPC_CALL_SEND(state->msg_ctx, nbt_servers[0],
929			      irpc, NBTD_PROXY_WINS_RELEASE_DEMAND,
930			      &r, state);
931	NT_STATUS_HAVE_NO_MEMORY(ireq);
932
933	ireq->async.fn		= r_do_late_release_demand_handler;
934	ireq->async.private_data= state;
935
936	return NT_STATUS_OK;
937}
938
939/*
940Test Replica vs. owned active: some more MHOMED combinations
941_MA_MA_SP_U<00>: C:MHOMED vs. B:ALL => B:ALL => REPLACE
942_MA_MA_SM_U<00>: C:MHOMED vs. B:MHOMED => B:MHOMED => REPLACE
943_MA_MA_SB_P<00>: C:MHOMED vs. B:BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
944_MA_MA_SB_A<00>: C:MHOMED vs. B:BEST (C:ALL) => B:MHOMED => MHOMED_MERGE
945_MA_MA_SB_PRA<00>: C:MHOMED vs. B:BEST (C:BEST) => C:MHOMED => NOT REPLACE
946_MA_MA_SB_O<00>: C:MHOMED vs. B:BEST (B:B_3_4) =>C:MHOMED => NOT REPLACE
947_MA_MA_SB_N<00>: C:MHOMED vs. B:BEST (NEGATIVE) => B:BEST => REPLACE
948Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
949_MA_UA_SB_P<00>: C:MHOMED vs. B:UNIQUE,BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
950_UA_UA_DI_PRA<00>: C:BEST vs. B:BEST2 (C:BEST2,LR:BEST2) => C:BEST => NOT REPLACE
951_UA_UA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
952_UA_MA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
953*/
954static void r_do_challenge_handler(struct irpc_request *ireq)
955{
956	NTSTATUS status;
957	struct r_do_challenge_state *state = talloc_get_type(ireq->async.private_data,
958							     struct r_do_challenge_state);
959	bool old_is_subset = false;
960	bool new_is_subset = false;
961	bool found = false;
962	uint32_t i,j;
963	uint32_t num_rec_addrs;
964
965	status = irpc_call_recv(ireq);
966
967	DEBUG(4,("r_do_challenge_handler: %s: %s\n",
968		 nbt_name_string(state, &state->replica.name), nt_errstr(status)));
969
970	if (NT_STATUS_EQUAL(NT_STATUS_IO_TIMEOUT, status) ||
971	    NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
972		r_do_replace(state->partner, state, state->rec, &state->owner, &state->replica);
973		talloc_free(state);
974		return;
975	}
976
977	for (i=0; i < state->replica.num_addresses; i++) {
978		found = false;
979		new_is_subset = true;
980		for (j=0; j < state->r.out.num_addrs; j++) {
981			if (strcmp(state->replica.addresses[i].address, state->r.out.addrs[j].addr) == 0) {
982				found = true;
983				break;
984			}
985		}
986		if (found) continue;
987
988		new_is_subset = false;
989		break;
990	}
991
992	if (!new_is_subset) {
993		r_not_replace(state->partner, state, state->rec, &state->owner, &state->replica);
994		talloc_free(state);
995		return;
996	}
997
998	num_rec_addrs = winsdb_addr_list_length(state->rec->addresses);
999	for (i=0; i < num_rec_addrs; i++) {
1000		found = false;
1001		old_is_subset = true;
1002		for (j=0; j < state->r.out.num_addrs; j++) {
1003			if (strcmp(state->rec->addresses[i]->address, state->r.out.addrs[j].addr) == 0) {
1004				found = true;
1005				break;
1006			}
1007		}
1008		if (found) continue;
1009
1010		old_is_subset = false;
1011		break;
1012	}
1013
1014	if (!old_is_subset) {
1015		status = r_do_late_release_demand(state);
1016		/*
1017		 * only free state on error, because we pass it down,
1018		 * and r_do_late_release_demand() will free it
1019		 */
1020		if (!NT_STATUS_IS_OK(status)) {
1021			talloc_free(state);
1022		}
1023		return;
1024	}
1025
1026	r_do_mhomed_merge(state->partner, state, state->rec, &state->owner, &state->replica);
1027	talloc_free(state);
1028}
1029
1030static NTSTATUS r_do_challenge(struct wreplsrv_partner *partner,
1031			       TALLOC_CTX *mem_ctx,
1032			       struct winsdb_record *rec,
1033			       struct wrepl_wins_owner *owner,
1034			       struct wrepl_name *replica)
1035{
1036	struct irpc_request *ireq;
1037	struct r_do_challenge_state *state;
1038	struct server_id *nbt_servers;
1039	const char **addrs;
1040	uint32_t i;
1041
1042	DEBUG(4,("challenge record %s\n",
1043		 nbt_name_string(mem_ctx, &replica->name)));
1044
1045	state = talloc_zero(mem_ctx, struct r_do_challenge_state);
1046	NT_STATUS_HAVE_NO_MEMORY(state);
1047	state->msg_ctx	= partner->service->task->msg_ctx;
1048	state->partner	= partner;
1049	state->rec	= talloc_steal(state, rec);
1050	state->owner	= *owner;
1051	state->replica	= *replica;
1052	/* some stuff to have valid memory pointers in the async complete function */
1053	state->replica.name = *state->rec->name;
1054	talloc_steal(state, replica->owner);
1055	talloc_steal(state, replica->addresses);
1056
1057	nbt_servers = irpc_servers_byname(state->msg_ctx, state, "nbt_server");
1058	if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
1059		return NT_STATUS_INTERNAL_ERROR;
1060	}
1061
1062	state->r.in.name	= *rec->name;
1063	state->r.in.num_addrs	= winsdb_addr_list_length(rec->addresses);
1064	state->r.in.addrs	= talloc_array(state, struct nbtd_proxy_wins_addr, state->r.in.num_addrs);
1065	NT_STATUS_HAVE_NO_MEMORY(state->r.in.addrs);
1066	/* TODO: fix pidl to handle inline ipv4address arrays */
1067	addrs			= winsdb_addr_string_list(state->r.in.addrs, rec->addresses);
1068	NT_STATUS_HAVE_NO_MEMORY(addrs);
1069	for (i=0; i < state->r.in.num_addrs; i++) {
1070		state->r.in.addrs[i].addr = addrs[i];
1071	}
1072
1073	ireq = IRPC_CALL_SEND(state->msg_ctx, nbt_servers[0],
1074			      irpc, NBTD_PROXY_WINS_CHALLENGE,
1075			      &state->r, state);
1076	NT_STATUS_HAVE_NO_MEMORY(ireq);
1077
1078	ireq->async.fn		= r_do_challenge_handler;
1079	ireq->async.private_data= state;
1080
1081	talloc_steal(partner, state);
1082	return NT_STATUS_OK;
1083}
1084
1085struct r_do_release_demand_state {
1086	struct messaging_context *msg_ctx;
1087	struct nbtd_proxy_wins_release_demand r;
1088};
1089
1090static void r_do_release_demand_handler(struct irpc_request *ireq)
1091{
1092	NTSTATUS status;
1093	struct r_do_release_demand_state *state = talloc_get_type(ireq->async.private_data,
1094						  struct r_do_release_demand_state);
1095
1096	status = irpc_call_recv(ireq);
1097	/* don't care about the result */
1098	talloc_free(state);
1099}
1100
1101static NTSTATUS r_do_release_demand(struct wreplsrv_partner *partner,
1102				    TALLOC_CTX *mem_ctx,
1103				    struct winsdb_record *rec,
1104				    struct wrepl_wins_owner *owner,
1105				    struct wrepl_name *replica)
1106{
1107	NTSTATUS status;
1108	struct irpc_request *ireq;
1109	struct server_id *nbt_servers;
1110	const char **addrs;
1111	struct winsdb_addr **addresses;
1112	struct r_do_release_demand_state *state;
1113	uint32_t i;
1114
1115	/*
1116	 * we need to get a reference to the old addresses,
1117	 * as we need to send a release demand to them after replacing the record
1118	 * and r_do_replace() will modify rec->addresses
1119	 */
1120	addresses = rec->addresses;
1121
1122	status = r_do_replace(partner, mem_ctx, rec, owner, replica);
1123	NT_STATUS_NOT_OK_RETURN(status);
1124
1125	DEBUG(4,("release demand record %s\n",
1126		 nbt_name_string(mem_ctx, &replica->name)));
1127
1128	state = talloc_zero(mem_ctx, struct r_do_release_demand_state);
1129	NT_STATUS_HAVE_NO_MEMORY(state);
1130	state->msg_ctx	= partner->service->task->msg_ctx;
1131
1132	nbt_servers = irpc_servers_byname(state->msg_ctx, state, "nbt_server");
1133	if ((nbt_servers == NULL) || (nbt_servers[0].id == 0)) {
1134		return NT_STATUS_INTERNAL_ERROR;
1135	}
1136
1137	state->r.in.name	= *rec->name;
1138	state->r.in.num_addrs	= winsdb_addr_list_length(addresses);
1139	state->r.in.addrs	= talloc_array(state, struct nbtd_proxy_wins_addr,
1140					       state->r.in.num_addrs);
1141	NT_STATUS_HAVE_NO_MEMORY(state->r.in.addrs);
1142	/* TODO: fix pidl to handle inline ipv4address arrays */
1143	addrs			= winsdb_addr_string_list(state->r.in.addrs, addresses);
1144	NT_STATUS_HAVE_NO_MEMORY(addrs);
1145	for (i=0; i < state->r.in.num_addrs; i++) {
1146		state->r.in.addrs[i].addr = addrs[i];
1147	}
1148
1149	ireq = IRPC_CALL_SEND(state->msg_ctx, nbt_servers[0],
1150			      irpc, NBTD_PROXY_WINS_RELEASE_DEMAND,
1151			      &state->r, state);
1152	NT_STATUS_HAVE_NO_MEMORY(ireq);
1153
1154	ireq->async.fn		= r_do_release_demand_handler;
1155	ireq->async.private_data= state;
1156
1157	talloc_steal(partner, state);
1158	return NT_STATUS_OK;
1159}
1160
1161/*
1162SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4 => NOT REPLACE
1163SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:NULL => NOT REPLACE
1164SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4 => NOT REPLACE
1165SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4 vs. B:A_3_4 => REPLACE
1166SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4_OWNER_B => REPLACE
1167SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_OWNER_B vs. B:A_3_4 => REPLACE
1168
1169SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4 => SGROUP_MERGE
1170SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4_X_3_4 => SGROUP_MERGE
1171SGROUP,ACTIVE vs. SGROUP,ACTIVE A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4 => SGROUP_MERGE
1172SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4_OWNER_B => B:A_3_4_OWNER_B_X_3_4 => SGROUP_MERGE
1173SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:B_3_4_X_1_2 => C:B_3_4_X_1_2_3_4 => SGROUP_MERGE
1174SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4 => SGROUP_MERGE
1175
1176Test Replica vs. owned active: SGROUP vs. SGROUP tests
1177_SA_SA_DI_U<1c> => SGROUP_MERGE
1178_SA_SA_SI_U<1c> => SGROUP_MERGE
1179_SA_SA_SP_U<1c> => SGROUP_MERGE
1180_SA_SA_SB_U<1c> => SGROUP_MERGE
1181*/
1182static NTSTATUS r_do_sgroup_merge(struct wreplsrv_partner *partner,
1183				  TALLOC_CTX *mem_ctx,
1184				  struct winsdb_record *rec,
1185				  struct wrepl_wins_owner *owner,
1186				  struct wrepl_name *replica)
1187{
1188	struct winsdb_record *merge;
1189	uint32_t modify_flags = 0;
1190	uint32_t i,j;
1191	uint8_t ret;
1192	size_t len;
1193	bool changed_old_addrs = false;
1194	bool skip_replica_owned_by_us = false;
1195	bool become_owner = true;
1196	bool propagate = lp_parm_bool(partner->service->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false);
1197	const char *local_owner = partner->service->wins_db->local_owner;
1198
1199	merge = talloc(mem_ctx, struct winsdb_record);
1200	NT_STATUS_HAVE_NO_MEMORY(merge);
1201
1202	merge->name		= &replica->name;
1203	merge->type		= replica->type;
1204	merge->state		= replica->state;
1205	merge->node		= replica->node;
1206	merge->is_static	= replica->is_static;
1207	merge->expire_time	= time(NULL) + partner->service->config.verify_interval;
1208	merge->version		= replica->version_id;
1209	merge->wins_owner	= replica->owner;
1210	merge->addresses	= winsdb_addr_list_make(merge);
1211	NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
1212	merge->registered_by = NULL;
1213
1214	len = winsdb_addr_list_length(rec->addresses);
1215
1216	for (i=0; i < len; i++) {
1217		bool found = false;
1218
1219		for (j=0; j < replica->num_addresses; j++) {
1220			if (strcmp(rec->addresses[i]->address, replica->addresses[j].address) != 0) {
1221				continue;
1222			}
1223
1224			found = true;
1225
1226			if (strcmp(rec->addresses[i]->wins_owner, replica->addresses[j].owner) != 0) {
1227				changed_old_addrs = true;
1228				break;
1229			}
1230			break;
1231		}
1232
1233		/*
1234		 * if the address isn't in the replica and is owned by replicas owner,
1235		 * it won't be added to the merged record
1236		 */
1237		if (!found && strcmp(rec->addresses[i]->wins_owner, owner->address) == 0) {
1238			changed_old_addrs = true;
1239			continue;
1240		}
1241
1242		/*
1243		 * add the address to the merge result, with the old owner and expire_time,
1244		 * the owner and expire_time will be overwritten later if the address is
1245		 * in the replica too
1246		 */
1247		merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
1248							merge, merge->addresses,
1249							rec->addresses[i]->address,
1250							rec->addresses[i]->wins_owner,
1251							rec->addresses[i]->expire_time,
1252							false);
1253		NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
1254	}
1255
1256	for (i=0; i < replica->num_addresses; i++) {
1257		if (propagate &&
1258		    strcmp(replica->addresses[i].owner, local_owner) == 0) {
1259			const struct winsdb_addr *a;
1260
1261			/*
1262			 * NOTE: this is different to the windows behavior
1263			 *       and off by default, but it better propagated
1264			 *       name releases
1265			 */
1266			a = winsdb_addr_list_check(merge->addresses,
1267						   replica->addresses[i].address);
1268			if (!a) {
1269				/* don't add addresses owned by us */
1270				skip_replica_owned_by_us = true;
1271			}
1272			continue;
1273		}
1274		merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
1275							merge, merge->addresses,
1276							replica->addresses[i].address,
1277							replica->addresses[i].owner,
1278							merge->expire_time,
1279							false);
1280		NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
1281	}
1282
1283	/* we the old addresses change changed we don't become the owner */
1284	if (changed_old_addrs) {
1285		become_owner = false;
1286	}
1287
1288	/*
1289	 * when we notice another server believes an address
1290	 * is owned by us and that's not the case
1291	 * we propagate the result
1292	 */
1293	if (skip_replica_owned_by_us) {
1294		become_owner = true;
1295	}
1296
1297	/* if we're the owner of the old record, we'll be the owner of the new one too */
1298	if (strcmp(rec->wins_owner, local_owner)==0) {
1299		become_owner = true;
1300	}
1301
1302	/*
1303	 * if the result has no addresses we take the ownership
1304	 */
1305	len = winsdb_addr_list_length(merge->addresses);
1306	if (len == 0) {
1307		become_owner = true;
1308	}
1309
1310	/*
1311	 * if addresses of the old record will be changed the replica owner
1312	 * will be owner of the merge result, otherwise we take the ownership
1313	 */
1314	if (become_owner) {
1315		time_t lh = 0;
1316
1317		modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
1318
1319		/*
1320		 * if we're the owner, the expire time becomes the highest
1321		 * expire time of owned addresses
1322		 */
1323		len = winsdb_addr_list_length(merge->addresses);
1324
1325		for (i=0; i < len; i++) {
1326			if (strcmp(merge->addresses[i]->wins_owner, local_owner)==0) {
1327				lh = MAX(lh, merge->addresses[i]->expire_time);
1328			}
1329		}
1330
1331		if (lh != 0) {
1332			merge->expire_time = lh;
1333		}
1334	}
1335
1336	ret = winsdb_modify(partner->service->wins_db, merge, modify_flags);
1337	if (ret != NBT_RCODE_OK) {
1338		DEBUG(0,("Failed to modify sgroup merge record %s: %u\n",
1339			nbt_name_string(mem_ctx, &replica->name), ret));
1340		return NT_STATUS_FOOBAR;
1341	}
1342
1343	DEBUG(4,("sgroup merge record %s\n",
1344		nbt_name_string(mem_ctx, &replica->name)));
1345
1346	return NT_STATUS_OK;
1347}
1348
1349static NTSTATUS wreplsrv_apply_one_record(struct wreplsrv_partner *partner,
1350					  TALLOC_CTX *mem_ctx,
1351					  struct wrepl_wins_owner *owner,
1352					  struct wrepl_name *replica)
1353{
1354	NTSTATUS status;
1355	struct winsdb_record *rec = NULL;
1356	enum _R_ACTION action = R_INVALID;
1357	bool same_owner = false;
1358	bool replica_vs_replica = false;
1359	bool local_vs_replica = false;
1360
1361	status = winsdb_lookup(partner->service->wins_db,
1362			       &replica->name, mem_ctx, &rec);
1363	if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
1364		return r_do_add(partner, mem_ctx, owner, replica);
1365	}
1366	NT_STATUS_NOT_OK_RETURN(status);
1367
1368	if (strcmp(rec->wins_owner, partner->service->wins_db->local_owner)==0) {
1369		local_vs_replica = true;
1370	} else if (strcmp(rec->wins_owner, owner->address)==0) {
1371		same_owner = true;
1372	} else {
1373		replica_vs_replica = true;
1374	}
1375
1376	if (rec->is_static && !same_owner) {
1377		action = R_NOT_REPLACE;
1378
1379		/*
1380		 * if we own the local record, then propagate it back to
1381		 * the other wins servers.
1382		 * to prevent ping-pong with other servers, we don't do this
1383		 * if the replica is static too.
1384		 *
1385		 * It seems that w2k3 doesn't do this, but I thing that's a bug
1386		 * and doing propagation helps to have consistent data on all servers
1387		 */
1388		if (local_vs_replica && !replica->is_static) {
1389			action = R_DO_PROPAGATE;
1390		}
1391	} else if (replica->is_static && !rec->is_static && !same_owner) {
1392		action = R_DO_REPLACE;
1393	} else if (same_owner) {
1394		action = replace_same_owner(rec, replica);
1395	} else if (replica_vs_replica) {
1396		switch (rec->type) {
1397		case WREPL_TYPE_UNIQUE:
1398			action = replace_unique_replica_vs_X_replica(rec, replica);
1399			break;
1400		case WREPL_TYPE_GROUP:
1401			action = replace_group_replica_vs_X_replica(rec, replica);
1402			break;
1403		case WREPL_TYPE_SGROUP:
1404			action = replace_sgroup_replica_vs_X_replica(rec, replica);
1405			break;
1406		case WREPL_TYPE_MHOMED:
1407			action = replace_mhomed_replica_vs_X_replica(rec, replica);
1408			break;
1409		}
1410	} else if (local_vs_replica) {
1411		switch (rec->type) {
1412		case WREPL_TYPE_UNIQUE:
1413			action = replace_unique_owned_vs_X_replica(rec, replica);
1414			break;
1415		case WREPL_TYPE_GROUP:
1416			action = replace_group_owned_vs_X_replica(rec, replica);
1417			break;
1418		case WREPL_TYPE_SGROUP:
1419			action = replace_sgroup_owned_vs_X_replica(rec, replica);
1420			break;
1421		case WREPL_TYPE_MHOMED:
1422			action = replace_mhomed_owned_vs_X_replica(rec, replica);
1423			break;
1424		}
1425	}
1426
1427	DEBUG(4,("apply record %s: %s\n",
1428		 nbt_name_string(mem_ctx, &replica->name), _R_ACTION_enum_string(action)));
1429
1430	switch (action) {
1431	case R_INVALID: break;
1432	case R_DO_REPLACE:
1433		return r_do_replace(partner, mem_ctx, rec, owner, replica);
1434	case R_NOT_REPLACE:
1435		return r_not_replace(partner, mem_ctx, rec, owner, replica);
1436	case R_DO_PROPAGATE:
1437		return r_do_propagate(partner, mem_ctx, rec, owner, replica);
1438	case R_DO_CHALLENGE:
1439		return r_do_challenge(partner, mem_ctx, rec, owner, replica);
1440	case R_DO_RELEASE_DEMAND:
1441		return r_do_release_demand(partner, mem_ctx, rec, owner, replica);
1442	case R_DO_SGROUP_MERGE:
1443		return r_do_sgroup_merge(partner, mem_ctx, rec, owner, replica);
1444	}
1445
1446	return NT_STATUS_INTERNAL_ERROR;
1447}
1448
1449NTSTATUS wreplsrv_apply_records(struct wreplsrv_partner *partner,
1450				struct wrepl_wins_owner *owner,
1451				uint32_t num_names, struct wrepl_name *names)
1452{
1453	NTSTATUS status;
1454	uint32_t i;
1455
1456	DEBUG(4,("apply records count[%u]:owner[%s]:min[%llu]:max[%llu]:partner[%s]\n",
1457		num_names, owner->address,
1458		(long long)owner->min_version,
1459		(long long)owner->max_version,
1460		partner->address));
1461
1462	for (i=0; i < num_names; i++) {
1463		TALLOC_CTX *tmp_mem = talloc_new(partner);
1464		NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
1465
1466		status = wreplsrv_apply_one_record(partner, tmp_mem,
1467						   owner, &names[i]);
1468		talloc_free(tmp_mem);
1469		NT_STATUS_NOT_OK_RETURN(status);
1470	}
1471
1472	status = wreplsrv_add_table(partner->service,
1473				    partner->service,
1474				    &partner->service->table,
1475				    owner->address,
1476				    owner->max_version);
1477	NT_STATUS_NOT_OK_RETURN(status);
1478
1479	return NT_STATUS_OK;
1480}
1481