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