LCOV - code coverage report
Current view: top level - source4/dsdb/repl - drepl_notify.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 164 211 77.7 %
Date: 2024-04-21 15:09:00 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS Implementation.
       3             : 
       4             :    DSDB replication service periodic notification handling
       5             :    
       6             :    Copyright (C) Andrew Tridgell 2009
       7             :    based on drepl_periodic
       8             :     
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             :    
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             :    
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             :    
      22             : */
      23             : 
      24             : #include "includes.h"
      25             : #include "lib/events/events.h"
      26             : #include "dsdb/samdb/samdb.h"
      27             : #include "auth/auth.h"
      28             : #include "samba/service.h"
      29             : #include "dsdb/repl/drepl_service.h"
      30             : #include <ldb_errors.h>
      31             : #include "../lib/util/dlinklist.h"
      32             : #include "librpc/gen_ndr/ndr_misc.h"
      33             : #include "librpc/gen_ndr/ndr_drsuapi.h"
      34             : #include "librpc/gen_ndr/ndr_drsblobs.h"
      35             : #include "libcli/composite/composite.h"
      36             : #include "../lib/util/tevent_ntstatus.h"
      37             : 
      38             : #undef DBGC_CLASS
      39             : #define DBGC_CLASS            DBGC_DRS_REPL
      40             : 
      41             : 
      42             : struct dreplsrv_op_notify_state {
      43             :         struct tevent_context *ev;
      44             :         struct dreplsrv_notify_operation *op;
      45             :         void *ndr_struct_ptr;
      46             : };
      47             : 
      48             : static void dreplsrv_op_notify_connect_done(struct tevent_req *subreq);
      49             : 
      50             : /*
      51             :   start the ReplicaSync async call
      52             :  */
      53        5611 : static struct tevent_req *dreplsrv_op_notify_send(TALLOC_CTX *mem_ctx,
      54             :                                                   struct tevent_context *ev,
      55             :                                                   struct dreplsrv_notify_operation *op)
      56             : {
      57           0 :         struct tevent_req *req;
      58           0 :         struct dreplsrv_op_notify_state *state;
      59           0 :         struct tevent_req *subreq;
      60             : 
      61        5611 :         req = tevent_req_create(mem_ctx, &state,
      62             :                                 struct dreplsrv_op_notify_state);
      63        5611 :         if (req == NULL) {
      64           0 :                 return NULL;
      65             :         }
      66        5611 :         state->ev = ev;
      67        5611 :         state->op = op;
      68             : 
      69        5611 :         subreq = dreplsrv_out_drsuapi_send(state,
      70             :                                            ev,
      71        5611 :                                            op->source_dsa->conn);
      72        5611 :         if (tevent_req_nomem(subreq, req)) {
      73           0 :                 return tevent_req_post(req, ev);
      74             :         }
      75        5611 :         tevent_req_set_callback(subreq, dreplsrv_op_notify_connect_done, req);
      76             : 
      77        5611 :         return req;
      78             : }
      79             : 
      80             : static void dreplsrv_op_notify_replica_sync_trigger(struct tevent_req *req);
      81             : 
      82        5610 : static void dreplsrv_op_notify_connect_done(struct tevent_req *subreq)
      83             : {
      84        5610 :         struct tevent_req *req = tevent_req_callback_data(subreq,
      85             :                                                           struct tevent_req);
      86           0 :         NTSTATUS status;
      87             : 
      88        5610 :         status = dreplsrv_out_drsuapi_recv(subreq);
      89        5610 :         TALLOC_FREE(subreq);
      90        5610 :         if (tevent_req_nterror(req, status)) {
      91        4474 :                 return;
      92             :         }
      93             : 
      94        1136 :         dreplsrv_op_notify_replica_sync_trigger(req);
      95             : }
      96             : 
      97             : static void dreplsrv_op_notify_replica_sync_done(struct tevent_req *subreq);
      98             : 
      99        1136 : static void dreplsrv_op_notify_replica_sync_trigger(struct tevent_req *req)
     100             : {
     101           0 :         struct dreplsrv_op_notify_state *state =
     102        1136 :                 tevent_req_data(req,
     103             :                 struct dreplsrv_op_notify_state);
     104        1136 :         struct dreplsrv_partition *partition = state->op->source_dsa->partition;
     105        1136 :         struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
     106           0 :         struct drsuapi_DsReplicaSync *r;
     107           0 :         struct tevent_req *subreq;
     108             : 
     109        1136 :         r = talloc_zero(state, struct drsuapi_DsReplicaSync);
     110        1136 :         if (tevent_req_nomem(r, req)) {
     111           0 :                 return;
     112             :         }
     113        1136 :         r->in.req = talloc_zero(r, union drsuapi_DsReplicaSyncRequest);
     114        1136 :         if (tevent_req_nomem(r, req)) {
     115           0 :                 return;
     116             :         }
     117        1136 :         r->in.bind_handle    = &drsuapi->bind_handle;
     118        1136 :         r->in.level = 1;
     119        1136 :         r->in.req->req1.naming_context = &partition->nc;
     120        1136 :         r->in.req->req1.source_dsa_guid = state->op->service->ntds_guid;
     121        1136 :         r->in.req->req1.options =
     122             :                 DRSUAPI_DRS_ASYNC_OP |
     123             :                 DRSUAPI_DRS_UPDATE_NOTIFICATION |
     124             :                 DRSUAPI_DRS_WRIT_REP;
     125             : 
     126        1136 :         if (state->op->is_urgent) {
     127         316 :                 r->in.req->req1.options |= DRSUAPI_DRS_SYNC_URGENT;
     128             :         }
     129             : 
     130        1136 :         state->ndr_struct_ptr = r;
     131             : 
     132        1136 :         if (DEBUGLVL(10)) {
     133           0 :                 NDR_PRINT_IN_DEBUG(drsuapi_DsReplicaSync, r);
     134             :         }
     135             : 
     136        1136 :         subreq = dcerpc_drsuapi_DsReplicaSync_r_send(state,
     137             :                                                      state->ev,
     138             :                                                      drsuapi->drsuapi_handle,
     139             :                                                      r);
     140        1136 :         if (tevent_req_nomem(subreq, req)) {
     141           0 :                 return;
     142             :         }
     143        1136 :         tevent_req_set_callback(subreq, dreplsrv_op_notify_replica_sync_done, req);
     144             : }
     145             : 
     146        1136 : static void dreplsrv_op_notify_replica_sync_done(struct tevent_req *subreq)
     147             : {
     148           0 :         struct tevent_req *req =
     149        1136 :                 tevent_req_callback_data(subreq,
     150             :                 struct tevent_req);
     151           0 :         struct dreplsrv_op_notify_state *state =
     152        1136 :                 tevent_req_data(req,
     153             :                 struct dreplsrv_op_notify_state);
     154        1136 :         struct drsuapi_DsReplicaSync *r = talloc_get_type(state->ndr_struct_ptr,
     155             :                                                           struct drsuapi_DsReplicaSync);
     156           0 :         NTSTATUS status;
     157             : 
     158        1136 :         state->ndr_struct_ptr = NULL;
     159             : 
     160        1136 :         status = dcerpc_drsuapi_DsReplicaSync_r_recv(subreq, r);
     161        1136 :         TALLOC_FREE(subreq);
     162        1136 :         if (tevent_req_nterror(req, status)) {
     163           0 :                 return;
     164             :         }
     165             : 
     166        1136 :         if (!W_ERROR_IS_OK(r->out.result)) {
     167           0 :                 status = werror_to_ntstatus(r->out.result);
     168           0 :                 tevent_req_nterror(req, status);
     169           0 :                 return;
     170             :         }
     171             : 
     172        1136 :         tevent_req_done(req);
     173             : }
     174             : 
     175        5610 : static NTSTATUS dreplsrv_op_notify_recv(struct tevent_req *req)
     176             : {
     177        5610 :         return tevent_req_simple_recv_ntstatus(req);
     178             : }
     179             : 
     180             : /*
     181             :   called when a notify operation has completed
     182             :  */
     183        5610 : static void dreplsrv_notify_op_callback(struct tevent_req *subreq)
     184             : {
     185           0 :         struct dreplsrv_notify_operation *op =
     186        5610 :                 tevent_req_callback_data(subreq,
     187             :                 struct dreplsrv_notify_operation);
     188           0 :         NTSTATUS status;
     189        5610 :         struct dreplsrv_service *s = op->service;
     190           0 :         WERROR werr;
     191             : 
     192        5610 :         status = dreplsrv_op_notify_recv(subreq);
     193        5610 :         werr = ntstatus_to_werror(status);
     194        5610 :         TALLOC_FREE(subreq);
     195        5610 :         if (!NT_STATUS_IS_OK(status)) {
     196        4474 :                 DBG_INFO("dreplsrv_notify: Failed to send DsReplicaSync to %s for %s - %s : %s\n",
     197             :                          op->source_dsa->repsFrom1->other_info->dns_name,
     198             :                          ldb_dn_get_linearized(op->source_dsa->partition->dn),
     199             :                          nt_errstr(status), win_errstr(werr));
     200             :         } else {
     201        1136 :                 DBG_INFO("dreplsrv_notify: DsReplicaSync successfully sent to %s\n",
     202             :                          op->source_dsa->repsFrom1->other_info->dns_name);
     203        1136 :                 op->source_dsa->notify_uSN = op->uSN;
     204             :         }
     205             : 
     206        5610 :         drepl_reps_update(s, "repsTo", op->source_dsa->partition->dn,
     207        5610 :                           &op->source_dsa->repsFrom1->source_dsa_obj_guid,
     208             :                           werr);
     209             : 
     210        5610 :         talloc_free(op);
     211        5610 :         s->ops.n_current = NULL;
     212        5610 :         dreplsrv_run_pending_ops(s);
     213        5610 : }
     214             : 
     215             : /*
     216             :   run any pending replica sync calls
     217             :  */
     218        5660 : void dreplsrv_notify_run_ops(struct dreplsrv_service *s)
     219             : {
     220           0 :         struct dreplsrv_notify_operation *op;
     221           0 :         struct tevent_req *subreq;
     222             : 
     223        5660 :         if (s->ops.n_current || s->ops.current) {
     224             :                 /* if there's still one running, we're done */
     225          49 :                 return;
     226             :         }
     227             : 
     228        5611 :         if (!s->ops.notifies) {
     229             :                 /* if there're no pending operations, we're done */
     230           0 :                 return;
     231             :         }
     232             : 
     233        5611 :         op = s->ops.notifies;
     234        5611 :         s->ops.n_current = op;
     235        5611 :         DLIST_REMOVE(s->ops.notifies, op);
     236             : 
     237        5611 :         subreq = dreplsrv_op_notify_send(op, s->task->event_ctx, op);
     238        5611 :         if (!subreq) {
     239           0 :                 DBG_ERR("dreplsrv_notify_run_ops: dreplsrv_op_notify_send[%s][%s] - no memory\n",
     240             :                         op->source_dsa->repsFrom1->other_info->dns_name,
     241             :                         ldb_dn_get_linearized(op->source_dsa->partition->dn));
     242           0 :                 return;
     243             :         }
     244        5611 :         tevent_req_set_callback(subreq, dreplsrv_notify_op_callback, op);
     245        5611 :         DBG_INFO("started DsReplicaSync for %s to %s\n",
     246             :                  ldb_dn_get_linearized(op->source_dsa->partition->dn),
     247             :                  op->source_dsa->repsFrom1->other_info->dns_name);
     248             : }
     249             : 
     250             : 
     251             : /*
     252             :   find a source_dsa for a given guid
     253             :  */
     254       17548 : static struct dreplsrv_partition_source_dsa *dreplsrv_find_notify_dsa(struct dreplsrv_partition *p,
     255             :                                                                       struct GUID *guid)
     256             : {
     257           0 :         struct dreplsrv_partition_source_dsa *s;
     258             : 
     259             :         /* first check the sources list */
     260       23497 :         for (s=p->sources; s; s=s->next) {
     261       12845 :                 if (GUID_equal(&s->repsFrom1->source_dsa_obj_guid, guid)) {
     262        6896 :                         return s;
     263             :                 }
     264             :         }
     265             : 
     266             :         /* then the notifies list */
     267       21471 :         for (s=p->notifies; s; s=s->next) {
     268       21471 :                 if (GUID_equal(&s->repsFrom1->source_dsa_obj_guid, guid)) {
     269       10652 :                         return s;
     270             :                 }
     271             :         }
     272           0 :         return NULL;
     273             : }
     274             : 
     275             : 
     276             : /*
     277             :   schedule a replicaSync message
     278             :  */
     279        5743 : static WERROR dreplsrv_schedule_notify_sync(struct dreplsrv_service *service,
     280             :                                             struct dreplsrv_partition *p,
     281             :                                             struct repsFromToBlob *reps,
     282             :                                             TALLOC_CTX *mem_ctx,
     283             :                                             uint64_t uSN,
     284             :                                             bool is_urgent,
     285             :                                             uint32_t replica_flags)
     286             : {
     287           0 :         struct dreplsrv_notify_operation *op;
     288           0 :         struct dreplsrv_partition_source_dsa *s;
     289             : 
     290        5743 :         s = dreplsrv_find_notify_dsa(p, &reps->ctr.ctr1.source_dsa_obj_guid);
     291        5743 :         if (s == NULL) {
     292           0 :                 DBG_ERR("Unable to find source_dsa for %s\n",
     293             :                         GUID_string(mem_ctx, &reps->ctr.ctr1.source_dsa_obj_guid));
     294           0 :                 return WERR_DS_UNAVAILABLE;
     295             :         }
     296             : 
     297             :         /* first try to find an existing notify operation */
     298       34865 :         for (op = service->ops.notifies; op; op = op->next) {
     299       29250 :                 if (op->source_dsa != s) {
     300       29122 :                         continue;
     301             :                 }
     302             : 
     303         128 :                 if (op->is_urgent != is_urgent) {
     304           0 :                         continue;
     305             :                 }
     306             : 
     307         128 :                 if (op->replica_flags != replica_flags) {
     308           0 :                         continue;
     309             :                 }
     310             : 
     311         128 :                 if (op->uSN < uSN) {
     312          36 :                         op->uSN = uSN;
     313             :                 }
     314             : 
     315             :                 /* reuse the notify operation, as it's not yet started */
     316         128 :                 return WERR_OK;
     317             :         }
     318             : 
     319        5615 :         op = talloc_zero(mem_ctx, struct dreplsrv_notify_operation);
     320        5615 :         W_ERROR_HAVE_NO_MEMORY(op);
     321             : 
     322        5615 :         op->service    = service;
     323        5615 :         op->source_dsa         = s;
     324        5615 :         op->uSN           = uSN;
     325        5615 :         op->is_urgent          = is_urgent;
     326        5615 :         op->replica_flags = replica_flags;
     327        5615 :         op->schedule_time = time(NULL);
     328             : 
     329        5615 :         DLIST_ADD_END(service->ops.notifies, op);
     330        5615 :         talloc_steal(service, op);
     331        5615 :         return WERR_OK;
     332             : }
     333             : 
     334             : /*
     335             :   see if a partition has a hugher uSN than what is in the repsTo and
     336             :   if so then send a DsReplicaSync
     337             :  */
     338       49911 : static WERROR dreplsrv_notify_check(struct dreplsrv_service *s, 
     339             :                                     struct dreplsrv_partition *p,
     340             :                                     TALLOC_CTX *mem_ctx)
     341             : {
     342       49911 :         uint32_t count=0;
     343         440 :         struct repsFromToBlob *reps;
     344         440 :         WERROR werr;
     345         440 :         uint64_t uSNHighest;
     346         440 :         uint64_t uSNUrgent;
     347         440 :         uint32_t i;
     348         440 :         int ret;
     349             : 
     350       49911 :         werr = dsdb_loadreps(s->samdb, mem_ctx, p->dn, "repsTo", &reps, &count);
     351       49911 :         if (!W_ERROR_IS_OK(werr)) {
     352           0 :                 DBG_ERR("Failed to load repsTo for %s\n",
     353             :                          ldb_dn_get_linearized(p->dn));
     354           0 :                 return werr;
     355             :         }
     356             : 
     357             :         /* loads the partition uSNHighest and uSNUrgent */
     358       49911 :         ret = dsdb_load_partition_usn(s->samdb, p->dn, &uSNHighest, &uSNUrgent);
     359       49911 :         if (ret != LDB_SUCCESS || uSNHighest == 0) {
     360             :                 /* nothing to do */
     361           0 :                 return WERR_OK;
     362             :         }
     363             : 
     364             :         /* see if any of our partners need some of our objects */
     365       61716 :         for (i=0; i<count; i++) {
     366           0 :                 struct dreplsrv_partition_source_dsa *sdsa;
     367           0 :                 uint32_t replica_flags;
     368       11805 :                 sdsa = dreplsrv_find_notify_dsa(p, &reps[i].ctr.ctr1.source_dsa_obj_guid);
     369       11805 :                 replica_flags = reps[i].ctr.ctr1.replica_flags;
     370       11805 :                 if (sdsa == NULL) continue;
     371       11805 :                 if (sdsa->notify_uSN < uSNHighest) {
     372             :                         /* we need to tell this partner to replicate
     373             :                            with us */
     374        5743 :                         bool is_urgent = sdsa->notify_uSN < uSNUrgent;
     375             : 
     376             :                         /* check if urgent replication is needed */
     377        5743 :                         werr = dreplsrv_schedule_notify_sync(s, p, &reps[i], mem_ctx,
     378             :                                                              uSNHighest, is_urgent, replica_flags);
     379        5743 :                         if (!W_ERROR_IS_OK(werr)) {
     380           0 :                                 DBG_ERR("Failed to setup notify to %s for %s\n",
     381             :                                          reps[i].ctr.ctr1.other_info->dns_name,
     382             :                                          ldb_dn_get_linearized(p->dn));
     383           0 :                                 return werr;
     384             :                         }
     385        5743 :                         DBG_DEBUG("queued DsReplicaSync for %s to %s "
     386             :                                   "(urgent=%s) uSN=%llu:%llu\n",
     387             :                                   ldb_dn_get_linearized(p->dn),
     388             :                                   reps[i].ctr.ctr1.other_info->dns_name,
     389             :                                   is_urgent?"true":"false",
     390             :                                   (unsigned long long)sdsa->notify_uSN,
     391             :                                   (unsigned long long)uSNHighest);
     392             :                 }
     393             :         }
     394             : 
     395       49911 :         return WERR_OK;
     396             : }
     397             : 
     398             : /*
     399             :   see if any of the partitions have changed, and if so then send a
     400             :   DsReplicaSync to all the replica partners in the repsTo object
     401             :  */
     402       10247 : static WERROR dreplsrv_notify_check_all(struct dreplsrv_service *s, TALLOC_CTX *mem_ctx)
     403             : {
     404          88 :         WERROR status;
     405          88 :         struct dreplsrv_partition *p;
     406             : 
     407       60158 :         for (p = s->partitions; p; p = p->next) {
     408       49911 :                 status = dreplsrv_notify_check(s, p, mem_ctx);
     409       49911 :                 W_ERROR_NOT_OK_RETURN(status);
     410             :         }
     411             : 
     412       10247 :         return WERR_OK;
     413             : }
     414             : 
     415             : static void dreplsrv_notify_run(struct dreplsrv_service *service);
     416             : 
     417       10247 : static void dreplsrv_notify_handler_te(struct tevent_context *ev, struct tevent_timer *te,
     418             :                                        struct timeval t, void *ptr)
     419             : {
     420       10247 :         struct dreplsrv_service *service = talloc_get_type(ptr, struct dreplsrv_service);
     421          88 :         WERROR status;
     422             : 
     423       10247 :         service->notify.te = NULL;
     424             : 
     425       10247 :         dreplsrv_notify_run(service);
     426             : 
     427       10247 :         status = dreplsrv_notify_schedule(service, service->notify.interval);
     428       10247 :         if (!W_ERROR_IS_OK(status)) {
     429           0 :                 task_server_terminate(service->task, win_errstr(status), false);
     430           0 :                 return;
     431             :         }
     432             : }
     433             : 
     434       10305 : WERROR dreplsrv_notify_schedule(struct dreplsrv_service *service, uint32_t next_interval)
     435             : {
     436          90 :         TALLOC_CTX *tmp_mem;
     437          90 :         struct tevent_timer *new_te;
     438          90 :         struct timeval next_time;
     439             : 
     440             :         /* prevent looping */
     441       10305 :         if (next_interval == 0) next_interval = 1;
     442             : 
     443       10305 :         next_time = timeval_current_ofs(next_interval, 50);
     444             : 
     445       10305 :         if (service->notify.te) {
     446             :                 /*
     447             :                  * if the timestamp of the new event is higher,
     448             :                  * as current next we don't need to reschedule
     449             :                  */
     450           0 :                 if (timeval_compare(&next_time, &service->notify.next_event) > 0) {
     451           0 :                         return WERR_OK;
     452             :                 }
     453             :         }
     454             : 
     455             :         /* reset the next scheduled timestamp */
     456       10305 :         service->notify.next_event = next_time;
     457             : 
     458       10305 :         new_te = tevent_add_timer(service->task->event_ctx, service,
     459             :                                  service->notify.next_event,
     460             :                                  dreplsrv_notify_handler_te, service);
     461       10305 :         W_ERROR_HAVE_NO_MEMORY(new_te);
     462             : 
     463       10305 :         tmp_mem = talloc_new(service);
     464       10305 :         DBG_DEBUG("dreplsrv_notify_schedule(%u) %sscheduled for: %s\n",
     465             :                   next_interval,
     466             :                   (service->notify.te?"re":""),
     467             :                   nt_time_string(tmp_mem, timeval_to_nttime(&next_time)));
     468       10305 :         talloc_free(tmp_mem);
     469             : 
     470       10305 :         talloc_free(service->notify.te);
     471       10305 :         service->notify.te = new_te;
     472             : 
     473       10305 :         return WERR_OK;
     474             : }
     475             : 
     476       10247 : static void dreplsrv_notify_run(struct dreplsrv_service *service)
     477             : {
     478          88 :         TALLOC_CTX *mem_ctx;
     479             : 
     480       10247 :         mem_ctx = talloc_new(service);
     481       10247 :         dreplsrv_notify_check_all(service, mem_ctx);
     482       10247 :         talloc_free(mem_ctx);
     483             : 
     484       10247 :         dreplsrv_run_pending_ops(service);
     485       10247 : }

Generated by: LCOV version 1.14