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