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