tdb: Reproducer for Bug 11381
[Samba.git] / source4 / wrepl_server / wrepl_apply_records.c
blobbc8a820013235f9d0b29961c50cb6fd7246ed1f0
1 /*
2 Unix SMB/CIFS implementation.
4 WINS Replication server
6 Copyright (C) Stefan Metzmacher 2005
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.
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.
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/>.
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"
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
45 static const char *_R_ACTION_enum_string(enum _R_ACTION action)
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";
57 return "enum _R_ACTION unknown";
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)
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
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)
76 /* REPLACE */
77 return R_DO_REPLACE;
80 static bool r_1_is_subset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners)
82 uint32_t i,j;
83 size_t len = winsdb_addr_list_length(r1->addresses);
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;
92 if (check_owners && strcmp(r1->addresses[i]->wins_owner, r2->addresses[j].owner) != 0) {
93 return false;
95 found = true;
96 break;
98 if (!found) return false;
101 return true;
104 static bool r_1_is_superset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners)
106 uint32_t i,j;
107 size_t len = winsdb_addr_list_length(r1->addresses);
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;
116 if (check_owners && strcmp(r2->addresses[i].owner, r1->addresses[j]->wins_owner) != 0) {
117 return false;
119 found = true;
120 break;
122 if (!found) return false;
125 return true;
128 static bool r_1_is_same_as_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners)
130 size_t len = winsdb_addr_list_length(r1->addresses);
132 if (len != r2->num_addresses) {
133 return false;
136 return r_1_is_superset_of_2_address_list(r1, r2, check_owners);
139 static bool r_contains_addrs_from_owner(struct winsdb_record *r1, const char *owner)
141 uint32_t i;
142 size_t len = winsdb_addr_list_length(r1->addresses);
144 for (i=0; i < len; i++) {
145 if (strcmp(r1->addresses[i]->wins_owner, owner) == 0) {
146 return true;
150 return false;
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
179 static enum _R_ACTION replace_unique_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
181 if (!R_IS_ACTIVE(r1)) {
182 /* REPLACE */
183 return R_DO_REPLACE;
186 if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) {
187 /* REPLACE */
188 return R_DO_REPLACE;
191 /* NOT REPLACE */
192 return R_NOT_REPLACE;
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
221 static enum _R_ACTION replace_group_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
223 if (!R_IS_ACTIVE(r1) && R_IS_GROUP(r2)) {
224 /* REPLACE */
225 return R_DO_REPLACE;
228 if (R_IS_TOMBSTONE(r1) && !R_IS_UNIQUE(r2)) {
229 /* REPLACE */
230 return R_DO_REPLACE;
233 /* NOT REPLACE */
234 return R_NOT_REPLACE;
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
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
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
276 this is a bit strange, incoming tombstone replicas always replace old replicas:
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
283 static enum _R_ACTION replace_sgroup_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
285 if (!R_IS_ACTIVE(r1)) {
286 /* REPLACE */
287 return R_DO_REPLACE;
290 if (!R_IS_SGROUP(r2)) {
291 /* NOT REPLACE */
292 return R_NOT_REPLACE;
296 * this is strange, but correct
297 * the incoming tombstone replace the current active
298 * record
300 if (!R_IS_ACTIVE(r2)) {
301 /* REPLACE */
302 return R_DO_REPLACE;
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;
311 /* NOT REPLACE */
312 return R_NOT_REPLACE;
315 if (r_1_is_superset_of_2_address_list(r1, r2, true)) {
316 /* NOT REPLACE */
317 return R_NOT_REPLACE;
320 if (r_1_is_same_as_2_address_list(r1, r2, false)) {
321 /* REPLACE */
322 return R_DO_REPLACE;
325 /* not handled here: MERGE */
326 return R_DO_SGROUP_MERGE;
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
355 static enum _R_ACTION replace_mhomed_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
357 if (!R_IS_ACTIVE(r1)) {
358 /* REPLACE */
359 return R_DO_REPLACE;
362 if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) {
363 /* REPLACE */
364 return R_DO_REPLACE;
367 /* NOT REPLACE */
368 return R_NOT_REPLACE;
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
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
416 static enum _R_ACTION replace_unique_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
418 if (!R_IS_ACTIVE(r1)) {
419 /* REPLACE */
420 return R_DO_REPLACE;
423 if (!R_IS_ACTIVE(r2)) {
424 /* NOT REPLACE, and PROPAGATE */
425 return R_DO_PROPAGATE;
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;
434 * here we only have unique,active,owned vs.
435 * is unique,active,replica or mhomed,active,replica
438 if (r_1_is_subset_of_2_address_list(r1, r2, false)) {
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
444 * then replace the record
446 return R_DO_REPLACE;
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...
454 return R_DO_CHALLENGE;
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
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
494 static enum _R_ACTION replace_group_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
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;
503 /* NOT REPLACE, but PROPAGATE */
504 return R_DO_PROPAGATE;
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
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
532 SGROUP,ACTIVE vs. SGROUP,* is not handled here!
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
552 static enum _R_ACTION replace_sgroup_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
554 if (!R_IS_ACTIVE(r1)) {
555 /* REPLACE */
556 return R_DO_REPLACE;
559 if (!R_IS_SGROUP(r2) || !R_IS_ACTIVE(r2)) {
560 /* NOT REPLACE, but PROPAGATE */
561 return R_DO_PROPAGATE;
564 if (r_1_is_same_as_2_address_list(r1, r2, true)) {
566 * as we're the old owner and the addresses and their
567 * owners are identical
569 return R_NOT_REPLACE;
572 /* not handled here: MERGE */
573 return R_DO_SGROUP_MERGE;
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
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
628 static enum _R_ACTION replace_mhomed_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2)
630 if (!R_IS_ACTIVE(r1)) {
631 /* REPLACE */
632 return R_DO_REPLACE;
635 if (!R_IS_ACTIVE(r2)) {
636 /* NOT REPLACE, but PROPAGATE */
637 return R_DO_PROPAGATE;
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;
646 * here we only have mhomed,active,owned vs.
647 * is unique,active,replica or mhomed,active,replica
650 if (r_1_is_subset_of_2_address_list(r1, r2, false)) {
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
656 * then replace the record
658 return R_DO_REPLACE;
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...
666 return R_DO_CHALLENGE;
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)
674 struct winsdb_record *rec;
675 uint32_t i;
676 uint8_t ret;
678 rec = talloc(mem_ctx, struct winsdb_record);
679 NT_STATUS_HAVE_NO_MEMORY(rec);
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;
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);
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;
711 DEBUG(4,("added record %s\n",
712 nbt_name_string(mem_ctx, &replica->name)));
714 return NT_STATUS_OK;
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)
723 uint32_t i;
724 uint8_t ret;
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;
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);
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;
756 DEBUG(4,("replaced record %s\n",
757 nbt_name_string(mem_ctx, &replica->name)));
759 return NT_STATUS_OK;
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)
768 DEBUG(4,("not replace record %s\n",
769 nbt_name_string(mem_ctx, &replica->name)));
770 return NT_STATUS_OK;
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)
779 uint8_t ret;
780 uint32_t modify_flags;
783 * allocate a new version id for the record to that it'll be replicated
785 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
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;
794 DEBUG(4,("propagated record %s\n",
795 nbt_name_string(mem_ctx, &replica->name)));
797 return NT_STATUS_OK;
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
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)
821 struct winsdb_record *merge;
822 uint32_t i,j;
823 uint8_t ret;
824 size_t len;
826 merge = talloc(mem_ctx, struct winsdb_record);
827 NT_STATUS_HAVE_NO_MEMORY(merge);
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;
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);
851 len = winsdb_addr_list_length(rec->addresses);
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;
861 if (found) continue;
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);
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;
879 DEBUG(4,("mhomed merge record %s\n",
880 nbt_name_string(mem_ctx, &replica->name)));
882 return NT_STATUS_OK;
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;
895 static void r_do_late_release_demand_handler(struct tevent_req *subreq)
897 NTSTATUS status;
898 struct r_do_challenge_state *state =
899 tevent_req_callback_data(subreq,
900 struct r_do_challenge_state);
902 status = dcerpc_nbtd_proxy_wins_release_demand_r_recv(subreq, state);
903 TALLOC_FREE(subreq);
905 /* don't care about the result */
906 (void)status;
907 talloc_free(state);
910 static NTSTATUS r_do_late_release_demand(struct r_do_challenge_state *state)
912 struct tevent_req *subreq;
913 uint32_t i;
915 DEBUG(4,("late release demand record %s\n",
916 nbt_name_string(state, &state->replica.name)));
918 state->dr.in.name = state->replica.name;
919 state->dr.in.num_addrs = state->r.out.num_addrs;
920 state->dr.in.addrs = talloc_array(state,
921 struct nbtd_proxy_wins_addr,
922 state->dr.in.num_addrs);
923 NT_STATUS_HAVE_NO_MEMORY(state->dr.in.addrs);
924 /* TODO: fix pidl to handle inline ipv4address arrays */
925 for (i=0; i < state->dr.in.num_addrs; i++) {
926 state->dr.in.addrs[i].addr = state->r.out.addrs[i].addr;
929 subreq = dcerpc_nbtd_proxy_wins_release_demand_r_send(state,
930 state->partner->service->task->event_ctx,
931 state->irpc_handle,
932 &state->dr);
933 NT_STATUS_HAVE_NO_MEMORY(subreq);
935 tevent_req_set_callback(subreq, r_do_late_release_demand_handler, state);
937 return NT_STATUS_OK;
941 Test Replica vs. owned active: some more MHOMED combinations
942 _MA_MA_SP_U<00>: C:MHOMED vs. B:ALL => B:ALL => REPLACE
943 _MA_MA_SM_U<00>: C:MHOMED vs. B:MHOMED => B:MHOMED => REPLACE
944 _MA_MA_SB_P<00>: C:MHOMED vs. B:BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
945 _MA_MA_SB_A<00>: C:MHOMED vs. B:BEST (C:ALL) => B:MHOMED => MHOMED_MERGE
946 _MA_MA_SB_PRA<00>: C:MHOMED vs. B:BEST (C:BEST) => C:MHOMED => NOT REPLACE
947 _MA_MA_SB_O<00>: C:MHOMED vs. B:BEST (B:B_3_4) =>C:MHOMED => NOT REPLACE
948 _MA_MA_SB_N<00>: C:MHOMED vs. B:BEST (NEGATIVE) => B:BEST => REPLACE
949 Test Replica vs. owned active: some more UNIQUE,MHOMED combinations
950 _MA_UA_SB_P<00>: C:MHOMED vs. B:UNIQUE,BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE
951 _UA_UA_DI_PRA<00>: C:BEST vs. B:BEST2 (C:BEST2,LR:BEST2) => C:BEST => NOT REPLACE
952 _UA_UA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
953 _UA_MA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE
955 static void r_do_challenge_handler(struct tevent_req *subreq)
957 NTSTATUS status;
958 struct r_do_challenge_state *state =
959 tevent_req_callback_data(subreq,
960 struct r_do_challenge_state);
961 bool old_is_subset = false;
962 bool new_is_subset = false;
963 bool found = false;
964 uint32_t i,j;
965 uint32_t num_rec_addrs;
967 status = dcerpc_nbtd_proxy_wins_challenge_r_recv(subreq, state);
968 TALLOC_FREE(subreq);
970 DEBUG(4,("r_do_challenge_handler: %s: %s\n",
971 nbt_name_string(state, &state->replica.name), nt_errstr(status)));
973 if (NT_STATUS_EQUAL(NT_STATUS_IO_TIMEOUT, status) ||
974 NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
975 r_do_replace(state->partner, state, state->rec, &state->owner, &state->replica);
976 talloc_free(state);
977 return;
980 for (i=0; i < state->replica.num_addresses; i++) {
981 found = false;
982 new_is_subset = true;
983 for (j=0; j < state->r.out.num_addrs; j++) {
984 if (strcmp(state->replica.addresses[i].address, state->r.out.addrs[j].addr) == 0) {
985 found = true;
986 break;
989 if (found) continue;
991 new_is_subset = false;
992 break;
995 if (!new_is_subset) {
996 r_not_replace(state->partner, state, state->rec, &state->owner, &state->replica);
997 talloc_free(state);
998 return;
1001 num_rec_addrs = winsdb_addr_list_length(state->rec->addresses);
1002 for (i=0; i < num_rec_addrs; i++) {
1003 found = false;
1004 old_is_subset = true;
1005 for (j=0; j < state->r.out.num_addrs; j++) {
1006 if (strcmp(state->rec->addresses[i]->address, state->r.out.addrs[j].addr) == 0) {
1007 found = true;
1008 break;
1011 if (found) continue;
1013 old_is_subset = false;
1014 break;
1017 if (!old_is_subset) {
1018 status = r_do_late_release_demand(state);
1020 * only free state on error, because we pass it down,
1021 * and r_do_late_release_demand() will free it
1023 if (!NT_STATUS_IS_OK(status)) {
1024 talloc_free(state);
1026 return;
1029 r_do_mhomed_merge(state->partner, state, state->rec, &state->owner, &state->replica);
1030 talloc_free(state);
1033 static NTSTATUS r_do_challenge(struct wreplsrv_partner *partner,
1034 TALLOC_CTX *mem_ctx,
1035 struct winsdb_record *rec,
1036 struct wrepl_wins_owner *owner,
1037 struct wrepl_name *replica)
1039 struct r_do_challenge_state *state;
1040 struct tevent_req *subreq;
1041 const char **addrs;
1042 uint32_t i;
1044 DEBUG(4,("challenge record %s\n",
1045 nbt_name_string(mem_ctx, &replica->name)));
1047 state = talloc_zero(mem_ctx, struct r_do_challenge_state);
1048 NT_STATUS_HAVE_NO_MEMORY(state);
1049 state->partner = partner;
1050 state->rec = talloc_steal(state, rec);
1051 state->owner = *owner;
1052 state->replica = *replica;
1053 /* some stuff to have valid memory pointers in the async complete function */
1054 state->replica.name = *state->rec->name;
1055 talloc_steal(state, replica->owner);
1056 talloc_steal(state, replica->addresses);
1058 state->irpc_handle = irpc_binding_handle_by_name(state,
1059 partner->service->task->msg_ctx,
1060 "nbt_server",
1061 &ndr_table_irpc);
1062 if (state->irpc_handle == NULL) {
1063 return NT_STATUS_INTERNAL_ERROR;
1066 state->r.in.name = *rec->name;
1067 state->r.in.num_addrs = winsdb_addr_list_length(rec->addresses);
1068 state->r.in.addrs = talloc_array(state, struct nbtd_proxy_wins_addr, state->r.in.num_addrs);
1069 NT_STATUS_HAVE_NO_MEMORY(state->r.in.addrs);
1070 /* TODO: fix pidl to handle inline ipv4address arrays */
1071 addrs = winsdb_addr_string_list(state->r.in.addrs, rec->addresses);
1072 NT_STATUS_HAVE_NO_MEMORY(addrs);
1073 for (i=0; i < state->r.in.num_addrs; i++) {
1074 state->r.in.addrs[i].addr = addrs[i];
1077 subreq = dcerpc_nbtd_proxy_wins_challenge_r_send(state,
1078 state->partner->service->task->event_ctx,
1079 state->irpc_handle,
1080 &state->r);
1081 NT_STATUS_HAVE_NO_MEMORY(subreq);
1083 tevent_req_set_callback(subreq, r_do_challenge_handler, state);
1085 talloc_steal(partner, state);
1086 return NT_STATUS_OK;
1089 struct r_do_release_demand_state {
1090 struct nbtd_proxy_wins_release_demand r;
1093 static void r_do_release_demand_handler(struct tevent_req *subreq)
1095 NTSTATUS status;
1096 struct r_do_release_demand_state *state =
1097 tevent_req_callback_data(subreq,
1098 struct r_do_release_demand_state);
1100 status = dcerpc_nbtd_proxy_wins_release_demand_r_recv(subreq, state);
1101 TALLOC_FREE(subreq);
1103 /* don't care about the result */
1104 (void)status;
1105 talloc_free(state);
1108 static NTSTATUS r_do_release_demand(struct wreplsrv_partner *partner,
1109 TALLOC_CTX *mem_ctx,
1110 struct winsdb_record *rec,
1111 struct wrepl_wins_owner *owner,
1112 struct wrepl_name *replica)
1114 NTSTATUS status;
1115 struct dcerpc_binding_handle *irpc_handle;
1116 const char **addrs;
1117 struct winsdb_addr **addresses;
1118 struct r_do_release_demand_state *state;
1119 struct tevent_req *subreq;
1120 uint32_t i;
1123 * we need to get a reference to the old addresses,
1124 * as we need to send a release demand to them after replacing the record
1125 * and r_do_replace() will modify rec->addresses
1127 addresses = rec->addresses;
1129 status = r_do_replace(partner, mem_ctx, rec, owner, replica);
1130 NT_STATUS_NOT_OK_RETURN(status);
1132 DEBUG(4,("release demand record %s\n",
1133 nbt_name_string(mem_ctx, &replica->name)));
1135 state = talloc_zero(mem_ctx, struct r_do_release_demand_state);
1136 NT_STATUS_HAVE_NO_MEMORY(state);
1138 irpc_handle = irpc_binding_handle_by_name(state,
1139 partner->service->task->msg_ctx,
1140 "nbt_server",
1141 &ndr_table_irpc);
1142 if (irpc_handle == NULL) {
1143 return NT_STATUS_INTERNAL_ERROR;
1146 state->r.in.name = *rec->name;
1147 state->r.in.num_addrs = winsdb_addr_list_length(addresses);
1148 state->r.in.addrs = talloc_array(state, struct nbtd_proxy_wins_addr,
1149 state->r.in.num_addrs);
1150 NT_STATUS_HAVE_NO_MEMORY(state->r.in.addrs);
1151 /* TODO: fix pidl to handle inline ipv4address arrays */
1152 addrs = winsdb_addr_string_list(state->r.in.addrs, addresses);
1153 NT_STATUS_HAVE_NO_MEMORY(addrs);
1154 for (i=0; i < state->r.in.num_addrs; i++) {
1155 state->r.in.addrs[i].addr = addrs[i];
1158 subreq = dcerpc_nbtd_proxy_wins_release_demand_r_send(state,
1159 partner->service->task->event_ctx,
1160 irpc_handle,
1161 &state->r);
1162 NT_STATUS_HAVE_NO_MEMORY(subreq);
1164 tevent_req_set_callback(subreq, r_do_release_demand_handler, state);
1166 talloc_steal(partner, state);
1167 return NT_STATUS_OK;
1171 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4 => NOT REPLACE
1172 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:NULL => NOT REPLACE
1173 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4 => NOT REPLACE
1174 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4 vs. B:A_3_4 => REPLACE
1175 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4_OWNER_B => REPLACE
1176 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_OWNER_B vs. B:A_3_4 => REPLACE
1178 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4 => SGROUP_MERGE
1179 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
1180 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4 => SGROUP_MERGE
1181 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
1182 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
1183 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4 => SGROUP_MERGE
1185 Test Replica vs. owned active: SGROUP vs. SGROUP tests
1186 _SA_SA_DI_U<1c> => SGROUP_MERGE
1187 _SA_SA_SI_U<1c> => SGROUP_MERGE
1188 _SA_SA_SP_U<1c> => SGROUP_MERGE
1189 _SA_SA_SB_U<1c> => SGROUP_MERGE
1191 static NTSTATUS r_do_sgroup_merge(struct wreplsrv_partner *partner,
1192 TALLOC_CTX *mem_ctx,
1193 struct winsdb_record *rec,
1194 struct wrepl_wins_owner *owner,
1195 struct wrepl_name *replica)
1197 struct winsdb_record *merge;
1198 uint32_t modify_flags = 0;
1199 uint32_t i,j;
1200 uint8_t ret;
1201 size_t len;
1202 bool changed_old_addrs = false;
1203 bool skip_replica_owned_by_us = false;
1204 bool become_owner = true;
1205 bool propagate = lpcfg_parm_bool(partner->service->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false);
1206 const char *local_owner = partner->service->wins_db->local_owner;
1208 merge = talloc(mem_ctx, struct winsdb_record);
1209 NT_STATUS_HAVE_NO_MEMORY(merge);
1211 merge->name = &replica->name;
1212 merge->type = replica->type;
1213 merge->state = replica->state;
1214 merge->node = replica->node;
1215 merge->is_static = replica->is_static;
1216 merge->expire_time = time(NULL) + partner->service->config.verify_interval;
1217 merge->version = replica->version_id;
1218 merge->wins_owner = replica->owner;
1219 merge->addresses = winsdb_addr_list_make(merge);
1220 NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
1221 merge->registered_by = NULL;
1223 len = winsdb_addr_list_length(rec->addresses);
1225 for (i=0; i < len; i++) {
1226 bool found = false;
1228 for (j=0; j < replica->num_addresses; j++) {
1229 if (strcmp(rec->addresses[i]->address, replica->addresses[j].address) != 0) {
1230 continue;
1233 found = true;
1235 if (strcmp(rec->addresses[i]->wins_owner, replica->addresses[j].owner) != 0) {
1236 changed_old_addrs = true;
1237 break;
1239 break;
1243 * if the address isn't in the replica and is owned by replicas owner,
1244 * it won't be added to the merged record
1246 if (!found && strcmp(rec->addresses[i]->wins_owner, owner->address) == 0) {
1247 changed_old_addrs = true;
1248 continue;
1252 * add the address to the merge result, with the old owner and expire_time,
1253 * the owner and expire_time will be overwritten later if the address is
1254 * in the replica too
1256 merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
1257 merge, merge->addresses,
1258 rec->addresses[i]->address,
1259 rec->addresses[i]->wins_owner,
1260 rec->addresses[i]->expire_time,
1261 false);
1262 NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
1265 for (i=0; i < replica->num_addresses; i++) {
1266 if (propagate &&
1267 strcmp(replica->addresses[i].owner, local_owner) == 0) {
1268 const struct winsdb_addr *a;
1271 * NOTE: this is different to the windows behavior
1272 * and off by default, but it better propagated
1273 * name releases
1275 a = winsdb_addr_list_check(merge->addresses,
1276 replica->addresses[i].address);
1277 if (!a) {
1278 /* don't add addresses owned by us */
1279 skip_replica_owned_by_us = true;
1281 continue;
1283 merge->addresses = winsdb_addr_list_add(partner->service->wins_db,
1284 merge, merge->addresses,
1285 replica->addresses[i].address,
1286 replica->addresses[i].owner,
1287 merge->expire_time,
1288 false);
1289 NT_STATUS_HAVE_NO_MEMORY(merge->addresses);
1292 /* we the old addresses change changed we don't become the owner */
1293 if (changed_old_addrs) {
1294 become_owner = false;
1298 * when we notice another server believes an address
1299 * is owned by us and that's not the case
1300 * we propagate the result
1302 if (skip_replica_owned_by_us) {
1303 become_owner = true;
1306 /* if we're the owner of the old record, we'll be the owner of the new one too */
1307 if (strcmp(rec->wins_owner, local_owner)==0) {
1308 become_owner = true;
1312 * if the result has no addresses we take the ownership
1314 len = winsdb_addr_list_length(merge->addresses);
1315 if (len == 0) {
1316 become_owner = true;
1320 * if addresses of the old record will be changed the replica owner
1321 * will be owner of the merge result, otherwise we take the ownership
1323 if (become_owner) {
1324 time_t lh = 0;
1326 modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP;
1329 * if we're the owner, the expire time becomes the highest
1330 * expire time of owned addresses
1332 len = winsdb_addr_list_length(merge->addresses);
1334 for (i=0; i < len; i++) {
1335 if (strcmp(merge->addresses[i]->wins_owner, local_owner)==0) {
1336 lh = MAX(lh, merge->addresses[i]->expire_time);
1340 if (lh != 0) {
1341 merge->expire_time = lh;
1345 ret = winsdb_modify(partner->service->wins_db, merge, modify_flags);
1346 if (ret != NBT_RCODE_OK) {
1347 DEBUG(0,("Failed to modify sgroup merge record %s: %u\n",
1348 nbt_name_string(mem_ctx, &replica->name), ret));
1349 return NT_STATUS_FOOBAR;
1352 DEBUG(4,("sgroup merge record %s\n",
1353 nbt_name_string(mem_ctx, &replica->name)));
1355 return NT_STATUS_OK;
1358 static NTSTATUS wreplsrv_apply_one_record(struct wreplsrv_partner *partner,
1359 TALLOC_CTX *mem_ctx,
1360 struct wrepl_wins_owner *owner,
1361 struct wrepl_name *replica)
1363 NTSTATUS status;
1364 struct winsdb_record *rec = NULL;
1365 enum _R_ACTION action = R_INVALID;
1366 bool same_owner = false;
1367 bool replica_vs_replica = false;
1368 bool local_vs_replica = false;
1370 if (replica->name.scope) {
1371 TALLOC_CTX *parent;
1372 const char *scope;
1375 * Windows 2008 truncates the scope to 237 bytes,
1376 * so we do...
1378 parent = talloc_parent(replica->name.scope);
1379 scope = talloc_strndup(parent, replica->name.scope, 237);
1380 NT_STATUS_HAVE_NO_MEMORY(scope);
1381 replica->name.scope = scope;
1384 status = winsdb_lookup(partner->service->wins_db,
1385 &replica->name, mem_ctx, &rec);
1386 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
1387 return r_do_add(partner, mem_ctx, owner, replica);
1389 NT_STATUS_NOT_OK_RETURN(status);
1391 if (strcmp(rec->wins_owner, partner->service->wins_db->local_owner)==0) {
1392 local_vs_replica = true;
1393 } else if (strcmp(rec->wins_owner, owner->address)==0) {
1394 same_owner = true;
1395 } else {
1396 replica_vs_replica = true;
1399 if (rec->is_static && !same_owner) {
1400 action = R_NOT_REPLACE;
1403 * if we own the local record, then propagate it back to
1404 * the other wins servers.
1405 * to prevent ping-pong with other servers, we don't do this
1406 * if the replica is static too.
1408 * It seems that w2k3 doesn't do this, but I thing that's a bug
1409 * and doing propagation helps to have consistent data on all servers
1411 if (local_vs_replica && !replica->is_static) {
1412 action = R_DO_PROPAGATE;
1414 } else if (replica->is_static && !rec->is_static && !same_owner) {
1415 action = R_DO_REPLACE;
1416 } else if (same_owner) {
1417 action = replace_same_owner(rec, replica);
1418 } else if (replica_vs_replica) {
1419 switch (rec->type) {
1420 case WREPL_TYPE_UNIQUE:
1421 action = replace_unique_replica_vs_X_replica(rec, replica);
1422 break;
1423 case WREPL_TYPE_GROUP:
1424 action = replace_group_replica_vs_X_replica(rec, replica);
1425 break;
1426 case WREPL_TYPE_SGROUP:
1427 action = replace_sgroup_replica_vs_X_replica(rec, replica);
1428 break;
1429 case WREPL_TYPE_MHOMED:
1430 action = replace_mhomed_replica_vs_X_replica(rec, replica);
1431 break;
1433 } else if (local_vs_replica) {
1434 switch (rec->type) {
1435 case WREPL_TYPE_UNIQUE:
1436 action = replace_unique_owned_vs_X_replica(rec, replica);
1437 break;
1438 case WREPL_TYPE_GROUP:
1439 action = replace_group_owned_vs_X_replica(rec, replica);
1440 break;
1441 case WREPL_TYPE_SGROUP:
1442 action = replace_sgroup_owned_vs_X_replica(rec, replica);
1443 break;
1444 case WREPL_TYPE_MHOMED:
1445 action = replace_mhomed_owned_vs_X_replica(rec, replica);
1446 break;
1450 DEBUG(4,("apply record %s: %s\n",
1451 nbt_name_string(mem_ctx, &replica->name), _R_ACTION_enum_string(action)));
1453 switch (action) {
1454 case R_INVALID: break;
1455 case R_DO_REPLACE:
1456 return r_do_replace(partner, mem_ctx, rec, owner, replica);
1457 case R_NOT_REPLACE:
1458 return r_not_replace(partner, mem_ctx, rec, owner, replica);
1459 case R_DO_PROPAGATE:
1460 return r_do_propagate(partner, mem_ctx, rec, owner, replica);
1461 case R_DO_CHALLENGE:
1462 return r_do_challenge(partner, mem_ctx, rec, owner, replica);
1463 case R_DO_RELEASE_DEMAND:
1464 return r_do_release_demand(partner, mem_ctx, rec, owner, replica);
1465 case R_DO_SGROUP_MERGE:
1466 return r_do_sgroup_merge(partner, mem_ctx, rec, owner, replica);
1469 return NT_STATUS_INTERNAL_ERROR;
1472 NTSTATUS wreplsrv_apply_records(struct wreplsrv_partner *partner,
1473 struct wrepl_wins_owner *owner,
1474 uint32_t num_names, struct wrepl_name *names)
1476 NTSTATUS status;
1477 uint32_t i;
1479 DEBUG(4,("apply records count[%u]:owner[%s]:min[%llu]:max[%llu]:partner[%s]\n",
1480 num_names, owner->address,
1481 (long long)owner->min_version,
1482 (long long)owner->max_version,
1483 partner->address));
1485 for (i=0; i < num_names; i++) {
1486 TALLOC_CTX *tmp_mem = talloc_new(partner);
1487 NT_STATUS_HAVE_NO_MEMORY(tmp_mem);
1489 status = wreplsrv_apply_one_record(partner, tmp_mem,
1490 owner, &names[i]);
1491 talloc_free(tmp_mem);
1492 NT_STATUS_NOT_OK_RETURN(status);
1495 status = wreplsrv_add_table(partner->service,
1496 partner->service,
1497 &partner->service->table,
1498 owner->address,
1499 owner->max_version);
1500 NT_STATUS_NOT_OK_RETURN(status);
1502 return NT_STATUS_OK;