4.1.11.3 Server Behavior of the IDL_DRSGetNT4ChangeLog Method

Informative summary of behavior: If the server is the PDC emulator FSMO role owner, it returns either a sequence of PDC change log entries or the NT4 replication state, or both, as requested by the client.

Multiple calls of this method might be required to retrieve the entire PDC change log. The client passes pRestart = null on the first call in a series of calls; the server returns a sequence of change log entries, including the first, a pointer to an opaque cookie, and a result code. If the server returns no change log entries, it returns null instead of a pointer to a cookie. If the server returns the result code zero, the sequence of change log entries in the response includes the final entry in the log.

The cookie encodes the serial number of the last change log entry returned. If the server returns ERROR_MORE_DATA, the final change log entry in the response was not the final entry in the change log. The client can make another call, with pRestart pointing to the cookie. The server processes this call identically to a call with pRestart = null, except that it returns change log entries starting with the entry following the last previously returned entry, as indicated by the cookie. By making enough calls the client can retrieve the entire change log.

If the client includes a cookie that is either corrupted or identifies a nonexistent change log entry (possibly because the cookie is too old), the server returns ERROR_INVALID_PARAMETER. If there are change log entries to return, but the client specifies a bound on the size of the returned change log entries that is too small to hold even a single entry, the server returns ERROR_INSUFFICIENT_BUFFER.

The NT4 replication state is a small, fixed-size structure and the server simply copies it into the response.

When the client requests both the PDC change log and the NT4 replication state, the server processes the PDC change log request first. If an error occurs during this processing the server does not process the request for NT4 replication state. If an error occurs while processing the NT4 replication state request, the server returns no indication to the client that the PDC change log request succeeded.

 ULONG
 IDL_DRSGetNT4ChangeLog(
     [in, ref] DRS_HANDLE hDrs,
     [in] DWORD dwInVersion,
     [in, ref, switch_is(dwInVersion)] DRS_MSG_NT4_CHGLOG_REQ *pmsgIn,
     [out, ref] DWORD *pdwOutVersion,
     [out, ref, switch_is(*pdwOutVersion)]
         DRS_MSG_NT4_CHGLOG_REPLY *pmsgOut)
  
 msgIn: DRS_MSG_NT4_CHGLOG_REQ_V1
 readStatus, ntStatus: DWORD
 sequenceNumber: integer
 nextIndexToBeReturned, lastIndexToBeReturned: integer
 lastReturnedSerialNumber: LONGLONG
 lastReturnedIndex: integer
 pChangeLog: ADDRESS OF CHANGE_LOG_ENTRIES
  
 ValidateDRSInput(hDrs, 11)
  
 pdwOutVersion^ := 1
 pmsgOut^.V1.cbRestart := 0
 pmsgOut^.V1.cbLog := 0
 pmsgOut^.V1.ReplicationState.SamSerialNumber := 0
 pmsgOut^.V1.ReplicationState.SamCreationTime := 0
 pmsgOut^.V1.ReplicationState.BuiltinSerialNumber := 0
 pmsgOut^.V1.ReplicationState.BuiltinCreationTime := 0
 pmsgOut^.V1.ReplicationState.LsaSerialNumber := 0
 pmsgOut^.V1.ReplicationState.LsaCreationTime := 0
 pmsgOut^.V1.ActualNtStatus := 0 
 pmsgOut^.V1.pRestart := null
 pmsgOut^.V1.pLog := null
  
 /* Validate the request version */
 if dwInVersion ≠ 1 then
   return ERROR_DS_DRA_INVALID_PARAMETER
 endif
 msgIn := pmsgIn^.V1
  
 /* Access check */
 if not AccessCheckCAR(DefaultNC(), DS-Replication-Get-Changes) then
   return ERR0R_ACCESS_DENIED
 endif
  
 /* The DC must own the PDC role */
 if not IsPDC() then
   return ERROR_INVALID_DOMAIN_ROLE
 endif
  
 ntStatus := 0
 readStatus := 0
 if DRS_NT4_CHGLOG_GET_CHANGE_LOG in msgIn.dwFlags then
   /* Return NT4 change log entries. */
  
   /* Determine the position of the first entry in the change log that
    * needs to be returned. If pRestart = null, this is the first
    * entry of the change log, otherwise it is the entry following the
    * entry identified in the cookie pRestart^. */
   if msgIn.pRestart = null then
     sequenceNumber := 1
     nextIndexToBeReturned := 0
   else
     sequenceNumber :=
         (Sequence number extracted from msgIn.pRestart^) + 1
     lastReturnedSerialNumber :=
         Serial number extracted from msgIn.pRestart^
     lastReturnedIndex := select one i in dc.pdcChangeLog where
         dc.pdcChangeLog[i].SerialNumber = lastReturnedSerialNumber
     if lastReturnedIndex = null then
       /* Cookie is old or corrupted.
        * The STATUS code STATUS_INVALID_PARAMETER corresponds to
        * the Windows error code ERROR_INVALID_PARAMETER. */
       ntStatus := STATUS_INVALID_PARAMETER
     else
       nextIndexToBeReturned := lastReturnedIndex + 1
     endif
   endif
  
   if ntStatus = 0 and nextIndexToBeReturned ≥ dc.pdcChangeLog.length
       then
     /* No entries to be returned, complete the response message */
     pmsgOut^.V1.pLog := null
     pmsgOut^.V1.cbLog := 0
     pmsgOut^.V1.pRestart := null
     pmsgOut^.V1.cbRestart := 0
   endif
  
   if ntStatus = 0 and 
       nextIndexToBeReturned < dc.pdcChangeLog.length then
     /* Entries to be returned. First, determine how many entries fit
      * into the response message */
     lastIndexToBeReturned := the largest integer q such that
         q < dc.pdcChangeLog.length and
           the size in bytes of
           dc.pdcChangeLog[nextIndexToBeReturned .. q]
             is <= msgIn.PreferredMaximumLength
     if lastIndexToBeReturned < nextIndexToBeReturned then
       /* Client's PreferredMaximumLength is too small for a single
        * entry, so return no entries.
        * The STATUS code STATUS_BUFFER_TOO_SMALL corresponds to
        * the Windows error code ERROR_INSUFFICIENT_BUFFER. */
       ntStatus := STATUS_BUFFER_TOO_SMALL
     else
       /* Client's PreferredMaximumLength is large enough for one or
        * more entries. Fill in pChangeLog^ from dc.pdcChangeLog */
       pChangeLog^.Size := 0x00000010
       pChangeLog^.Version := 0x00000001
       pChangeLog^.SequenceNumber := sequenceNumber
       pChangeLog^.Flags := 0x00000000
       pChangeLog^.ChangeLogEntries := 
          dc.pdcChangeLog[nextIndexToBeReturned .. 
                          lastIndexToBeReturned]
       if a fatal error occurred while retrieving dc.pdcChangeLog then
         ntStatus :=
             STATUS code of error that occurred, high-order bit set
       end
     endif
     if ntStatus = 0 then
       /*  No errors, complete the response message */
       pmsgOut^.V1.pLog := pChangeLog
       pmsgOut^.V1.cbLog := size in bytes of pmsgOut^.V1.pLog^
       /* Construct a new cookie */
       lastReturnedSerialNumber :=
           dc.pdcChangeLog[lastIndexToBeReturned].SerialNumber
       pmsgOut^.V1.pRestart := 
           ADDRESS OF implementation-specific struct
           encapsulating lastReturnedSerialNumber and sequenceNumber
       pmsgOut^.V1.cbRestart := size in bytes of pmsgOut^.V1.pRestart^
       if lastIndexToBeReturned < dc.pdcChangeLog.length - 1 then
         /* There are more entries to be returned.
          * The STATUS code STATUS_MORE_ENTRIES corresponds to
          * the Windows error code ERROR_MORE_DATA. */
         ntStatus := STATUS_MORE_ENTRIES
       endif
     endif /* Response complete */
   endif /* Entries returned */
 endif /* Processed change log request */
  
 /* Save the status code from the previous operation */
 readStatus := ntStatus
  
 if ntStatus < 0x80000000 and
     DRS_NT4_CHGLOG_GET_SERIAL_NUMBERS in msgIn.dwFlags then
   /* Return NT4 replication state. */
   pmsgOut^.V1.ReplicationState.SamSerialNumber :=
       dc.nt4ReplicationState.SamNT4ReplicationUSN
   pmsgOut^.V1.ReplicationState.SamCreationTime :=
       dc.nt4ReplicationState.SamCreationTime
   pmsgOut^.V1.ReplicationState.BuiltinSerialNumber :=
       dc.nt4ReplicationState.BuiltinNT4ReplicationUSN
   pmsgOut^.V1.ReplicationState.BuiltinCreationTime :=
       dc.nt4ReplicationState.BuiltinCreationTime
   pmsgOut^.V1.ReplicationState.LsaSerialNumber := 1
   pmsgOut^.V1.ReplicationState.LsaCreationTime := 
       current time on the DC
   if a fatal error occurred while retrieving NT4 replication state 
       then
     ntStatus :=
         STATUS code of error that occurred, high-order bit set
   end
 endif
  
 if ntStatus < 0x80000000 then
   pmsgOut^.V1.ActualStatus := readStatus
 else
   pmsgOut^.V1.ActualStatus := ntStatus
 endif
  
 return GetWindowsErrorCode(ntStatus)