debug: Add new debug class "drs_repl" for DRS replication processing
[Samba.git] / source4 / dsdb / repl / drepl_out_helpers.c
blob80762f03130bef52c813e552cc5cf2976866b951
1 /*
2 Unix SMB/CIFS mplementation.
3 DSDB replication service helper function for outgoing traffic
5 Copyright (C) Stefan Metzmacher 2007
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "dsdb/samdb/samdb.h"
24 #include "auth/auth.h"
25 #include "smbd/service.h"
26 #include "lib/events/events.h"
27 #include "dsdb/repl/drepl_service.h"
28 #include <ldb_errors.h>
29 #include "../lib/util/dlinklist.h"
30 #include "librpc/gen_ndr/ndr_misc.h"
31 #include "librpc/gen_ndr/ndr_drsuapi.h"
32 #include "librpc/gen_ndr/ndr_drsblobs.h"
33 #include "libcli/composite/composite.h"
34 #include "auth/gensec/gensec.h"
35 #include "param/param.h"
36 #include "../lib/util/tevent_ntstatus.h"
37 #include "libcli/security/security.h"
39 #undef DBGC_CLASS
40 #define DBGC_CLASS DBGC_DRS_REPL
42 struct dreplsrv_out_drsuapi_state {
43 struct tevent_context *ev;
45 struct dreplsrv_out_connection *conn;
47 struct dreplsrv_drsuapi_connection *drsuapi;
49 struct drsuapi_DsBindInfoCtr bind_info_ctr;
50 struct drsuapi_DsBind bind_r;
53 static void dreplsrv_out_drsuapi_connect_done(struct composite_context *creq);
55 struct tevent_req *dreplsrv_out_drsuapi_send(TALLOC_CTX *mem_ctx,
56 struct tevent_context *ev,
57 struct dreplsrv_out_connection *conn)
59 struct tevent_req *req;
60 struct dreplsrv_out_drsuapi_state *state;
61 struct composite_context *creq;
63 req = tevent_req_create(mem_ctx, &state,
64 struct dreplsrv_out_drsuapi_state);
65 if (req == NULL) {
66 return NULL;
69 state->ev = ev;
70 state->conn = conn;
71 state->drsuapi = conn->drsuapi;
73 if (state->drsuapi != NULL) {
74 struct dcerpc_binding_handle *b =
75 state->drsuapi->pipe->binding_handle;
76 bool is_connected = dcerpc_binding_handle_is_connected(b);
78 if (is_connected) {
79 tevent_req_done(req);
80 return tevent_req_post(req, ev);
83 TALLOC_FREE(conn->drsuapi);
86 state->drsuapi = talloc_zero(state, struct dreplsrv_drsuapi_connection);
87 if (tevent_req_nomem(state->drsuapi, req)) {
88 return tevent_req_post(req, ev);
91 creq = dcerpc_pipe_connect_b_send(state, conn->binding, &ndr_table_drsuapi,
92 conn->service->system_session_info->credentials,
93 ev, conn->service->task->lp_ctx);
94 if (tevent_req_nomem(creq, req)) {
95 return tevent_req_post(req, ev);
97 composite_continue(NULL, creq, dreplsrv_out_drsuapi_connect_done, req);
99 return req;
102 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req *subreq);
104 static void dreplsrv_out_drsuapi_connect_done(struct composite_context *creq)
106 struct tevent_req *req = talloc_get_type(creq->async.private_data,
107 struct tevent_req);
108 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
109 struct dreplsrv_out_drsuapi_state);
110 NTSTATUS status;
111 struct tevent_req *subreq;
113 status = dcerpc_pipe_connect_b_recv(creq,
114 state->drsuapi,
115 &state->drsuapi->pipe);
116 if (tevent_req_nterror(req, status)) {
117 return;
120 state->drsuapi->drsuapi_handle = state->drsuapi->pipe->binding_handle;
122 status = gensec_session_key(state->drsuapi->pipe->conn->security_state.generic_state,
123 state->drsuapi,
124 &state->drsuapi->gensec_skey);
125 if (tevent_req_nterror(req, status)) {
126 return;
129 state->bind_info_ctr.length = 28;
130 state->bind_info_ctr.info.info28 = state->conn->service->bind_info28;
132 state->bind_r.in.bind_guid = &state->conn->service->ntds_guid;
133 state->bind_r.in.bind_info = &state->bind_info_ctr;
134 state->bind_r.out.bind_handle = &state->drsuapi->bind_handle;
136 subreq = dcerpc_drsuapi_DsBind_r_send(state,
137 state->ev,
138 state->drsuapi->drsuapi_handle,
139 &state->bind_r);
140 if (tevent_req_nomem(subreq, req)) {
141 return;
143 tevent_req_set_callback(subreq, dreplsrv_out_drsuapi_bind_done, req);
146 static void dreplsrv_out_drsuapi_bind_done(struct tevent_req *subreq)
148 struct tevent_req *req = tevent_req_callback_data(subreq,
149 struct tevent_req);
150 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
151 struct dreplsrv_out_drsuapi_state);
152 NTSTATUS status;
154 status = dcerpc_drsuapi_DsBind_r_recv(subreq, state);
155 TALLOC_FREE(subreq);
156 if (tevent_req_nterror(req, status)) {
157 return;
160 if (!W_ERROR_IS_OK(state->bind_r.out.result)) {
161 status = werror_to_ntstatus(state->bind_r.out.result);
162 tevent_req_nterror(req, status);
163 return;
166 ZERO_STRUCT(state->drsuapi->remote_info28);
167 if (state->bind_r.out.bind_info) {
168 struct drsuapi_DsBindInfo28 *info28;
169 info28 = &state->drsuapi->remote_info28;
171 switch (state->bind_r.out.bind_info->length) {
172 case 24: {
173 struct drsuapi_DsBindInfo24 *info24;
174 info24 = &state->bind_r.out.bind_info->info.info24;
176 info28->supported_extensions = info24->supported_extensions;
177 info28->site_guid = info24->site_guid;
178 info28->pid = info24->pid;
179 info28->repl_epoch = 0;
180 break;
182 case 28: {
183 *info28 = state->bind_r.out.bind_info->info.info28;
184 break;
186 case 32: {
187 struct drsuapi_DsBindInfo32 *info32;
188 info32 = &state->bind_r.out.bind_info->info.info32;
190 info28->supported_extensions = info32->supported_extensions;
191 info28->site_guid = info32->site_guid;
192 info28->pid = info32->pid;
193 info28->repl_epoch = info32->repl_epoch;
194 break;
196 case 48: {
197 struct drsuapi_DsBindInfo48 *info48;
198 info48 = &state->bind_r.out.bind_info->info.info48;
200 info28->supported_extensions = info48->supported_extensions;
201 info28->site_guid = info48->site_guid;
202 info28->pid = info48->pid;
203 info28->repl_epoch = info48->repl_epoch;
204 break;
206 case 52: {
207 struct drsuapi_DsBindInfo52 *info52;
208 info52 = &state->bind_r.out.bind_info->info.info52;
210 info28->supported_extensions = info52->supported_extensions;
211 info28->site_guid = info52->site_guid;
212 info28->pid = info52->pid;
213 info28->repl_epoch = info52->repl_epoch;
214 break;
216 default:
217 DEBUG(1, ("Warning: invalid info length in bind info: %d\n",
218 state->bind_r.out.bind_info->length));
219 break;
223 tevent_req_done(req);
226 NTSTATUS dreplsrv_out_drsuapi_recv(struct tevent_req *req)
228 struct dreplsrv_out_drsuapi_state *state = tevent_req_data(req,
229 struct dreplsrv_out_drsuapi_state);
230 NTSTATUS status;
232 if (tevent_req_is_nterror(req, &status)) {
233 tevent_req_received(req);
234 return status;
237 state->conn->drsuapi = talloc_move(state->conn, &state->drsuapi);
239 tevent_req_received(req);
240 return NT_STATUS_OK;
243 struct dreplsrv_op_pull_source_state {
244 struct tevent_context *ev;
245 struct dreplsrv_out_operation *op;
246 void *ndr_struct_ptr;
248 * Used when we have to re-try with a different NC, eg for
249 * EXOP retry or to get a current schema first
251 struct dreplsrv_partition_source_dsa *source_dsa_retry;
252 enum drsuapi_DsExtendedOperation extended_op_retry;
253 bool retry_started;
256 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq);
258 struct tevent_req *dreplsrv_op_pull_source_send(TALLOC_CTX *mem_ctx,
259 struct tevent_context *ev,
260 struct dreplsrv_out_operation *op)
262 struct tevent_req *req;
263 struct dreplsrv_op_pull_source_state *state;
264 struct tevent_req *subreq;
266 req = tevent_req_create(mem_ctx, &state,
267 struct dreplsrv_op_pull_source_state);
268 if (req == NULL) {
269 return NULL;
271 state->ev = ev;
272 state->op = op;
274 subreq = dreplsrv_out_drsuapi_send(state, ev, op->source_dsa->conn);
275 if (tevent_req_nomem(subreq, req)) {
276 return tevent_req_post(req, ev);
278 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_connect_done, req);
280 return req;
283 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req);
285 static void dreplsrv_op_pull_source_connect_done(struct tevent_req *subreq)
287 struct tevent_req *req = tevent_req_callback_data(subreq,
288 struct tevent_req);
289 NTSTATUS status;
291 status = dreplsrv_out_drsuapi_recv(subreq);
292 TALLOC_FREE(subreq);
293 if (tevent_req_nterror(req, status)) {
294 return;
297 dreplsrv_op_pull_source_get_changes_trigger(req);
300 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq);
303 get a RODC partial attribute set for a replication call
305 static NTSTATUS dreplsrv_get_rodc_partial_attribute_set(struct dreplsrv_service *service,
306 TALLOC_CTX *mem_ctx,
307 struct drsuapi_DsPartialAttributeSet **_pas,
308 struct drsuapi_DsReplicaOIDMapping_Ctr **pfm,
309 bool for_schema)
311 struct drsuapi_DsPartialAttributeSet *pas;
312 struct dsdb_schema *schema;
313 uint32_t i;
315 pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
316 NT_STATUS_HAVE_NO_MEMORY(pas);
318 schema = dsdb_get_schema(service->samdb, NULL);
320 pas->version = 1;
321 pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
322 if (pas->attids == NULL) {
323 TALLOC_FREE(pas);
324 return NT_STATUS_NO_MEMORY;
327 for (i=0; i<schema->num_attributes; i++) {
328 struct dsdb_attribute *a;
329 a = schema->attributes_by_attributeID_id[i];
330 if (a->systemFlags & (DS_FLAG_ATTR_NOT_REPLICATED | DS_FLAG_ATTR_IS_CONSTRUCTED)) {
331 continue;
333 if (a->searchFlags & SEARCH_FLAG_RODC_ATTRIBUTE) {
334 continue;
336 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, for_schema);
337 pas->num_attids++;
340 pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
341 if (pas->attids == NULL) {
342 TALLOC_FREE(pas);
343 return NT_STATUS_NO_MEMORY;
346 *_pas = pas;
348 if (pfm != NULL) {
349 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, pfm);
352 return NT_STATUS_OK;
357 get a GC partial attribute set for a replication call
359 static NTSTATUS dreplsrv_get_gc_partial_attribute_set(struct dreplsrv_service *service,
360 TALLOC_CTX *mem_ctx,
361 struct drsuapi_DsPartialAttributeSet **_pas,
362 struct drsuapi_DsReplicaOIDMapping_Ctr **pfm)
364 struct drsuapi_DsPartialAttributeSet *pas;
365 struct dsdb_schema *schema;
366 uint32_t i;
368 pas = talloc_zero(mem_ctx, struct drsuapi_DsPartialAttributeSet);
369 NT_STATUS_HAVE_NO_MEMORY(pas);
371 schema = dsdb_get_schema(service->samdb, NULL);
373 pas->version = 1;
374 pas->attids = talloc_array(pas, enum drsuapi_DsAttributeId, schema->num_attributes);
375 if (pas->attids == NULL) {
376 TALLOC_FREE(pas);
377 return NT_STATUS_NO_MEMORY;
380 for (i=0; i<schema->num_attributes; i++) {
381 struct dsdb_attribute *a;
382 a = schema->attributes_by_attributeID_id[i];
383 if (a->isMemberOfPartialAttributeSet) {
384 pas->attids[pas->num_attids] = dsdb_attribute_get_attid(a, false);
385 pas->num_attids++;
389 pas->attids = talloc_realloc(pas, pas->attids, enum drsuapi_DsAttributeId, pas->num_attids);
390 if (pas->attids == NULL) {
391 TALLOC_FREE(pas);
392 return NT_STATUS_NO_MEMORY;
395 *_pas = pas;
397 if (pfm != NULL) {
398 dsdb_get_oid_mappings_drsuapi(schema, true, mem_ctx, pfm);
401 return NT_STATUS_OK;
405 convert from one udv format to the other
407 static WERROR udv_convert(TALLOC_CTX *mem_ctx,
408 const struct replUpToDateVectorCtr2 *udv,
409 struct drsuapi_DsReplicaCursorCtrEx *udv_ex)
411 uint32_t i;
413 udv_ex->version = 2;
414 udv_ex->reserved1 = 0;
415 udv_ex->reserved2 = 0;
416 udv_ex->count = udv->count;
417 udv_ex->cursors = talloc_array(mem_ctx, struct drsuapi_DsReplicaCursor, udv->count);
418 W_ERROR_HAVE_NO_MEMORY(udv_ex->cursors);
420 for (i=0; i<udv->count; i++) {
421 udv_ex->cursors[i].source_dsa_invocation_id = udv->cursors[i].source_dsa_invocation_id;
422 udv_ex->cursors[i].highest_usn = udv->cursors[i].highest_usn;
425 return WERR_OK;
429 static void dreplsrv_op_pull_source_get_changes_trigger(struct tevent_req *req)
431 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
432 struct dreplsrv_op_pull_source_state);
433 struct repsFromTo1 *rf1 = state->op->source_dsa->repsFrom1;
434 struct dreplsrv_service *service = state->op->service;
435 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
436 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
437 struct drsuapi_DsGetNCChanges *r;
438 struct drsuapi_DsReplicaCursorCtrEx *uptodateness_vector;
439 struct tevent_req *subreq;
440 struct drsuapi_DsPartialAttributeSet *pas = NULL;
441 NTSTATUS status;
442 uint32_t replica_flags;
443 struct drsuapi_DsReplicaHighWaterMark highwatermark;
444 struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
445 struct drsuapi_DsReplicaOIDMapping_Ctr *mappings = NULL;
447 r = talloc(state, struct drsuapi_DsGetNCChanges);
448 if (tevent_req_nomem(r, req)) {
449 return;
452 r->out.level_out = talloc(r, uint32_t);
453 if (tevent_req_nomem(r->out.level_out, req)) {
454 return;
456 r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
457 if (tevent_req_nomem(r->in.req, req)) {
458 return;
460 r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
461 if (tevent_req_nomem(r->out.ctr, req)) {
462 return;
465 if (partition->uptodatevector.count != 0 &&
466 partition->uptodatevector_ex.count == 0) {
467 WERROR werr;
468 werr = udv_convert(partition, &partition->uptodatevector, &partition->uptodatevector_ex);
469 if (!W_ERROR_IS_OK(werr)) {
470 DEBUG(0,(__location__ ": Failed to convert UDV for %s : %s\n",
471 ldb_dn_get_linearized(partition->dn), win_errstr(werr)));
472 tevent_req_nterror(req, werror_to_ntstatus(werr));
473 return;
477 if (partition->uptodatevector_ex.count == 0) {
478 uptodateness_vector = NULL;
479 } else {
480 uptodateness_vector = &partition->uptodatevector_ex;
483 replica_flags = rf1->replica_flags;
484 highwatermark = rf1->highwatermark;
486 if (state->op->options & DRSUAPI_DRS_GET_ANC) {
487 replica_flags |= DRSUAPI_DRS_GET_ANC;
490 if (state->op->options & DRSUAPI_DRS_SYNC_FORCED) {
491 replica_flags |= DRSUAPI_DRS_SYNC_FORCED;
494 if (partition->partial_replica) {
495 status = dreplsrv_get_gc_partial_attribute_set(service, r,
496 &pas,
497 &mappings);
498 if (!NT_STATUS_IS_OK(status)) {
499 DEBUG(0,(__location__ ": Failed to construct GC partial attribute set : %s\n", nt_errstr(status)));
500 tevent_req_nterror(req, status);
501 return;
503 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
504 } else if (partition->rodc_replica || state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
505 bool for_schema = false;
506 if (ldb_dn_compare_base(schema_dn, partition->dn) == 0) {
507 for_schema = true;
509 status = dreplsrv_get_rodc_partial_attribute_set(service, r,
510 &pas,
511 &mappings,
512 for_schema);
513 if (!NT_STATUS_IS_OK(status)) {
514 DEBUG(0,(__location__ ": Failed to construct RODC partial attribute set : %s\n", nt_errstr(status)));
515 tevent_req_nterror(req, status);
516 return;
518 replica_flags &= ~DRSUAPI_DRS_WRIT_REP;
519 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
520 replica_flags &= ~DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
521 } else {
522 replica_flags |= DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING;
526 * As per MS-DRSR:
528 * 4.1.10.4
529 * Client Behavior When Sending the IDL_DRSGetNCChanges Request
531 * 4.1.10.4.1
532 * ReplicateNCRequestMsg
534 replica_flags |= DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP;
535 } else {
536 replica_flags |= DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP;
539 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
541 * If it's an exop never set the ADD_REF even if it's in
542 * repsFrom flags.
544 replica_flags &= ~DRSUAPI_DRS_ADD_REF;
547 /* is this a full resync of all objects? */
548 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_NOW) {
549 ZERO_STRUCT(highwatermark);
550 /* clear the FULL_SYNC_NOW option for subsequent
551 stages of the replication cycle */
552 state->op->options &= ~DRSUAPI_DRS_FULL_SYNC_NOW;
553 state->op->options |= DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS;
554 replica_flags |= DRSUAPI_DRS_NEVER_SYNCED;
556 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
557 uptodateness_vector = NULL;
560 r->in.bind_handle = &drsuapi->bind_handle;
561 if (drsuapi->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
562 r->in.level = 8;
563 r->in.req->req8.destination_dsa_guid = service->ntds_guid;
564 r->in.req->req8.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
565 r->in.req->req8.naming_context = &partition->nc;
566 r->in.req->req8.highwatermark = highwatermark;
567 r->in.req->req8.uptodateness_vector = uptodateness_vector;
568 r->in.req->req8.replica_flags = replica_flags;
569 r->in.req->req8.max_object_count = 133;
570 r->in.req->req8.max_ndr_size = 1336811;
571 r->in.req->req8.extended_op = state->op->extended_op;
572 r->in.req->req8.fsmo_info = state->op->fsmo_info;
573 r->in.req->req8.partial_attribute_set = pas;
574 r->in.req->req8.partial_attribute_set_ex= NULL;
575 r->in.req->req8.mapping_ctr.num_mappings= mappings == NULL ? 0 : mappings->num_mappings;
576 r->in.req->req8.mapping_ctr.mappings = mappings == NULL ? NULL : mappings->mappings;
577 } else {
578 r->in.level = 5;
579 r->in.req->req5.destination_dsa_guid = service->ntds_guid;
580 r->in.req->req5.source_dsa_invocation_id= rf1->source_dsa_invocation_id;
581 r->in.req->req5.naming_context = &partition->nc;
582 r->in.req->req5.highwatermark = highwatermark;
583 r->in.req->req5.uptodateness_vector = uptodateness_vector;
584 r->in.req->req5.replica_flags = replica_flags;
585 r->in.req->req5.max_object_count = 133;
586 r->in.req->req5.max_ndr_size = 1336770;
587 r->in.req->req5.extended_op = state->op->extended_op;
588 r->in.req->req5.fsmo_info = state->op->fsmo_info;
591 #if 0
592 NDR_PRINT_IN_DEBUG(drsuapi_DsGetNCChanges, r);
593 #endif
595 state->ndr_struct_ptr = r;
596 subreq = dcerpc_drsuapi_DsGetNCChanges_r_send(state,
597 state->ev,
598 drsuapi->drsuapi_handle,
600 if (tevent_req_nomem(subreq, req)) {
601 return;
603 tevent_req_set_callback(subreq, dreplsrv_op_pull_source_get_changes_done, req);
606 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
607 struct drsuapi_DsGetNCChanges *r,
608 uint32_t ctr_level,
609 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
610 struct drsuapi_DsGetNCChangesCtr6 *ctr6);
612 static void dreplsrv_op_pull_source_get_changes_done(struct tevent_req *subreq)
614 struct tevent_req *req = tevent_req_callback_data(subreq,
615 struct tevent_req);
616 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
617 struct dreplsrv_op_pull_source_state);
618 NTSTATUS status;
619 struct drsuapi_DsGetNCChanges *r = talloc_get_type(state->ndr_struct_ptr,
620 struct drsuapi_DsGetNCChanges);
621 uint32_t ctr_level = 0;
622 struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
623 struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
624 enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE;
625 state->ndr_struct_ptr = NULL;
627 status = dcerpc_drsuapi_DsGetNCChanges_r_recv(subreq, r);
628 TALLOC_FREE(subreq);
629 if (tevent_req_nterror(req, status)) {
630 return;
633 if (!W_ERROR_IS_OK(r->out.result)) {
634 status = werror_to_ntstatus(r->out.result);
635 tevent_req_nterror(req, status);
636 return;
639 if (*r->out.level_out == 1) {
640 ctr_level = 1;
641 ctr1 = &r->out.ctr->ctr1;
642 } else if (*r->out.level_out == 2 &&
643 r->out.ctr->ctr2.mszip1.ts) {
644 ctr_level = 1;
645 ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
646 } else if (*r->out.level_out == 6) {
647 ctr_level = 6;
648 ctr6 = &r->out.ctr->ctr6;
649 } else if (*r->out.level_out == 7 &&
650 r->out.ctr->ctr7.level == 6 &&
651 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
652 r->out.ctr->ctr7.ctr.mszip6.ts) {
653 ctr_level = 6;
654 ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
655 } else if (*r->out.level_out == 7 &&
656 r->out.ctr->ctr7.level == 6 &&
657 r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
658 r->out.ctr->ctr7.ctr.xpress6.ts) {
659 ctr_level = 6;
660 ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
661 } else {
662 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
663 tevent_req_nterror(req, status);
664 return;
667 if (!ctr1 && !ctr6) {
668 status = werror_to_ntstatus(WERR_BAD_NET_RESP);
669 tevent_req_nterror(req, status);
670 return;
673 if (ctr_level == 6) {
674 if (!W_ERROR_IS_OK(ctr6->drs_error)) {
675 status = werror_to_ntstatus(ctr6->drs_error);
676 tevent_req_nterror(req, status);
677 return;
679 extended_ret = ctr6->extended_ret;
682 if (ctr_level == 1) {
683 extended_ret = ctr1->extended_ret;
686 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
687 state->op->extended_ret = extended_ret;
689 if (extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) {
690 status = NT_STATUS_UNSUCCESSFUL;
691 tevent_req_nterror(req, status);
692 return;
696 dreplsrv_op_pull_source_apply_changes_trigger(req, r, ctr_level, ctr1, ctr6);
699 static void dreplsrv_update_refs_trigger(struct tevent_req *req);
701 static void dreplsrv_op_pull_source_apply_changes_trigger(struct tevent_req *req,
702 struct drsuapi_DsGetNCChanges *r,
703 uint32_t ctr_level,
704 struct drsuapi_DsGetNCChangesCtr1 *ctr1,
705 struct drsuapi_DsGetNCChangesCtr6 *ctr6)
707 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
708 struct dreplsrv_op_pull_source_state);
709 struct repsFromTo1 rf1 = *state->op->source_dsa->repsFrom1;
710 struct dreplsrv_service *service = state->op->service;
711 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
712 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
713 struct ldb_dn *schema_dn = ldb_get_schema_basedn(service->samdb);
714 struct dsdb_schema *schema;
715 struct dsdb_schema *working_schema = NULL;
716 const struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr;
717 uint32_t object_count;
718 struct drsuapi_DsReplicaObjectListItemEx *first_object;
719 uint32_t linked_attributes_count;
720 struct drsuapi_DsReplicaLinkedAttribute *linked_attributes;
721 const struct drsuapi_DsReplicaCursor2CtrEx *uptodateness_vector;
722 struct dsdb_extended_replicated_objects *objects;
723 bool more_data = false;
724 WERROR status;
725 NTSTATUS nt_status;
726 uint32_t dsdb_repl_flags = 0;
727 struct ldb_dn *nc_root = NULL;
728 int ret;
730 switch (ctr_level) {
731 case 1:
732 mapping_ctr = &ctr1->mapping_ctr;
733 object_count = ctr1->object_count;
734 first_object = ctr1->first_object;
735 linked_attributes_count = 0;
736 linked_attributes = NULL;
737 rf1.source_dsa_obj_guid = ctr1->source_dsa_guid;
738 rf1.source_dsa_invocation_id = ctr1->source_dsa_invocation_id;
739 rf1.highwatermark = ctr1->new_highwatermark;
740 uptodateness_vector = NULL; /* TODO: map it */
741 more_data = ctr1->more_data;
742 break;
743 case 6:
744 mapping_ctr = &ctr6->mapping_ctr;
745 object_count = ctr6->object_count;
746 first_object = ctr6->first_object;
747 linked_attributes_count = ctr6->linked_attributes_count;
748 linked_attributes = ctr6->linked_attributes;
749 rf1.source_dsa_obj_guid = ctr6->source_dsa_guid;
750 rf1.source_dsa_invocation_id = ctr6->source_dsa_invocation_id;
751 rf1.highwatermark = ctr6->new_highwatermark;
752 uptodateness_vector = ctr6->uptodateness_vector;
753 more_data = ctr6->more_data;
754 break;
755 default:
756 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
757 tevent_req_nterror(req, nt_status);
758 return;
761 schema = dsdb_get_schema(service->samdb, state);
762 if (!schema) {
763 DEBUG(0,(__location__ ": Schema is not loaded yet!\n"));
764 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
765 return;
769 * Decide what working schema to use for object conversion.
770 * We won't need a working schema for empty replicas sent.
772 if (first_object) {
773 bool is_schema = ldb_dn_compare(partition->dn, schema_dn) == 0;
774 if (is_schema) {
775 /* create working schema to convert objects with */
776 status = dsdb_repl_make_working_schema(service->samdb,
777 schema,
778 mapping_ctr,
779 object_count,
780 first_object,
781 &drsuapi->gensec_skey,
782 state, &working_schema);
783 if (!W_ERROR_IS_OK(status)) {
784 DEBUG(0,("Failed to create working schema: %s\n",
785 win_errstr(status)));
786 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
787 return;
792 if (partition->partial_replica || partition->rodc_replica) {
793 dsdb_repl_flags |= DSDB_REPL_FLAG_PARTIAL_REPLICA;
795 if (state->op->options & DRSUAPI_DRS_FULL_SYNC_IN_PROGRESS) {
796 dsdb_repl_flags |= DSDB_REPL_FLAG_PRIORITISE_INCOMING;
798 if (state->op->options & DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING) {
799 dsdb_repl_flags |= DSDB_REPL_FLAG_EXPECT_NO_SECRETS;
802 if (state->op->extended_op != DRSUAPI_EXOP_NONE) {
803 ret = dsdb_find_nc_root(service->samdb, partition,
804 partition->dn, &nc_root);
805 if (ret != LDB_SUCCESS) {
806 DEBUG(0,(__location__ ": Failed to find nc_root for %s\n",
807 ldb_dn_get_linearized(partition->dn)));
808 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
809 return;
811 } else {
812 nc_root = partition->dn;
815 status = dsdb_replicated_objects_convert(service->samdb,
816 working_schema ? working_schema : schema,
817 nc_root,
818 mapping_ctr,
819 object_count,
820 first_object,
821 linked_attributes_count,
822 linked_attributes,
823 &rf1,
824 uptodateness_vector,
825 &drsuapi->gensec_skey,
826 dsdb_repl_flags,
827 state, &objects);
829 if (W_ERROR_EQUAL(status, WERR_DS_DRA_SCHEMA_MISMATCH)) {
830 struct dreplsrv_partition *p;
832 if (state->retry_started) {
833 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
834 DEBUG(0,("Failed to convert objects after retry: %s/%s\n",
835 win_errstr(status), nt_errstr(nt_status)));
836 tevent_req_nterror(req, nt_status);
837 return;
841 * Change info sync or extended operation into a fetch
842 * of the schema partition, so we get all the schema
843 * objects we need.
845 * We don't want to re-do the remote exop,
846 * unless it was REPL_SECRET so we set the
847 * fallback operation to just be a fetch of
848 * the relevent partition.
852 if (state->op->extended_op == DRSUAPI_EXOP_REPL_SECRET) {
853 state->extended_op_retry = state->op->extended_op;
854 } else {
855 state->extended_op_retry = DRSUAPI_EXOP_NONE;
857 state->op->extended_op = DRSUAPI_EXOP_NONE;
859 if (ldb_dn_compare(nc_root, partition->dn) == 0) {
860 state->source_dsa_retry = state->op->source_dsa;
861 } else {
862 status = dreplsrv_partition_find_for_nc(service,
863 NULL, NULL,
864 ldb_dn_get_linearized(nc_root),
865 &p);
866 if (!W_ERROR_IS_OK(status)) {
867 DEBUG(2, ("Failed to find requested Naming Context for %s: %s",
868 ldb_dn_get_linearized(nc_root),
869 win_errstr(status)));
870 nt_status = werror_to_ntstatus(status);
871 tevent_req_nterror(req, nt_status);
872 return;
874 status = dreplsrv_partition_source_dsa_by_guid(p,
875 &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
876 &state->source_dsa_retry);
878 if (!W_ERROR_IS_OK(status)) {
879 struct GUID_txt_buf str;
880 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
881 ldb_dn_get_linearized(nc_root),
882 GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
883 win_errstr(status)));
884 nt_status = werror_to_ntstatus(status);
885 tevent_req_nterror(req, nt_status);
886 return;
890 /* Find schema naming context to be synchronized first */
891 status = dreplsrv_partition_find_for_nc(service,
892 NULL, NULL,
893 ldb_dn_get_linearized(schema_dn),
894 &p);
895 if (!W_ERROR_IS_OK(status)) {
896 DEBUG(2, ("Failed to find requested Naming Context for schema: %s",
897 win_errstr(status)));
898 nt_status = werror_to_ntstatus(status);
899 tevent_req_nterror(req, nt_status);
900 return;
903 status = dreplsrv_partition_source_dsa_by_guid(p,
904 &state->op->source_dsa->repsFrom1->source_dsa_obj_guid,
905 &state->op->source_dsa);
906 if (!W_ERROR_IS_OK(status)) {
907 struct GUID_txt_buf str;
908 DEBUG(2, ("Failed to find requested source DSA for %s and %s: %s",
909 ldb_dn_get_linearized(schema_dn),
910 GUID_buf_string(&state->op->source_dsa->repsFrom1->source_dsa_obj_guid, &str),
911 win_errstr(status)));
912 nt_status = werror_to_ntstatus(status);
913 tevent_req_nterror(req, nt_status);
914 return;
916 DEBUG(4,("Wrong schema when applying reply GetNCChanges, retrying\n"));
918 state->retry_started = true;
919 dreplsrv_op_pull_source_get_changes_trigger(req);
920 return;
922 } else if (!W_ERROR_IS_OK(status)) {
923 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
924 DEBUG(0,("Failed to convert objects: %s/%s\n",
925 win_errstr(status), nt_errstr(nt_status)));
926 tevent_req_nterror(req, nt_status);
927 return;
930 status = dsdb_replicated_objects_commit(service->samdb,
931 working_schema,
932 objects,
933 &state->op->source_dsa->notify_uSN);
934 talloc_free(objects);
936 if (!W_ERROR_IS_OK(status)) {
939 * If we failed to apply the records due to a missing
940 * parent, try again after asking for the parent
941 * records first. Because we don't update the
942 * highwatermark, we start this part of the cycle
943 * again.
945 if (((state->op->options & DRSUAPI_DRS_GET_ANC) == 0)
946 && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
947 state->op->options |= DRSUAPI_DRS_GET_ANC;
948 DEBUG(4,("Missing parent object when we didn't set the DRSUAPI_DRS_GET_ANC flag, retrying\n"));
949 dreplsrv_op_pull_source_get_changes_trigger(req);
950 return;
951 } else if (((state->op->options & DRSUAPI_DRS_GET_ANC))
952 && W_ERROR_EQUAL(status, WERR_DS_DRA_MISSING_PARENT)) {
953 DEBUG(1,("Missing parent object despite setting DRSUAPI_DRS_GET_ANC flag\n"));
954 nt_status = NT_STATUS_INVALID_NETWORK_RESPONSE;
955 } else {
956 nt_status = werror_to_ntstatus(WERR_BAD_NET_RESP);
958 DEBUG(0,("Failed to commit objects: %s/%s\n",
959 win_errstr(status), nt_errstr(nt_status)));
960 tevent_req_nterror(req, nt_status);
961 return;
964 if (state->op->extended_op == DRSUAPI_EXOP_NONE) {
965 /* if it applied fine, we need to update the highwatermark */
966 *state->op->source_dsa->repsFrom1 = rf1;
969 /* we don't need this maybe very large structure anymore */
970 TALLOC_FREE(r);
972 if (more_data) {
973 dreplsrv_op_pull_source_get_changes_trigger(req);
974 return;
978 * If we had to divert via doing some other thing, such as
979 * pulling the schema, then go back and do the original
980 * operation once we are done.
982 if (state->source_dsa_retry != NULL) {
983 state->op->source_dsa = state->source_dsa_retry;
984 state->op->extended_op = state->extended_op_retry;
985 state->source_dsa_retry = NULL;
986 dreplsrv_op_pull_source_get_changes_trigger(req);
987 return;
990 if (state->op->extended_op != DRSUAPI_EXOP_NONE ||
991 state->op->service->am_rodc) {
993 we don't do the UpdateRefs for extended ops or if we
994 are a RODC
996 tevent_req_done(req);
997 return;
1000 /* now we need to update the repsTo record for this partition
1001 on the server. These records are initially established when
1002 we join the domain, but they quickly expire. We do it here
1003 so we can use the already established DRSUAPI pipe
1005 dreplsrv_update_refs_trigger(req);
1008 static void dreplsrv_update_refs_done(struct tevent_req *subreq);
1011 send a UpdateRefs request to refresh our repsTo record on the server
1013 static void dreplsrv_update_refs_trigger(struct tevent_req *req)
1015 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1016 struct dreplsrv_op_pull_source_state);
1017 struct dreplsrv_service *service = state->op->service;
1018 struct dreplsrv_partition *partition = state->op->source_dsa->partition;
1019 struct dreplsrv_drsuapi_connection *drsuapi = state->op->source_dsa->conn->drsuapi;
1020 struct drsuapi_DsReplicaUpdateRefs *r;
1021 char *ntds_dns_name;
1022 struct tevent_req *subreq;
1024 r = talloc(state, struct drsuapi_DsReplicaUpdateRefs);
1025 if (tevent_req_nomem(r, req)) {
1026 return;
1029 ntds_dns_name = samdb_ntds_msdcs_dns_name(service->samdb, r, &service->ntds_guid);
1030 if (tevent_req_nomem(ntds_dns_name, req)) {
1031 talloc_free(r);
1032 return;
1035 r->in.bind_handle = &drsuapi->bind_handle;
1036 r->in.level = 1;
1037 r->in.req.req1.naming_context = &partition->nc;
1038 r->in.req.req1.dest_dsa_dns_name = ntds_dns_name;
1039 r->in.req.req1.dest_dsa_guid = service->ntds_guid;
1040 r->in.req.req1.options = DRSUAPI_DRS_ADD_REF | DRSUAPI_DRS_DEL_REF;
1041 if (!service->am_rodc) {
1042 r->in.req.req1.options |= DRSUAPI_DRS_WRIT_REP;
1045 state->ndr_struct_ptr = r;
1046 subreq = dcerpc_drsuapi_DsReplicaUpdateRefs_r_send(state,
1047 state->ev,
1048 drsuapi->drsuapi_handle,
1050 if (tevent_req_nomem(subreq, req)) {
1051 talloc_free(r);
1052 return;
1054 tevent_req_set_callback(subreq, dreplsrv_update_refs_done, req);
1058 receive a UpdateRefs reply
1060 static void dreplsrv_update_refs_done(struct tevent_req *subreq)
1062 struct tevent_req *req = tevent_req_callback_data(subreq,
1063 struct tevent_req);
1064 struct dreplsrv_op_pull_source_state *state = tevent_req_data(req,
1065 struct dreplsrv_op_pull_source_state);
1066 struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(state->ndr_struct_ptr,
1067 struct drsuapi_DsReplicaUpdateRefs);
1068 NTSTATUS status;
1070 state->ndr_struct_ptr = NULL;
1072 status = dcerpc_drsuapi_DsReplicaUpdateRefs_r_recv(subreq, r);
1073 TALLOC_FREE(subreq);
1074 if (!NT_STATUS_IS_OK(status)) {
1075 DEBUG(0,("UpdateRefs failed with %s\n",
1076 nt_errstr(status)));
1077 tevent_req_nterror(req, status);
1078 return;
1081 if (!W_ERROR_IS_OK(r->out.result)) {
1082 status = werror_to_ntstatus(r->out.result);
1083 DEBUG(0,("UpdateRefs failed with %s/%s for %s %s\n",
1084 win_errstr(r->out.result),
1085 nt_errstr(status),
1086 r->in.req.req1.dest_dsa_dns_name,
1087 r->in.req.req1.naming_context->dn));
1089 * TODO we are currently not sending the
1090 * DsReplicaUpdateRefs at the correct moment,
1091 * we do it just after a GetNcChanges which is
1092 * not always correct.
1093 * Especially when another DC is trying to demote
1094 * it will sends us a DsReplicaSync that will trigger a getNcChanges
1095 * this call will succeed but the DsRecplicaUpdateRefs that we send
1096 * just after will not because the DC is in a demote state and
1097 * will reply us a WERR_DS_DRA_BUSY, this error will cause us to
1098 * answer to the DsReplicaSync with a non OK status, the other DC
1099 * will stop the demote due to this error.
1100 * In order to cope with this we will for the moment concider
1101 * a DS_DRA_BUSY not as an error.
1102 * It's not ideal but it should not have a too huge impact for
1103 * running production as this error otherwise never happen and
1104 * due to the fact the send a DsReplicaUpdateRefs after each getNcChanges
1106 if (!W_ERROR_EQUAL(r->out.result, WERR_DS_DRA_BUSY)) {
1107 tevent_req_nterror(req, status);
1108 return;
1112 DEBUG(4,("UpdateRefs OK for %s %s\n",
1113 r->in.req.req1.dest_dsa_dns_name,
1114 r->in.req.req1.naming_context->dn));
1116 tevent_req_done(req);
1119 WERROR dreplsrv_op_pull_source_recv(struct tevent_req *req)
1121 NTSTATUS status;
1123 if (tevent_req_is_nterror(req, &status)) {
1124 tevent_req_received(req);
1125 return ntstatus_to_werror(status);
1128 tevent_req_received(req);
1129 return WERR_OK;