/* Unix SMB/CIFS implementation. WINS Replication server Copyright (C) Stefan Metzmacher 2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include #include "smbd/service_task.h" #include "lib/messaging/irpc.h" #include "librpc/gen_ndr/ndr_irpc_c.h" #include "librpc/gen_ndr/ndr_winsrepl.h" #include "wrepl_server/wrepl_server.h" #include "nbt_server/wins/winsdb.h" #include "libcli/wrepl/winsrepl.h" #include "system/time.h" #include "librpc/gen_ndr/ndr_nbt.h" #include "param/param.h" enum _R_ACTION { R_INVALID, R_DO_REPLACE, R_NOT_REPLACE, R_DO_PROPAGATE, R_DO_CHALLENGE, R_DO_RELEASE_DEMAND, R_DO_SGROUP_MERGE }; static const char *_R_ACTION_enum_string(enum _R_ACTION action) { switch (action) { case R_INVALID: return "INVALID"; case R_DO_REPLACE: return "REPLACE"; case R_NOT_REPLACE: return "NOT_REPLACE"; case R_DO_PROPAGATE: return "PROPAGATE"; case R_DO_CHALLENGE: return "CHALLEGNE"; case R_DO_RELEASE_DEMAND: return "RELEASE_DEMAND"; case R_DO_SGROUP_MERGE: return "SGROUP_MERGE"; } return "enum _R_ACTION unknown"; } #define R_IS_ACTIVE(r) ((r)->state == WREPL_STATE_ACTIVE) #if 0 /* unused */ #define R_IS_RELEASED(r) ((r)->state == WREPL_STATE_RELEASED) #endif #define R_IS_TOMBSTONE(r) ((r)->state == WREPL_STATE_TOMBSTONE) #define R_IS_UNIQUE(r) ((r)->type == WREPL_TYPE_UNIQUE) #define R_IS_GROUP(r) ((r)->type == WREPL_TYPE_GROUP) #define R_IS_SGROUP(r) ((r)->type == WREPL_TYPE_SGROUP) #if 0 /* unused */ #define R_IS_MHOMED(r) ((r)->type == WREPL_TYPE_MHOMED) #endif /* blindly overwrite records from the same owner in all cases */ static enum _R_ACTION replace_same_owner(struct winsdb_record *r1, struct wrepl_name *r2) { /* REPLACE */ return R_DO_REPLACE; } static bool r_1_is_subset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners) { uint32_t i,j; size_t len = winsdb_addr_list_length(r1->addresses); for (i=0; i < len; i++) { bool found = false; for (j=0; j < r2->num_addresses; j++) { if (strcmp(r1->addresses[i]->address, r2->addresses[j].address) != 0) { continue; } if (check_owners && strcmp(r1->addresses[i]->wins_owner, r2->addresses[j].owner) != 0) { return false; } found = true; break; } if (!found) return false; } return true; } static bool r_1_is_superset_of_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners) { uint32_t i,j; size_t len = winsdb_addr_list_length(r1->addresses); for (i=0; i < r2->num_addresses; i++) { bool found = false; for (j=0; j < len; j++) { if (strcmp(r2->addresses[i].address, r1->addresses[j]->address) != 0) { continue; } if (check_owners && strcmp(r2->addresses[i].owner, r1->addresses[j]->wins_owner) != 0) { return false; } found = true; break; } if (!found) return false; } return true; } static bool r_1_is_same_as_2_address_list(struct winsdb_record *r1, struct wrepl_name *r2, bool check_owners) { size_t len = winsdb_addr_list_length(r1->addresses); if (len != r2->num_addresses) { return false; } return r_1_is_superset_of_2_address_list(r1, r2, check_owners); } static bool r_contains_addrs_from_owner(struct winsdb_record *r1, const char *owner) { uint32_t i; size_t len = winsdb_addr_list_length(r1->addresses); for (i=0; i < len; i++) { if (strcmp(r1->addresses[i]->wins_owner, owner) == 0) { return true; } } return false; } /* UNIQUE,ACTIVE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE UNIQUE,ACTIVE vs. UNIQUE,TOMBSTONE with different ip(s) => NOT REPLACE UNIQUE,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE UNIQUE,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE UNIQUE,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE UNIQUE,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE UNIQUE,ACTIVE vs. GROUP,ACTIVE with different ip(s) => REPLACE UNIQUE,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE UNIQUE,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE UNIQUE,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE UNIQUE,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE UNIQUE,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE UNIQUE,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE UNIQUE,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE UNIQUE,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE UNIQUE,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE UNIQUE,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE UNIQUE,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE UNIQUE,ACTIVE vs. MHOMED,ACTIVE with different ip(s) => REPLACE UNIQUE,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE UNIQUE,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE UNIQUE,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE UNIQUE,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE UNIQUE,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE */ static enum _R_ACTION replace_unique_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2) { if (!R_IS_ACTIVE(r1)) { /* REPLACE */ return R_DO_REPLACE; } if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) { /* REPLACE */ return R_DO_REPLACE; } /* NOT REPLACE */ return R_NOT_REPLACE; } /* GROUP,ACTIVE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE GROUP,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE GROUP,RELEASED vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE GROUP,RELEASED vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE GROUP,TOMBSTONE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE GROUP,TOMBSTONE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE GROUP,ACTIVE vs. GROUP,ACTIVE with same ip(s) => NOT REPLACE GROUP,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE GROUP,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE GROUP,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE GROUP,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE GROUP,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE GROUP,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE GROUP,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE GROUP,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE GROUP,RELEASED vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE GROUP,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE GROUP,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE GROUP,ACTIVE vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE GROUP,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE GROUP,RELEASED vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE GROUP,RELEASED vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE GROUP,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE GROUP,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE */ static enum _R_ACTION replace_group_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2) { if (!R_IS_ACTIVE(r1) && R_IS_GROUP(r2)) { /* REPLACE */ return R_DO_REPLACE; } if (R_IS_TOMBSTONE(r1) && !R_IS_UNIQUE(r2)) { /* REPLACE */ return R_DO_REPLACE; } /* NOT REPLACE */ return R_NOT_REPLACE; } /* SGROUP,ACTIVE vs. UNIQUE,ACTIVE with same ip(s) => NOT REPLACE SGROUP,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE SGROUP,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE SGROUP,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE SGROUP,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE SGROUP,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE SGROUP,ACTIVE vs. GROUP,ACTIVE with same ip(s) => NOT REPLACE SGROUP,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE SGROUP,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE SGROUP,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE SGROUP,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE SGROUP,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE SGROUP,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE SGROUP,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE SGROUP,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE SGROUP,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE SGROUP,ACTIVE vs. MHOMED,ACTIVE with same ip(s) => NOT REPLACE SGROUP,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE SGROUP,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE SGROUP,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE SGROUP,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE SGROUP,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4 => NOT REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:NULL => NOT REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4 => NOT REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4 vs. B:A_3_4 => REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4_OWNER_B => REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_OWNER_B vs. B:A_3_4 => REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4 => SGROUP_MERGE 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 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4 => SGROUP_MERGE 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 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 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4 => SGROUP_MERGE this is a bit strange, incoming tombstone replicas always replace old replicas: SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:NULL => B:NULL => REPLACE SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:A_3_4 => B:A_3_4 => REPLACE SGROUP,ACTIVE vs. SGROUP,TOMBSTONE A:B_3_4_X_3_4 vs. B:B_3_4 => B:B_3_4 => REPLACE 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 */ static enum _R_ACTION replace_sgroup_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2) { if (!R_IS_ACTIVE(r1)) { /* REPLACE */ return R_DO_REPLACE; } if (!R_IS_SGROUP(r2)) { /* NOT REPLACE */ return R_NOT_REPLACE; } /* * this is strange, but correct * the incoming tombstone replace the current active * record */ if (!R_IS_ACTIVE(r2)) { /* REPLACE */ return R_DO_REPLACE; } if (r2->num_addresses == 0) { if (r_contains_addrs_from_owner(r1, r2->owner)) { /* not handled here: MERGE */ return R_DO_SGROUP_MERGE; } /* NOT REPLACE */ return R_NOT_REPLACE; } if (r_1_is_superset_of_2_address_list(r1, r2, true)) { /* NOT REPLACE */ return R_NOT_REPLACE; } if (r_1_is_same_as_2_address_list(r1, r2, false)) { /* REPLACE */ return R_DO_REPLACE; } /* not handled here: MERGE */ return R_DO_SGROUP_MERGE; } /* MHOMED,ACTIVE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE MHOMED,ACTIVE vs. UNIQUE,TOMBSTONE with same ip(s) => NOT REPLACE MHOMED,RELEASED vs. UNIQUE,ACTIVE with different ip(s) => REPLACE MHOMED,RELEASED vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE MHOMED,TOMBSTONE vs. UNIQUE,ACTIVE with different ip(s) => REPLACE MHOMED,TOMBSTONE vs. UNIQUE,TOMBSTONE with different ip(s) => REPLACE MHOMED,ACTIVE vs. GROUP,ACTIVE with different ip(s) => REPLACE MHOMED,ACTIVE vs. GROUP,TOMBSTONE with same ip(s) => NOT REPLACE MHOMED,RELEASED vs. GROUP,ACTIVE with different ip(s) => REPLACE MHOMED,RELEASED vs. GROUP,TOMBSTONE with different ip(s) => REPLACE MHOMED,TOMBSTONE vs. GROUP,ACTIVE with different ip(s) => REPLACE MHOMED,TOMBSTONE vs. GROUP,TOMBSTONE with different ip(s) => REPLACE MHOMED,ACTIVE vs. SGROUP,ACTIVE with same ip(s) => NOT REPLACE MHOMED,ACTIVE vs. SGROUP,TOMBSTONE with same ip(s) => NOT REPLACE MHOMED,RELEASED vs. SGROUP,ACTIVE with different ip(s) => REPLACE MHOMED,RELEASED vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE MHOMED,TOMBSTONE vs. SGROUP,ACTIVE with different ip(s) => REPLACE MHOMED,TOMBSTONE vs. SGROUP,TOMBSTONE with different ip(s) => REPLACE MHOMED,ACTIVE vs. MHOMED,ACTIVE with different ip(s) => REPLACE MHOMED,ACTIVE vs. MHOMED,TOMBSTONE with same ip(s) => NOT REPLACE MHOMED,RELEASED vs. MHOMED,ACTIVE with different ip(s) => REPLACE MHOMED,RELEASED vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE MHOMED,TOMBSTONE vs. MHOMED,ACTIVE with different ip(s) => REPLACE MHOMED,TOMBSTONE vs. MHOMED,TOMBSTONE with different ip(s) => REPLACE */ static enum _R_ACTION replace_mhomed_replica_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2) { if (!R_IS_ACTIVE(r1)) { /* REPLACE */ return R_DO_REPLACE; } if (!R_IS_SGROUP(r2) && R_IS_ACTIVE(r2)) { /* REPLACE */ return R_DO_REPLACE; } /* NOT REPLACE */ return R_NOT_REPLACE; } /* active: _UA_UA_SI_U<00> => REPLACE _UA_UA_DI_P<00> => NOT REPLACE _UA_UA_DI_O<00> => NOT REPLACE _UA_UA_DI_N<00> => REPLACE _UA_UT_SI_U<00> => NOT REPLACE _UA_UT_DI_U<00> => NOT REPLACE _UA_GA_SI_R<00> => REPLACE _UA_GA_DI_R<00> => REPLACE _UA_GT_SI_U<00> => NOT REPLACE _UA_GT_DI_U<00> => NOT REPLACE _UA_SA_SI_R<00> => REPLACE _UA_SA_DI_R<00> => REPLACE _UA_ST_SI_U<00> => NOT REPLACE _UA_ST_DI_U<00> => NOT REPLACE _UA_MA_SI_U<00> => REPLACE _UA_MA_SP_U<00> => REPLACE _UA_MA_DI_P<00> => NOT REPLACE _UA_MA_DI_O<00> => NOT REPLACE _UA_MA_DI_N<00> => REPLACE _UA_MT_SI_U<00> => NOT REPLACE _UA_MT_DI_U<00> => NOT REPLACE Test Replica vs. owned active: some more UNIQUE,MHOMED combinations _UA_UA_DI_A<00> => MHOMED_MERGE _UA_MA_DI_A<00> => MHOMED_MERGE released: _UR_UA_SI<00> => REPLACE _UR_UA_DI<00> => REPLACE _UR_UT_SI<00> => REPLACE _UR_UT_DI<00> => REPLACE _UR_GA_SI<00> => REPLACE _UR_GA_DI<00> => REPLACE _UR_GT_SI<00> => REPLACE _UR_GT_DI<00> => REPLACE _UR_SA_SI<00> => REPLACE _UR_SA_DI<00> => REPLACE _UR_ST_SI<00> => REPLACE _UR_ST_DI<00> => REPLACE _UR_MA_SI<00> => REPLACE _UR_MA_DI<00> => REPLACE _UR_MT_SI<00> => REPLACE _UR_MT_DI<00> => REPLACE */ static enum _R_ACTION replace_unique_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2) { if (!R_IS_ACTIVE(r1)) { /* REPLACE */ return R_DO_REPLACE; } if (!R_IS_ACTIVE(r2)) { /* NOT REPLACE, and PROPAGATE */ return R_DO_PROPAGATE; } if (R_IS_GROUP(r2) || R_IS_SGROUP(r2)) { /* REPLACE and send a release demand to the old name owner */ return R_DO_RELEASE_DEMAND; } /* * here we only have unique,active,owned vs. * is unique,active,replica or mhomed,active,replica */ if (r_1_is_subset_of_2_address_list(r1, r2, false)) { /* * if r1 has a subset(or same) of the addresses of r2 * <=> * if r2 has a superset(or same) of the addresses of r1 * * then replace the record */ return R_DO_REPLACE; } /* * in any other case, we need to do * a name request to the old name holder * to see if it's still there... */ return R_DO_CHALLENGE; } /* active: _GA_UA_SI_U<00> => NOT REPLACE _GA_UA_DI_U<00> => NOT REPLACE _GA_UT_SI_U<00> => NOT REPLACE _GA_UT_DI_U<00> => NOT REPLACE _GA_GA_SI_U<00> => REPLACE _GA_GA_DI_U<00> => REPLACE _GA_GT_SI_U<00> => NOT REPLACE _GA_GT_DI_U<00> => NOT REPLACE _GA_SA_SI_U<00> => NOT REPLACE _GA_SA_DI_U<00> => NOT REPLACE _GA_ST_SI_U<00> => NOT REPLACE _GA_ST_DI_U<00> => NOT REPLACE _GA_MA_SI_U<00> => NOT REPLACE _GA_MA_DI_U<00> => NOT REPLACE _GA_MT_SI_U<00> => NOT REPLACE _GA_MT_DI_U<00> => NOT REPLACE released: _GR_UA_SI<00> => NOT REPLACE _GR_UA_DI<00> => NOT REPLACE _GR_UT_SI<00> => NOT REPLACE _GR_UT_DI<00> => NOT REPLACE _GR_GA_SI<00> => REPLACE _GR_GA_DI<00> => REPLACE _GR_GT_SI<00> => REPLACE _GR_GT_DI<00> => REPLACE _GR_SA_SI<00> => NOT REPLACE _GR_SA_DI<00> => NOT REPLACE _GR_ST_SI<00> => NOT REPLACE _GR_ST_DI<00> => NOT REPLACE _GR_MA_SI<00> => NOT REPLACE _GR_MA_DI<00> => NOT REPLACE _GR_MT_SI<00> => NOT REPLACE _GR_MT_DI<00> => NOT REPLACE */ static enum _R_ACTION replace_group_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2) { if (R_IS_GROUP(r1) && R_IS_GROUP(r2)) { if (!R_IS_ACTIVE(r1) || R_IS_ACTIVE(r2)) { /* REPLACE */ return R_DO_REPLACE; } } /* NOT REPLACE, but PROPAGATE */ return R_DO_PROPAGATE; } /* active (not sgroup vs. sgroup yet!): _SA_UA_SI_U<1c> => NOT REPLACE _SA_UA_DI_U<1c> => NOT REPLACE _SA_UT_SI_U<1c> => NOT REPLACE _SA_UT_DI_U<1c> => NOT REPLACE _SA_GA_SI_U<1c> => NOT REPLACE _SA_GA_DI_U<1c> => NOT REPLACE _SA_GT_SI_U<1c> => NOT REPLACE _SA_GT_DI_U<1c> => NOT REPLACE _SA_MA_SI_U<1c> => NOT REPLACE _SA_MA_DI_U<1c> => NOT REPLACE _SA_MT_SI_U<1c> => NOT REPLACE _SA_MT_DI_U<1c> => NOT REPLACE Test Replica vs. owned active: SGROUP vs. SGROUP tests _SA_SA_DI_U<1c> => SGROUP_MERGE _SA_SA_SI_U<1c> => SGROUP_MERGE _SA_SA_SP_U<1c> => SGROUP_MERGE _SA_SA_SB_U<1c> => SGROUP_MERGE _SA_ST_DI_U<1c> => NOT REPLACE _SA_ST_SI_U<1c> => NOT REPLACE _SA_ST_SP_U<1c> => NOT REPLACE _SA_ST_SB_U<1c> => NOT REPLACE SGROUP,ACTIVE vs. SGROUP,* is not handled here! released: _SR_UA_SI<1c> => REPLACE _SR_UA_DI<1c> => REPLACE _SR_UT_SI<1c> => REPLACE _SR_UT_DI<1c> => REPLACE _SR_GA_SI<1c> => REPLACE _SR_GA_DI<1c> => REPLACE _SR_GT_SI<1c> => REPLACE _SR_GT_DI<1c> => REPLACE _SR_SA_SI<1c> => REPLACE _SR_SA_DI<1c> => REPLACE _SR_ST_SI<1c> => REPLACE _SR_ST_DI<1c> => REPLACE _SR_MA_SI<1c> => REPLACE _SR_MA_DI<1c> => REPLACE _SR_MT_SI<1c> => REPLACE _SR_MT_DI<1c> => REPLACE */ static enum _R_ACTION replace_sgroup_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2) { if (!R_IS_ACTIVE(r1)) { /* REPLACE */ return R_DO_REPLACE; } if (!R_IS_SGROUP(r2) || !R_IS_ACTIVE(r2)) { /* NOT REPLACE, but PROPAGATE */ return R_DO_PROPAGATE; } if (r_1_is_same_as_2_address_list(r1, r2, true)) { /* * as we're the old owner and the addresses and their * owners are identical */ return R_NOT_REPLACE; } /* not handled here: MERGE */ return R_DO_SGROUP_MERGE; } /* active: _MA_UA_SI_U<00> => REPLACE _MA_UA_DI_P<00> => NOT REPLACE _MA_UA_DI_O<00> => NOT REPLACE _MA_UA_DI_N<00> => REPLACE _MA_UT_SI_U<00> => NOT REPLACE _MA_UT_DI_U<00> => NOT REPLACE _MA_GA_SI_R<00> => REPLACE _MA_GA_DI_R<00> => REPLACE _MA_GT_SI_U<00> => NOT REPLACE _MA_GT_DI_U<00> => NOT REPLACE _MA_SA_SI_R<00> => REPLACE _MA_SA_DI_R<00> => REPLACE _MA_ST_SI_U<00> => NOT REPLACE _MA_ST_DI_U<00> => NOT REPLACE _MA_MA_SI_U<00> => REPLACE _MA_MA_SP_U<00> => REPLACE _MA_MA_DI_P<00> => NOT REPLACE _MA_MA_DI_O<00> => NOT REPLACE _MA_MA_DI_N<00> => REPLACE _MA_MT_SI_U<00> => NOT REPLACE _MA_MT_DI_U<00> => NOT REPLACE Test Replica vs. owned active: some more MHOMED combinations _MA_MA_SP_U<00> => REPLACE _MA_MA_SM_U<00> => REPLACE _MA_MA_SB_P<00> => MHOMED_MERGE _MA_MA_SB_A<00> => MHOMED_MERGE _MA_MA_SB_PRA<00> => NOT REPLACE _MA_MA_SB_O<00> => NOT REPLACE _MA_MA_SB_N<00> => REPLACE Test Replica vs. owned active: some more UNIQUE,MHOMED combinations _MA_UA_SB_P<00> => MHOMED_MERGE released: _MR_UA_SI<00> => REPLACE _MR_UA_DI<00> => REPLACE _MR_UT_SI<00> => REPLACE _MR_UT_DI<00> => REPLACE _MR_GA_SI<00> => REPLACE _MR_GA_DI<00> => REPLACE _MR_GT_SI<00> => REPLACE _MR_GT_DI<00> => REPLACE _MR_SA_SI<00> => REPLACE _MR_SA_DI<00> => REPLACE _MR_ST_SI<00> => REPLACE _MR_ST_DI<00> => REPLACE _MR_MA_SI<00> => REPLACE _MR_MA_DI<00> => REPLACE _MR_MT_SI<00> => REPLACE _MR_MT_DI<00> => REPLACE */ static enum _R_ACTION replace_mhomed_owned_vs_X_replica(struct winsdb_record *r1, struct wrepl_name *r2) { if (!R_IS_ACTIVE(r1)) { /* REPLACE */ return R_DO_REPLACE; } if (!R_IS_ACTIVE(r2)) { /* NOT REPLACE, but PROPAGATE */ return R_DO_PROPAGATE; } if (R_IS_GROUP(r2) || R_IS_SGROUP(r2)) { /* REPLACE and send a release demand to the old name owner */ return R_DO_RELEASE_DEMAND; } /* * here we only have mhomed,active,owned vs. * is unique,active,replica or mhomed,active,replica */ if (r_1_is_subset_of_2_address_list(r1, r2, false)) { /* * if r1 has a subset(or same) of the addresses of r2 * <=> * if r2 has a superset(or same) of the addresses of r1 * * then replace the record */ return R_DO_REPLACE; } /* * in any other case, we need to do * a name request to the old name holder * to see if it's still there... */ return R_DO_CHALLENGE; } static NTSTATUS r_do_add(struct wreplsrv_partner *partner, TALLOC_CTX *mem_ctx, struct wrepl_wins_owner *owner, struct wrepl_name *replica) { struct winsdb_record *rec; uint32_t i; uint8_t ret; rec = talloc(mem_ctx, struct winsdb_record); NT_STATUS_HAVE_NO_MEMORY(rec); rec->name = &replica->name; rec->type = replica->type; rec->state = replica->state; rec->node = replica->node; rec->is_static = replica->is_static; rec->expire_time= time(NULL) + partner->service->config.verify_interval; rec->version = replica->version_id; rec->wins_owner = replica->owner; rec->addresses = winsdb_addr_list_make(rec); NT_STATUS_HAVE_NO_MEMORY(rec->addresses); rec->registered_by = NULL; for (i=0; i < replica->num_addresses; i++) { /* TODO: find out if rec->expire_time is correct here */ rec->addresses = winsdb_addr_list_add(partner->service->wins_db, rec, rec->addresses, replica->addresses[i].address, replica->addresses[i].owner, rec->expire_time, false); NT_STATUS_HAVE_NO_MEMORY(rec->addresses); } ret = winsdb_add(partner->service->wins_db, rec, 0); if (ret != NBT_RCODE_OK) { DEBUG(0,("Failed to add record %s: %u\n", nbt_name_string(mem_ctx, &replica->name), ret)); return NT_STATUS_FOOBAR; } DEBUG(4,("added record %s\n", nbt_name_string(mem_ctx, &replica->name))); return NT_STATUS_OK; } static NTSTATUS r_do_replace(struct wreplsrv_partner *partner, TALLOC_CTX *mem_ctx, struct winsdb_record *rec, struct wrepl_wins_owner *owner, struct wrepl_name *replica) { uint32_t i; uint8_t ret; rec->name = &replica->name; rec->type = replica->type; rec->state = replica->state; rec->node = replica->node; rec->is_static = replica->is_static; rec->expire_time= time(NULL) + partner->service->config.verify_interval; rec->version = replica->version_id; rec->wins_owner = replica->owner; rec->addresses = winsdb_addr_list_make(rec); NT_STATUS_HAVE_NO_MEMORY(rec->addresses); rec->registered_by = NULL; for (i=0; i < replica->num_addresses; i++) { /* TODO: find out if rec->expire_time is correct here */ rec->addresses = winsdb_addr_list_add(partner->service->wins_db, rec, rec->addresses, replica->addresses[i].address, replica->addresses[i].owner, rec->expire_time, false); NT_STATUS_HAVE_NO_MEMORY(rec->addresses); } ret = winsdb_modify(partner->service->wins_db, rec, 0); if (ret != NBT_RCODE_OK) { DEBUG(0,("Failed to replace record %s: %u\n", nbt_name_string(mem_ctx, &replica->name), ret)); return NT_STATUS_FOOBAR; } DEBUG(4,("replaced record %s\n", nbt_name_string(mem_ctx, &replica->name))); return NT_STATUS_OK; } static NTSTATUS r_not_replace(struct wreplsrv_partner *partner, TALLOC_CTX *mem_ctx, struct winsdb_record *rec, struct wrepl_wins_owner *owner, struct wrepl_name *replica) { DEBUG(4,("not replace record %s\n", nbt_name_string(mem_ctx, &replica->name))); return NT_STATUS_OK; } static NTSTATUS r_do_propagate(struct wreplsrv_partner *partner, TALLOC_CTX *mem_ctx, struct winsdb_record *rec, struct wrepl_wins_owner *owner, struct wrepl_name *replica) { uint8_t ret; uint32_t modify_flags; /* * allocate a new version id for the record to that it'll be replicated */ modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP; ret = winsdb_modify(partner->service->wins_db, rec, modify_flags); if (ret != NBT_RCODE_OK) { DEBUG(0,("Failed to replace record %s: %u\n", nbt_name_string(mem_ctx, &replica->name), ret)); return NT_STATUS_FOOBAR; } DEBUG(4,("propagated record %s\n", nbt_name_string(mem_ctx, &replica->name))); return NT_STATUS_OK; } /* Test Replica vs. owned active: some more MHOMED combinations _MA_MA_SP_U<00>: C:MHOMED vs. B:ALL => B:ALL => REPLACE _MA_MA_SM_U<00>: C:MHOMED vs. B:MHOMED => B:MHOMED => REPLACE _MA_MA_SB_P<00>: C:MHOMED vs. B:BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE _MA_MA_SB_A<00>: C:MHOMED vs. B:BEST (C:ALL) => B:MHOMED => MHOMED_MERGE _MA_MA_SB_PRA<00>: C:MHOMED vs. B:BEST (C:BEST) => C:MHOMED => NOT REPLACE _MA_MA_SB_O<00>: C:MHOMED vs. B:BEST (B:B_3_4) =>C:MHOMED => NOT REPLACE _MA_MA_SB_N<00>: C:MHOMED vs. B:BEST (NEGATIVE) => B:BEST => REPLACE Test Replica vs. owned active: some more UNIQUE,MHOMED combinations _MA_UA_SB_P<00>: C:MHOMED vs. B:UNIQUE,BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE _UA_UA_DI_PRA<00>: C:BEST vs. B:BEST2 (C:BEST2,LR:BEST2) => C:BEST => NOT REPLACE _UA_UA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE _UA_MA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE */ static NTSTATUS r_do_mhomed_merge(struct wreplsrv_partner *partner, TALLOC_CTX *mem_ctx, struct winsdb_record *rec, struct wrepl_wins_owner *owner, struct wrepl_name *replica) { struct winsdb_record *merge; uint32_t i,j; uint8_t ret; size_t len; merge = talloc(mem_ctx, struct winsdb_record); NT_STATUS_HAVE_NO_MEMORY(merge); merge->name = &replica->name; merge->type = WREPL_TYPE_MHOMED; merge->state = replica->state; merge->node = replica->node; merge->is_static = replica->is_static; merge->expire_time = time(NULL) + partner->service->config.verify_interval; merge->version = replica->version_id; merge->wins_owner = replica->owner; merge->addresses = winsdb_addr_list_make(merge); NT_STATUS_HAVE_NO_MEMORY(merge->addresses); merge->registered_by = NULL; for (i=0; i < replica->num_addresses; i++) { merge->addresses = winsdb_addr_list_add(partner->service->wins_db, merge, merge->addresses, replica->addresses[i].address, replica->addresses[i].owner, merge->expire_time, false); NT_STATUS_HAVE_NO_MEMORY(merge->addresses); } len = winsdb_addr_list_length(rec->addresses); for (i=0; i < len; i++) { bool found = false; for (j=0; j < replica->num_addresses; j++) { if (strcmp(replica->addresses[j].address, rec->addresses[i]->address) == 0) { found = true; break; } } if (found) continue; merge->addresses = winsdb_addr_list_add(partner->service->wins_db, merge, merge->addresses, rec->addresses[i]->address, rec->addresses[i]->wins_owner, rec->addresses[i]->expire_time, false); NT_STATUS_HAVE_NO_MEMORY(merge->addresses); } ret = winsdb_modify(partner->service->wins_db, merge, 0); if (ret != NBT_RCODE_OK) { DEBUG(0,("Failed to modify mhomed merge record %s: %u\n", nbt_name_string(mem_ctx, &replica->name), ret)); return NT_STATUS_FOOBAR; } DEBUG(4,("mhomed merge record %s\n", nbt_name_string(mem_ctx, &replica->name))); return NT_STATUS_OK; } struct r_do_challenge_state { struct dcerpc_binding_handle *irpc_handle; struct wreplsrv_partner *partner; struct winsdb_record *rec; struct wrepl_wins_owner owner; struct wrepl_name replica; struct nbtd_proxy_wins_challenge r; struct nbtd_proxy_wins_release_demand dr; }; static void r_do_late_release_demand_handler(struct tevent_req *subreq) { NTSTATUS status; struct r_do_challenge_state *state = tevent_req_callback_data(subreq, struct r_do_challenge_state); status = dcerpc_nbtd_proxy_wins_release_demand_r_recv(subreq, state); TALLOC_FREE(subreq); /* don't care about the result */ talloc_free(state); } static NTSTATUS r_do_late_release_demand(struct r_do_challenge_state *state) { struct tevent_req *subreq; uint32_t i; DEBUG(4,("late release demand record %s\n", nbt_name_string(state, &state->replica.name))); state->dr.in.name = state->replica.name; state->dr.in.num_addrs = state->r.out.num_addrs; state->dr.in.addrs = talloc_array(state, struct nbtd_proxy_wins_addr, state->dr.in.num_addrs); NT_STATUS_HAVE_NO_MEMORY(state->dr.in.addrs); /* TODO: fix pidl to handle inline ipv4address arrays */ for (i=0; i < state->dr.in.num_addrs; i++) { state->dr.in.addrs[i].addr = state->r.out.addrs[i].addr; } subreq = dcerpc_nbtd_proxy_wins_release_demand_r_send(state, state->partner->service->task->event_ctx, state->irpc_handle, &state->dr); NT_STATUS_HAVE_NO_MEMORY(subreq); tevent_req_set_callback(subreq, r_do_late_release_demand_handler, state); return NT_STATUS_OK; } /* Test Replica vs. owned active: some more MHOMED combinations _MA_MA_SP_U<00>: C:MHOMED vs. B:ALL => B:ALL => REPLACE _MA_MA_SM_U<00>: C:MHOMED vs. B:MHOMED => B:MHOMED => REPLACE _MA_MA_SB_P<00>: C:MHOMED vs. B:BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE _MA_MA_SB_A<00>: C:MHOMED vs. B:BEST (C:ALL) => B:MHOMED => MHOMED_MERGE _MA_MA_SB_PRA<00>: C:MHOMED vs. B:BEST (C:BEST) => C:MHOMED => NOT REPLACE _MA_MA_SB_O<00>: C:MHOMED vs. B:BEST (B:B_3_4) =>C:MHOMED => NOT REPLACE _MA_MA_SB_N<00>: C:MHOMED vs. B:BEST (NEGATIVE) => B:BEST => REPLACE Test Replica vs. owned active: some more UNIQUE,MHOMED combinations _MA_UA_SB_P<00>: C:MHOMED vs. B:UNIQUE,BEST (C:MHOMED) => B:MHOMED => MHOMED_MERGE _UA_UA_DI_PRA<00>: C:BEST vs. B:BEST2 (C:BEST2,LR:BEST2) => C:BEST => NOT REPLACE _UA_UA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE _UA_MA_DI_A<00>: C:BEST vs. B:BEST2 (C:ALL) => B:MHOMED => MHOMED_MERGE */ static void r_do_challenge_handler(struct tevent_req *subreq) { NTSTATUS status; struct r_do_challenge_state *state = tevent_req_callback_data(subreq, struct r_do_challenge_state); bool old_is_subset = false; bool new_is_subset = false; bool found = false; uint32_t i,j; uint32_t num_rec_addrs; status = dcerpc_nbtd_proxy_wins_challenge_r_recv(subreq, state); TALLOC_FREE(subreq); DEBUG(4,("r_do_challenge_handler: %s: %s\n", nbt_name_string(state, &state->replica.name), nt_errstr(status))); if (NT_STATUS_EQUAL(NT_STATUS_IO_TIMEOUT, status) || NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) { r_do_replace(state->partner, state, state->rec, &state->owner, &state->replica); talloc_free(state); return; } for (i=0; i < state->replica.num_addresses; i++) { found = false; new_is_subset = true; for (j=0; j < state->r.out.num_addrs; j++) { if (strcmp(state->replica.addresses[i].address, state->r.out.addrs[j].addr) == 0) { found = true; break; } } if (found) continue; new_is_subset = false; break; } if (!new_is_subset) { r_not_replace(state->partner, state, state->rec, &state->owner, &state->replica); talloc_free(state); return; } num_rec_addrs = winsdb_addr_list_length(state->rec->addresses); for (i=0; i < num_rec_addrs; i++) { found = false; old_is_subset = true; for (j=0; j < state->r.out.num_addrs; j++) { if (strcmp(state->rec->addresses[i]->address, state->r.out.addrs[j].addr) == 0) { found = true; break; } } if (found) continue; old_is_subset = false; break; } if (!old_is_subset) { status = r_do_late_release_demand(state); /* * only free state on error, because we pass it down, * and r_do_late_release_demand() will free it */ if (!NT_STATUS_IS_OK(status)) { talloc_free(state); } return; } r_do_mhomed_merge(state->partner, state, state->rec, &state->owner, &state->replica); talloc_free(state); } static NTSTATUS r_do_challenge(struct wreplsrv_partner *partner, TALLOC_CTX *mem_ctx, struct winsdb_record *rec, struct wrepl_wins_owner *owner, struct wrepl_name *replica) { struct r_do_challenge_state *state; struct tevent_req *subreq; const char **addrs; uint32_t i; DEBUG(4,("challenge record %s\n", nbt_name_string(mem_ctx, &replica->name))); state = talloc_zero(mem_ctx, struct r_do_challenge_state); NT_STATUS_HAVE_NO_MEMORY(state); state->partner = partner; state->rec = talloc_steal(state, rec); state->owner = *owner; state->replica = *replica; /* some stuff to have valid memory pointers in the async complete function */ state->replica.name = *state->rec->name; talloc_steal(state, replica->owner); talloc_steal(state, replica->addresses); state->irpc_handle = irpc_binding_handle_by_name(state, partner->service->task->msg_ctx, "nbt_server", &ndr_table_irpc); if (state->irpc_handle == NULL) { return NT_STATUS_INTERNAL_ERROR; } state->r.in.name = *rec->name; state->r.in.num_addrs = winsdb_addr_list_length(rec->addresses); state->r.in.addrs = talloc_array(state, struct nbtd_proxy_wins_addr, state->r.in.num_addrs); NT_STATUS_HAVE_NO_MEMORY(state->r.in.addrs); /* TODO: fix pidl to handle inline ipv4address arrays */ addrs = winsdb_addr_string_list(state->r.in.addrs, rec->addresses); NT_STATUS_HAVE_NO_MEMORY(addrs); for (i=0; i < state->r.in.num_addrs; i++) { state->r.in.addrs[i].addr = addrs[i]; } subreq = dcerpc_nbtd_proxy_wins_challenge_r_send(state, state->partner->service->task->event_ctx, state->irpc_handle, &state->r); NT_STATUS_HAVE_NO_MEMORY(subreq); tevent_req_set_callback(subreq, r_do_challenge_handler, state); talloc_steal(partner, state); return NT_STATUS_OK; } struct r_do_release_demand_state { struct nbtd_proxy_wins_release_demand r; }; static void r_do_release_demand_handler(struct tevent_req *subreq) { NTSTATUS status; struct r_do_release_demand_state *state = tevent_req_callback_data(subreq, struct r_do_release_demand_state); status = dcerpc_nbtd_proxy_wins_release_demand_r_recv(subreq, state); TALLOC_FREE(subreq); /* don't care about the result */ talloc_free(state); } static NTSTATUS r_do_release_demand(struct wreplsrv_partner *partner, TALLOC_CTX *mem_ctx, struct winsdb_record *rec, struct wrepl_wins_owner *owner, struct wrepl_name *replica) { NTSTATUS status; struct dcerpc_binding_handle *irpc_handle; const char **addrs; struct winsdb_addr **addresses; struct r_do_release_demand_state *state; struct tevent_req *subreq; uint32_t i; /* * we need to get a reference to the old addresses, * as we need to send a release demand to them after replacing the record * and r_do_replace() will modify rec->addresses */ addresses = rec->addresses; status = r_do_replace(partner, mem_ctx, rec, owner, replica); NT_STATUS_NOT_OK_RETURN(status); DEBUG(4,("release demand record %s\n", nbt_name_string(mem_ctx, &replica->name))); state = talloc_zero(mem_ctx, struct r_do_release_demand_state); NT_STATUS_HAVE_NO_MEMORY(state); irpc_handle = irpc_binding_handle_by_name(state, partner->service->task->msg_ctx, "nbt_server", &ndr_table_irpc); if (irpc_handle == NULL) { return NT_STATUS_INTERNAL_ERROR; } state->r.in.name = *rec->name; state->r.in.num_addrs = winsdb_addr_list_length(addresses); state->r.in.addrs = talloc_array(state, struct nbtd_proxy_wins_addr, state->r.in.num_addrs); NT_STATUS_HAVE_NO_MEMORY(state->r.in.addrs); /* TODO: fix pidl to handle inline ipv4address arrays */ addrs = winsdb_addr_string_list(state->r.in.addrs, addresses); NT_STATUS_HAVE_NO_MEMORY(addrs); for (i=0; i < state->r.in.num_addrs; i++) { state->r.in.addrs[i].addr = addrs[i]; } subreq = dcerpc_nbtd_proxy_wins_release_demand_r_send(state, partner->service->task->event_ctx, irpc_handle, &state->r); NT_STATUS_HAVE_NO_MEMORY(subreq); tevent_req_set_callback(subreq, r_do_release_demand_handler, state); talloc_steal(partner, state); return NT_STATUS_OK; } /* SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4 => NOT REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:NULL => NOT REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_X_3_4 vs. B:A_3_4 => NOT REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4 vs. B:A_3_4 => REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:A_3_4_OWNER_B => REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4_OWNER_B vs. B:A_3_4 => REPLACE SGROUP,ACTIVE vs. SGROUP,ACTIVE A:A_3_4 vs. B:B_3_4 => C:A_3_4_B_3_4 => SGROUP_MERGE 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 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:X_3_4 vs. B:A_3_4 => C:A_3_4_X_3_4 => SGROUP_MERGE 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 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 SGROUP,ACTIVE vs. SGROUP,ACTIVE A:B_3_4_X_3_4 vs. B:NULL => B:X_3_4 => SGROUP_MERGE Test Replica vs. owned active: SGROUP vs. SGROUP tests _SA_SA_DI_U<1c> => SGROUP_MERGE _SA_SA_SI_U<1c> => SGROUP_MERGE _SA_SA_SP_U<1c> => SGROUP_MERGE _SA_SA_SB_U<1c> => SGROUP_MERGE */ static NTSTATUS r_do_sgroup_merge(struct wreplsrv_partner *partner, TALLOC_CTX *mem_ctx, struct winsdb_record *rec, struct wrepl_wins_owner *owner, struct wrepl_name *replica) { struct winsdb_record *merge; uint32_t modify_flags = 0; uint32_t i,j; uint8_t ret; size_t len; bool changed_old_addrs = false; bool skip_replica_owned_by_us = false; bool become_owner = true; bool propagate = lpcfg_parm_bool(partner->service->task->lp_ctx, NULL, "wreplsrv", "propagate name releases", false); const char *local_owner = partner->service->wins_db->local_owner; merge = talloc(mem_ctx, struct winsdb_record); NT_STATUS_HAVE_NO_MEMORY(merge); merge->name = &replica->name; merge->type = replica->type; merge->state = replica->state; merge->node = replica->node; merge->is_static = replica->is_static; merge->expire_time = time(NULL) + partner->service->config.verify_interval; merge->version = replica->version_id; merge->wins_owner = replica->owner; merge->addresses = winsdb_addr_list_make(merge); NT_STATUS_HAVE_NO_MEMORY(merge->addresses); merge->registered_by = NULL; len = winsdb_addr_list_length(rec->addresses); for (i=0; i < len; i++) { bool found = false; for (j=0; j < replica->num_addresses; j++) { if (strcmp(rec->addresses[i]->address, replica->addresses[j].address) != 0) { continue; } found = true; if (strcmp(rec->addresses[i]->wins_owner, replica->addresses[j].owner) != 0) { changed_old_addrs = true; break; } break; } /* * if the address isn't in the replica and is owned by replicas owner, * it won't be added to the merged record */ if (!found && strcmp(rec->addresses[i]->wins_owner, owner->address) == 0) { changed_old_addrs = true; continue; } /* * add the address to the merge result, with the old owner and expire_time, * the owner and expire_time will be overwritten later if the address is * in the replica too */ merge->addresses = winsdb_addr_list_add(partner->service->wins_db, merge, merge->addresses, rec->addresses[i]->address, rec->addresses[i]->wins_owner, rec->addresses[i]->expire_time, false); NT_STATUS_HAVE_NO_MEMORY(merge->addresses); } for (i=0; i < replica->num_addresses; i++) { if (propagate && strcmp(replica->addresses[i].owner, local_owner) == 0) { const struct winsdb_addr *a; /* * NOTE: this is different to the windows behavior * and off by default, but it better propagated * name releases */ a = winsdb_addr_list_check(merge->addresses, replica->addresses[i].address); if (!a) { /* don't add addresses owned by us */ skip_replica_owned_by_us = true; } continue; } merge->addresses = winsdb_addr_list_add(partner->service->wins_db, merge, merge->addresses, replica->addresses[i].address, replica->addresses[i].owner, merge->expire_time, false); NT_STATUS_HAVE_NO_MEMORY(merge->addresses); } /* we the old addresses change changed we don't become the owner */ if (changed_old_addrs) { become_owner = false; } /* * when we notice another server believes an address * is owned by us and that's not the case * we propagate the result */ if (skip_replica_owned_by_us) { become_owner = true; } /* if we're the owner of the old record, we'll be the owner of the new one too */ if (strcmp(rec->wins_owner, local_owner)==0) { become_owner = true; } /* * if the result has no addresses we take the ownership */ len = winsdb_addr_list_length(merge->addresses); if (len == 0) { become_owner = true; } /* * if addresses of the old record will be changed the replica owner * will be owner of the merge result, otherwise we take the ownership */ if (become_owner) { time_t lh = 0; modify_flags = WINSDB_FLAG_ALLOC_VERSION | WINSDB_FLAG_TAKE_OWNERSHIP; /* * if we're the owner, the expire time becomes the highest * expire time of owned addresses */ len = winsdb_addr_list_length(merge->addresses); for (i=0; i < len; i++) { if (strcmp(merge->addresses[i]->wins_owner, local_owner)==0) { lh = MAX(lh, merge->addresses[i]->expire_time); } } if (lh != 0) { merge->expire_time = lh; } } ret = winsdb_modify(partner->service->wins_db, merge, modify_flags); if (ret != NBT_RCODE_OK) { DEBUG(0,("Failed to modify sgroup merge record %s: %u\n", nbt_name_string(mem_ctx, &replica->name), ret)); return NT_STATUS_FOOBAR; } DEBUG(4,("sgroup merge record %s\n", nbt_name_string(mem_ctx, &replica->name))); return NT_STATUS_OK; } static NTSTATUS wreplsrv_apply_one_record(struct wreplsrv_partner *partner, TALLOC_CTX *mem_ctx, struct wrepl_wins_owner *owner, struct wrepl_name *replica) { NTSTATUS status; struct winsdb_record *rec = NULL; enum _R_ACTION action = R_INVALID; bool same_owner = false; bool replica_vs_replica = false; bool local_vs_replica = false; if (replica->name.scope) { TALLOC_CTX *parent; const char *scope; /* * Windows 2008 truncates the scope to 237 bytes, * so we do... */ parent = talloc_parent(replica->name.scope); scope = talloc_strndup(parent, replica->name.scope, 237); NT_STATUS_HAVE_NO_MEMORY(scope); replica->name.scope = scope; } status = winsdb_lookup(partner->service->wins_db, &replica->name, mem_ctx, &rec); if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) { return r_do_add(partner, mem_ctx, owner, replica); } NT_STATUS_NOT_OK_RETURN(status); if (strcmp(rec->wins_owner, partner->service->wins_db->local_owner)==0) { local_vs_replica = true; } else if (strcmp(rec->wins_owner, owner->address)==0) { same_owner = true; } else { replica_vs_replica = true; } if (rec->is_static && !same_owner) { action = R_NOT_REPLACE; /* * if we own the local record, then propagate it back to * the other wins servers. * to prevent ping-pong with other servers, we don't do this * if the replica is static too. * * It seems that w2k3 doesn't do this, but I thing that's a bug * and doing propagation helps to have consistent data on all servers */ if (local_vs_replica && !replica->is_static) { action = R_DO_PROPAGATE; } } else if (replica->is_static && !rec->is_static && !same_owner) { action = R_DO_REPLACE; } else if (same_owner) { action = replace_same_owner(rec, replica); } else if (replica_vs_replica) { switch (rec->type) { case WREPL_TYPE_UNIQUE: action = replace_unique_replica_vs_X_replica(rec, replica); break; case WREPL_TYPE_GROUP: action = replace_group_replica_vs_X_replica(rec, replica); break; case WREPL_TYPE_SGROUP: action = replace_sgroup_replica_vs_X_replica(rec, replica); break; case WREPL_TYPE_MHOMED: action = replace_mhomed_replica_vs_X_replica(rec, replica); break; } } else if (local_vs_replica) { switch (rec->type) { case WREPL_TYPE_UNIQUE: action = replace_unique_owned_vs_X_replica(rec, replica); break; case WREPL_TYPE_GROUP: action = replace_group_owned_vs_X_replica(rec, replica); break; case WREPL_TYPE_SGROUP: action = replace_sgroup_owned_vs_X_replica(rec, replica); break; case WREPL_TYPE_MHOMED: action = replace_mhomed_owned_vs_X_replica(rec, replica); break; } } DEBUG(4,("apply record %s: %s\n", nbt_name_string(mem_ctx, &replica->name), _R_ACTION_enum_string(action))); switch (action) { case R_INVALID: break; case R_DO_REPLACE: return r_do_replace(partner, mem_ctx, rec, owner, replica); case R_NOT_REPLACE: return r_not_replace(partner, mem_ctx, rec, owner, replica); case R_DO_PROPAGATE: return r_do_propagate(partner, mem_ctx, rec, owner, replica); case R_DO_CHALLENGE: return r_do_challenge(partner, mem_ctx, rec, owner, replica); case R_DO_RELEASE_DEMAND: return r_do_release_demand(partner, mem_ctx, rec, owner, replica); case R_DO_SGROUP_MERGE: return r_do_sgroup_merge(partner, mem_ctx, rec, owner, replica); } return NT_STATUS_INTERNAL_ERROR; } NTSTATUS wreplsrv_apply_records(struct wreplsrv_partner *partner, struct wrepl_wins_owner *owner, uint32_t num_names, struct wrepl_name *names) { NTSTATUS status; uint32_t i; DEBUG(4,("apply records count[%u]:owner[%s]:min[%llu]:max[%llu]:partner[%s]\n", num_names, owner->address, (long long)owner->min_version, (long long)owner->max_version, partner->address)); for (i=0; i < num_names; i++) { TALLOC_CTX *tmp_mem = talloc_new(partner); NT_STATUS_HAVE_NO_MEMORY(tmp_mem); status = wreplsrv_apply_one_record(partner, tmp_mem, owner, &names[i]); talloc_free(tmp_mem); NT_STATUS_NOT_OK_RETURN(status); } status = wreplsrv_add_table(partner->service, partner->service, &partner->service->table, owner->address, owner->max_version); NT_STATUS_NOT_OK_RETURN(status); return NT_STATUS_OK; }