次の方法で共有


4.1.13.3 Server Behavior of the IDL_DRSGetReplInfo Method

Informative summary of behavior: This method retrieves the replication state information of a DC. Based on the value of the InfoType field in the request message, different information is returned, which is summarized in the definition of DS_REPL_INFO in section 4.1.13.1.4.

 ULONG
 IDL_DRSGetReplInfo(
   [in, ref] DRS_HANDLE hDrs,
   [in] DWORD dwInVersion,
   [in, ref, switch_is(dwInVersion)]
       DRS_MSG_GETREPLINFO_REQ *pmsgIn,
   [out, ref] DWORD *pdwOutVersion,
   [out, ref, switch_is(*pdwOutVersion)]
       DRS_MSG_GETREPLINFO_REPLY *pmsgOut)
  
 msgIn: DRS_MSG_GETREPLINFO_REQ_V2
 infoType: DWORD
 fAccessGranted: boolean
 infoTypeValid: boolean
 defaultNC: DSName
 object: DSName
 enumerationContext: DWORD
 baseIndex: DWORD
 endIndex: DWORD
 ncs: set of DSName
 nc: DSName
 i, j: DWORD
 r: RepsFrom
 q: RepsTo
 pNeighbor: ADDRESS OF DS_REPL_NEIGHBORW
 utd: sequence of ReplUpToDateVector
 pCursor: ADDRESS OF DS_REPL_CURSOR
 pCursor2: ADDRESS OF DS_REPL_CURSOR_2
 pCursor3: ADDRESS OF DS_REPL_CURSOR_3W
 a: ATTRTYP
 attr: ATTRTYP
 attrs: set of ATTRTYP
 attrsSeq: sequence of ATTRTYP
 s: AttributeStamp
 stamp: LinkValueStamp
 pObjMetaData: ADDRESS OF DS_REPL_OBJ_META_DATA
 pObjMetaData2: ADDRESS OF DS_REPL_OBJ_META_DATA_2
 values: set of attribute value
 valuesSeq: sequence of attribute value
 ls: LinkValueStamp
 pAttrValueMetaData: ADDRESS OF DS_REPL_ATTR_VALUE_META_DATA
 pAttrValueMetaData2: ADDRESS OF DS_REPL_ATTR_VALUE_META_DATA_2
 pFailedConnection: ADDRESS OF DS_REPL_KCC_DSA_FAILUREW
 pFailedLink: ADDRESS OF DS_REPL_KCC_DSA_FAILUREW
 pPendingOp: ADDRESS OF DS_REPL_OPW
 pClientContext: ADDRESS OF DS_REPL_CLIENT_CONTEXT
 pOutgoingContext: ADDRESS OF DS_REPL_SERVER_OUTGOING_CALL
 v: attribute value
  
 ValidateDRSInput(hDrs, 19)
  
 if dwInVersion = 1 then
   infoType = pmsgIn^ V1.InfoType
 else
   infoType = pmsgIn^ V2.InfoType
 endif
 pdwOutVersion^ := infoType
  
 if infoType = DS_REPL_INFO_NEIGHBORS then
   pmsgOut^.pNeighbors := null
 else if infoType = DS_REPL_INFO_CURSORS_FOR_NC then
   pmsgOut^.pCursors := null
 else if infoType = DS_REPL_INFO_METADATA_FOR_OBJ then
   pmsgOut^.pObjMetaData := null
 else if infoType = DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES then
   pmsgOut^.pConnectFailures := null
 else if infoType = DS_REPL_INFO_KCC_DSA_LINK_FAILURES then
   pmsgOut^.pLinkFailures := null
 else if infoType = DS_REPL_INFO_PENDING_OPS then
   pmsgOut^.pPendingOps := null
 else if infoType = DS_REPL_INFO_METADATA_FOR_ATTR_VALUE then
   pmsgOut^.pAttrValueMetaData := null
 else if infoType = DS_REPL_INFO_CURSORS_2_FOR_NC then
   pmsgOut^.pCursors2 := null
 else if infoType = DS_REPL_INFO_CURSORS_3_FOR_NC then
   pmsgOut^.pCursors3 := null
 else if infoType = DS_REPL_INFO_METADATA_2_FOR_OBJ then
   pmsgOut^.pObjMetaData2 := null
 else if infoType = DS_REPL_INFO_METADATA_2_FOR_ATTR_VALUE then
   pmsgOut^.pAttrValueMetaData2 := null
 else if infoType = DS_REPL_INFO_SERVER_OUTGOING_CALLS then
   pmsgOut^.pServerOutgoingCalls := null
 else if infoType = DS_REPL_INFO_UPTODATE_VECTOR_V1 then
   pmsgOut^.pUpToDateVec := null
 else if infoType = DS_REPL_INFO_CLIENT_CONTEXTS then
   pmsgOut^.pClientContexts := null
 else if infoType = DS_REPL_INFO_REPSTO then
   pmsgOut^.pRepsTo := null
 endif
  
 /* Validate the version of the request message */
 if (dwInVersion ≠ 1 and dwInVersion ≠ 2) then
   return ERROR_REVISION_MISMATCH
 endif
  
 if dwInVersion = 1 then
   msgIn := pmsgIn^.V1
 else
   msgIn := pmsgIn^.V2
 endif
  
 /* For some of the request types, paging is supported. For these 
  * cases, a starting index into the result set is needed based on 
 * what has already been returned in a previous call. Only version 2
 * request messages provide a mechanism for the client to supply the
 * context information from a previous call. */
 if dwInVersion = 1 then 
   baseIndex := 0
 else 
   if msgIn.dwEnumerationContext = 0xffffffff then
     /* No more data is available. */
     return ERROR_NO_MORE_ITEMS
   endif
   baseIndex := msgIn.dwEnumerationContext
 endif
  
 /* Perform the necessary access checks. */
 defaultNC := DefaultNC()
 fAccessGranted := false
 infoTypeValid := false
 object := msgIn.pszObjectDN
 if (infoType = DS_REPL_INFO_NEIGHBORS and object ≠ null) then
   infoTypeValid := true
   fAccessGranted := 
     AccessCheckAttr(object, repsFrom, RIGHT_DS_READ_PROPERTY) or
     AccessCheckCAR(object, DS-Replication-Manage-Topology) or 
     AccessCheckCAR(object, DS-Replication-Monitor-Topology)
 endif
 if (infoType = DS_REPL_INFO_NEIGHBORS and object = null) then
   infoTypeValid := true
   fAccessGranted := 
     AccessCheckAttr(defaultNC, repsFrom, RIGHT_DS_READ_PROPERTY) or
     AccessCheckCAR(defaultNC, DS-Replication-Manage-Topology) or 
     AccessCheckCAR(defaultNC, DS-Replication-Monitor-Topology)
 endif
 if (infoType = DS_REPL_INFO_REPSTO and object ≠ null) then
   infoTypeValid := true
   fAccessGranted := 
     AccessCheckAttr(object, repsTo, RIGHT_DS_READ_PROPERTY) or
     AccessCheckCAR(object, DS-Replication-Manage-Topology) or 
     AccessCheckCAR(object, DS-Replication-Monitor-Topology)
 endif
 if (infoType = DS_REPL_INFO_REPSTO and object = null) then
   infoTypeValid := true
   fAccessGranted := 
     AccessCheckAttr(defaultNC, repsTo, RIGHT_DS_READ_PROPERTY) or
     AccessCheckCAR(defaultNC, DS-Replication-Manage-Topology) or 
     AccessCheckCAR(defaultNC, DS-Replication-Monitor-Topology)
 endif
 if (infoType in {DS_REPL_INFO_CURSORS_FOR_NC,
                 DS_REPL_INFO_CURSORS_2_FOR_NC,
                 DS_REPL_INFO_CURSORS_3_FOR_NC, 
                 DS_REPL_INFO_UPTODATE_VECTOR_V1} and
     object ≠ null) then
   infoTypeValid := true
   fAccessGranted := 
     AccessCheckAttr(
         object, replUpToDateVector, RIGHT_DS_READ_PROPERTY) or
     AccessCheckCAR(object, DS-Replication-Manage-Topology) or 
     AccessCheckCAR(object, DS-Replication-Monitor-Topology)
 endif
 if infoType in {DS_REPL_INFO_METADATA_FOR_OBJ,
                 DS_REPL_INFO_METADATA_2_FOR_OBJ,
                 DS_REPL_INFO_METADATA_FOR_ATTR_VALUE,
                 DS_REPL_INFO_METATDATA_2_FOR_ATTR_VALUE} then
   if object = null then
     return ERROR_INVALID_PARAMETER
   endif
   if  not ObjExists(object) then
     if object.dn = null then
     return ERROR_DS_DRA_BAD_DN
     else 
       return ERROR_DS_OBJ_NOT_FOUND
     endif
   endif
   infoTypeValid := true
   fAccessGranted := 
        AccessCheckAttr(object,
                        replPropertyMetaData,
                        RIGHT_DS_READ_PROPERTY) or
        AccessCheckCAR(object, DS-Replication-Manage-Topology) or
        AccessCheckCAR(object, DS-Replication-Monitor-Topology)
 endif
 if infoType in {DS_REPL_INFO_PENDING_OPS,
                 DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES,
                 DS_REPL_INFO_KCC_DSA_LINK_FAILURES,
                 DS_REPL_INFO_CLIENT_CONTEXTS,
                 DS_REPL_INFO_SERVER_OUTGOING_CALLS} then
   infoTypeValid := true
   fAccessGranted := 
     AccessCheckCAR(defaultNC, DS-Replication-Manage-Topology) or 
     AccessCheckCAR(defaultNC, DS-Replication-Monitor-Topology)
 endif
  
 if not infoTypeValid then
   return ERROR_INVALID_PARAMETER
 endif
  
 if not fAccessGranted then
   return ERROR_DS_DRA_ACCESS_DENIED
 endif
  
 /* Based on the type of information requested, the corresponding 
 * information is retrieved and the response message constructed */
  
 /* DS_REPL_INFO_NEIGHBORS/DS_REPL_INFO_REPSTO */
 if infoType in {DS_REPL_INFO_NEIGHBORS, DS_REPL_INFO_REPSTO}
   /* If an object is specified, it must be an NC root. */
   nc := object
  
   if nc ≠ null then
     ncs := {nc}
   else 
     ncs := GetNCs()
   endif
   
   if infoType = DS_REPL_INFO_NEIGHBORS then
     i := 0
     j := 0
     foreach nc in ncs
       foreach r in nc!repsFrom
         /* The ordering of ncs hosted by the server and the values of
          * repsFrom for each nc is arbitrary but consistent from call
          * to call on a server. */
  
         /* If a source server GUID is specified, only information for
          * that server is returned. */
         If (msgIn.uuidSourceDsaGuid = NULLGUID or  
               msgIn.uuidSourceDsaGuid = r.uuidDsa) then
           if i >= baseIndex then
             pNeighbor := ADR(pmsgOut^.pNeighbors^.rgNeighbor[j])
             pNeighbor^.pszSourceDsaAddress := r.naDsa
             pNeighbor^.uuidSourceDsaObjGuid := r.uuidDsa
             pNeighbor^.pszSourceDsaDN := 
                 GetDNFromObjectGuid(r.uuidDsa)
             pNeighbor^.pszNamingContext := nc!distinguishedName
             /* If a naming context is specified in the request,
              * the uuidNamingContextObjGuid field of the response
              * is set to the NULL GUID. */
             if object ≠ null then
               pNeighbor^.uuidNamingContextObjGuid := NULLGUID
             else 
               pNeighbor^.uuidNamingContextObjGuid := nc!objectGUID
             endif
             pNeighbor^.pszAsyncIntersiteTransportDN := 
                 GetDNFromObjectGuid(r.uuidTransportObj)
             pNeighbor^.uuidSourceDsaInvocationID := r.uuidInvocId
             pNeighbor^.uuidAsyncIntersiteTransportObjGuid := 
                 r.uuidTransportObj          
             pNeighbor^.usnLastObjChangeSynced := 
                 r.usnVec.usnHighObjUpdate
             pNeighbor^.usnAttributeFilter := 
                 r.usnVec.usnHighPropUpdate
             pNeighbor^.ftimeLastSyncSuccess := r.timeLastSuccess
             pNeighbor^.ftimeLastSyncAttempt := r.timeLastAttempt
             pNeighbor^.dwLastSyncResult := r.ulResultLastAttempt
             pNeighbor^.cNumConsecutiveSyncFailures := 
                 r.cConsecutiveFailures
             /* Only a subset of the possible DRS_OPTIONS in r.options
              * are preserved in pNeighbor^.dwReplicaFlags.
              * See section 5.169 repsFrom, RepsFrom for more info. */
             pNeighbor^.dwReplicaFlags := {}
             foreach flag in { DRS_WRIT_REP,
                               DRS_INIT_SYNC,
                               DRS_PER_SYNC,
                               DRS_MAIL_REP,
                               DRS_DISABLE_AUTO_SYNC,
                               DRS_DISABLE_PERIODIC_SYNC,
                               DRS_USE_COMPRESSION,
                               DRS_TWOWAY_SYNC,
                               DRS_NONGC_RO_REP,
                               DRS_FULL_SYNC_IN_PROGRESS,
                               DRS_FULL_SYNC_PACKET,
                               DRS_REF_GCSPN,
                               DRS_NEVER_SYNCED,
                               DRS_SPECIAL_SECRET_PROCESSING,
                               DRS_PREEMPTED,
                               DRS_NEVER_NOTIFY,
                               DRS_SYNC_PAS}
                 if flag in r.options then
                     pNeighbor^.dwReplicaFlags := pNeighbor^.dwReplicaFlags + flag
                 endif
             endfor
             j := j + 1
           endif
           i := i + 1
         endif
       endfor
     endfor
     pmsgOut^.pNeighbors^.cNumNeighbors := j
   else 
     /* DS_REPL_INFO_REPSTO case. */
     i := 0
     j := 0
     foreach nc in ncs
       foreach q in nc!repsTo
         /* The ordering of ncs hosted by the server and the values of
          * repsTo for each nc is arbitrary but consistent from call
          * to call on a server. */
         if i >= baseIndex then
           pNeighbor := ADR(pmsgOut^.pRepsTo^.rgNeighbor[j])
           pNeighbor^.pszSourceDsaAddress := q.naDsa
           pNeighbor^.ftimeLastSyncSuccess := q.timeLastSuccess
           pNeighbor^.ftimeLastSyncAttempt := q.timeLastAttempt
           pNeighbor^.dwLastSyncResult := q.ulResultLastAttempt
           pNeighbor^.cNumConsecutiveSyncFailures := 
                 q.cConsecutiveFailures
           pNeighbor^.uuidSourceDsaObjGuid := q.uuidDsa
           pNeighbor^.pszSourceDsaDN := GetDNFromObjectGuid(q.uuidDsa)
           pNeighbor^.pszNamingContext := nc!distinguishedName
           /* If a naming context is specified in the request,
            * the uuidNamingContextObjGuid field of the response
            * is set to the NULL GUID. */
           if object ≠ null then
             pNeighbor^.uuidNamingContextObjGuid := NULLGUID
           else 
             pNeighbor^.uuidNamingContextObjGuid := nc!objectGUID
           endif
           /* Only a subset of the possible DRS_OPTIONS in q.options
            * are preserved in pNeighbor^.dwReplicaFlags.
            * See section 5.170 repsTo, RepsTo for more info. */
           pNeighbor^.dwReplicaFlags := {}
           foreach flag in { DRS_WRIT_REP,
                             DRS_INIT_SYNC,
                             DRS_PER_SYNC,
                             DRS_MAIL_REP,
                             DRS_DISABLE_AUTO_SYNC,
                             DRS_DISABLE_PERIODIC_SYNC,
                             DRS_USE_COMPRESSION,
                             DRS_TWOWAY_SYNC,
                             DRS_NONGC_RO_REP,
                             DRS_FULL_SYNC_IN_PROGRESS,
                             DRS_FULL_SYNC_PACKET,
                             DRS_REF_GCSPN,
                             DRS_NEVER_SYNCED,
                             DRS_SPECIAL_SECRET_PROCESSING,
                             DRS_PREEMPTED,
                             DRS_NEVER_NOTIFY,
                             DRS_SYNC_PAS}
               if flag in q.options then
                   pNeighbor^.dwReplicaFlags := pNeighbor^.dwReplicaFlags + flag
               endif
           endfor
           j := j + 1
         endif
         i := i + 1
       endfor
     endfor
     pmsgOut^.pRepsTo^.cNumNeighbors := j
   endif
 endif
  
 /* DS_REPL_INFO_METADATA_FOR_OBJ/DS_REPL_INFO_METADATA_2_FOR_OBJ */
 if infoType in {DS_REPL_INFO_METADATA_FOR_OBJ,
     DS_REPL_INFO_METADATA_2_FOR_OBJ) then
  
   /* Enumerate all the replicated attributes */
   attrsSeq := ReplicatedAttributes()
   i := 0
   j := 0
   while (i < attrsSeq.length)
     attr := attrsSeq[i]
     s := AttrStamp(object, attr)
  
     if (IsForwardLinkAttribute(attr) and
         dwInVersion = 2 and
         DS_REPL_INFO_FLAG_IMPROVE_LINKED_ATTRS in msgIn.ulFlags) 
           then
       ls := null
       foreach v in GetAttrVals(object, attr, true)
         stamp := LinkStamp(object, attr, v)
         /* If v was last updated in win2k forest mode 
          * then it does not have LinkValueStamp associated with it.
          * LinkStamp() returns null in that case. */ 
         if stamp ≠ null and LinkValueStampCompare(stamp, ls) > 0 then
           ls := stamp;
         endif
       endfor
       if s = null then
         s := 0 /* An AttributeStamp with 0 for all fields. */
       endif
  
       /* Improve the stamp with the link value stamp. */
       s.dwVersion := ls.dwVersion
       s.timeChanged := ls.timeChanged
       s.uuidOriginating := NULLGUID
       s.usnOriginating := ls.usnOriginating
     endif  
  
     if s ≠ null then
       if i >= baseIndex
         if infoType = DS_REPL_INFO_METADATA_FOR_OBJ then
           pObjMetaData := ADR(pmsgOut^.pObjMetaData^.rgMetaData[j])
           pObjMetaData^.pszAttributeName := attr
           pObjMetaData^.dwVersion := s.dwVersion
           pObjMetaData^.ftimeLastOriginatingChange := s.timeChanged
           pObjMetaData^.uuidLastOriginatingDsaInvocationID := 
               s.uuidOriginating
           pObjMetaData^.usnOriginatingChange := s.usnOriginating
           pObjMetaData^.usnLocalChange :=
               An implementation-specific value that the server
               maintains for replicated attributes
         else
           pObjMetaData2 := ADR(pmsgOut^.pObjMetaData2^.rgMetaData[j])
           pObjMetaData2^.pszAttributeName := attr
           pObjMetaData2^.dwVersion := s.dwVersion
           pObjMetaData2^.ftimeLastOriginatingChange := s.timeChanged
           pObjMetaData2^.uuidLastOriginatingDsaInvocationID := 
               s.uuidOriginating
           pObjMetaData2^.usnOriginatingChange := s.usnOriginating
           pObjMetaData2^.usnLocalChange := 
               An implementation-specific value that the server
               maintains for replicated attributes
           pObjMetaData2^.pszLastOriginatingDsaDN := 
               GetDNFromInvocationID(s.uuidOriginating)
         endif
         j := j + 1
       endif
       i := i + 1
     endif
   endwhile
   if infoType = DS_REPL_INFO_METADATA_FOR_OBJ then
     pmsgOut^.pObjMetaData^.cNumEntries = j
   else
     pmsgOut^.pObjMetaData2^.cNumEntries = j
   endif
 endif
  
 /* DS_REPL_INFO_CURSORS_FOR_NC */
 if infoType = DS_REPL_INFO_CURSORS_FOR_NC then
  
   /* The NC root object must be specified */
   nc := object
  
   /* Parameter validation */
  
   if nc = null then
     return ERROR_INVALID_PARAMETER
   endif
  
   if not FullReplicaExists(nc) and
       not PartialGCReplicaExists(nc) then
     return ERROR_DS_DRA_BAD_NC
   endif
  
   utd := GetUpToDatenessVector(nc)
   i := baseIndex
   j := 0
   while i < utd.length
     pCursor := ADR(pmsgOut^.pCursors^.rgCursor[j])
     pCursor^.uuidSourceDsaInvocationID := utd[i].uuidDsa
     pCursor^.usnAttributeFilter := utd[i].usnHighPropUpdate
     i := i + 1
     j := j + 1
   endwhile
   pmsgOut^.pCursors^.cNumCursors := j
 endif
  
 /* DS_REPL_INFO_CURSORS_2_FOR_NC/ DS_REPL_INFO_CURSORS_3_FOR_NC */
 if infoType in {DS_REPL_INFO_CURSORS_2_FOR_NC,
     DS_REPL_INFO_CURSORS_3_FOR_NC} then
  
   /* The NC root object must be specified. */
   nc := object
  
   /* Parameter validation. */
   if (nc = null) then
     return ERROR_INVALID_PARAMETER
   endif
  
   if not FullReplicaExists(nc) and
       not PartialGCReplicaExists(nc) then
     return ERROR_DS_DRA_BAD_NC
   endif
  
   i := baseIndex
   j := 0
   utd := GetUpToDatenessVector(nc)
  
   /* A maximum of 1000 items will be sent in each call. */
   if utd.length - baseIndex - 1 > 1000 then
     endIndex = baseIndex + 1000
   else 
     endIndex = utd.length
   endif
  
   while i < endIndex
     if infoType = DS_REPL_INFO_CURSORS_2_FOR_NC then
       pCursor2 := ADR(pmsgOut^.pCursors2^.rgCursor[j])
       pCursor2^.uuidSourceDsaInvocationID := utd[i].uuidDsa
       pCursor2^.usnAttributeFilter := utd[i].usnHighPropUpdate
       pCursor2^.ftimeLastSyncSucess := utd[i].timeLastSyncSuccess
     else 
       pCursor3 := ADR(pmsgOut^.pCursor3^.rgCursor[j])
       pCursor3^.uuidSourceDsaInvocationID := utd[i].uuidDsa
       pCursor3^.usnAttributeFilter := utd[i].usnHighPropUpdate
       pCursor3^.ftimeLastSyncSucess := utd[i].timeLastSyncSuccess
       pCursor3^.pszSourceDsaDN := 
           GetDNFromInvocationID(utd[i].uuidDsa)
     endif
     j := j + 1
     i := i + 1
   endwhile
   if infoType = DS_REPL_INFO_CURSORS_2_NC then
      pmsgOut^.pCursors2^.cNumCursors := j
   else
     pmsgOut^.pCursors3^.cNumCursors := j
   endif
  
   if i < utd.length - 1 then
     /* Not all items could be sent back in this call, so save the
      * index of the first item to be sent in the next call. */ 
     If infoType = DS_REPL_INFO_CURSORS_2_NC then
        pmsgOut^.pCursor2^.dwEnumerationContext := i
     else
       pmsgOut^.pCursors3^.dwEnumerationContext := i
     endif
   else
     /* No more data is available. */
     If infoType = DS_REPL_INFO_CURSORS_2_NC then
        pmsgOut^.pCursor2^.dwEnumerationContext := 0xffffffff
     else
       pmsgOut^.pCursors3^.dwEnumerationContext := 0xffffffff
     endif
   endif
 endif
  
 /* DS_REPL_INFO_UPTODATE_VECTOR_V1 */
 if infoType = DS_REPL_INFO_UPTODATE_VECTOR_V1 then
  
   /* The NC root object must be specified. */
   nc := object
  
   /* Parameter validation. */
  
   if (nc = null) then
     return ERROR_INVALID_PARAMETER
   endif
  
  
   utd := GetUpToDatenessVector(nc)
   for i := 0 to utd.length - 1
     pCursor := ADR(pmsgOut^.pUpToDateVec^.rgCursors[i])
     pCursor^.uuidSourceDsaInvocationID := utd[i].uuidDsa
     pCursor^.usnAttributeFilter := utd[i].usnHighPropUpdate
   endfor
   pmsgOut^.pUpToDateVec^.cNumCursors := utd.length
 endif
  
 /* DS_REPL_INFO_METADATA_FOR_ATTR_VALUE/
  * DS_REPL_INFO_METADATA_2_FOR_ATTR_VALUE */
 if infoType in {DS_REPL_INFO_METADATA_FOR_ATTR_VALUE,
     DS_REPL_INFO_METADATA_2_FOR_ATTR_VALUE} then
  
   /* If the attribute name is specified it must be a link
    * attribute. */
   attrs := select all a in Link Attributes of object
   if (pmsgIn^.V2.pszAttributeNameValue ≠ null and 
        pmsgIn^.V2.pszAttributeNameValue not in attrs) then 
     return ERROR_DS_WRONG_LINKED_ATT_SYNTAX
   endif
  
   /* If the attribute name is not specified, replication state for a
    * link attribute of the object which has a value is returned. */
   if (pmsgIn^.V2.pszAttributeNameValue ≠ null) then 
     attr := pmsgIn^.V2.pszAttributeNameValue
   else 
     attrsSeq := select all a in attrs where
         GetAttrVals(object, a, true) ≠ null
     attr := attrsSeq[0]
   endif
  
   if attr ≠ null then
     valuesSeq := GetAttrVals(object, attr, true)
  
     /* If a start value has been specified, then start at the first 
      * occurrence of that value in the sequence of values, otherwise
      * start at the index determined from the enumeration context
      * which specifies the index of the next value to be returned. */
     if (pmsgIn^.V2.pszValueDN ≠ null and 
         Syntax(attr) = Object(DS-DN)) then
       i := index of pmsgIn^.V2.pszValueDN in valuesSeq
     else
       i := baseIndex
     endif
  
     j := 0
     while (i < valuesSeq.length and j < 1000)
       ls := LinkStamp(object, attr, valuesSeq[i])
       if infoType = DS_REPL_INFO_METADATA_FOR_ATTR_VALUE then
         pAttrValueMetaData :=
             ADR(pmsgOut^.pAttrValueMetaData^.rgMetadata[j])
         pAttrValueMetaData^.pszAttributeName := attr
         pAttrValueMetaData^.pszObjectDN := object!distinguishedName 
         if (Syntax(attr) = Object(DN-Binary) or 
             Syntax(attr) = Object(DN-String)) then
           pAttrValueMetaData^.cbData := 
               length of data associated with valuesSeq[i]
           pAttrValueMetaData^.pbData := data associated with
               valuesSeq[i]
         endif
         pAttrValueMetaData^.ftimeCreated := ls.timeCreated
         pAttrValueMetaData^.ftimeDeleted := ls.timeDeleted
         pAttrValueMetaData^.dwVersion := ls.dwVersion
         pAttrValueMetaData^.ftimeLastOriginatingChange :=
             ls.timeChanged
         pAttrValueMetaData^.uuidLastOriginatingDsaInvocationID := 
             ls.uuidOriginating
         pAttrValueMetaData^.usnOriginatingChange := ls.usnOriginating
         pAttrValueMetaData^.usnLocalChange :=
             implementation-specific value maintained for each link
             attribute value
       else
         pAttrValueMetaData2 :=
             ADR(pmsgOut^.pAttrValueMetaData2^.rgMetadata[j])
         pAttrValueMetaData2^.pszAttributeName := attr
         pAttrValueMetaData2^.pszObjectDN := object!distinguishedName 
         if (Syntax(attr) = Object(DN-Binary) or 
             Syntax(attr) = Object(DN-String)) then
           pAttrValueMetaData2^.cbData := 
               length of data associated with valuesSeq[i]
           pAttrValueMetaData2^.pbData := 
               data associated with valuesSeq[i]
         endif
         pAttrValueMetaData2^.ftimeCreated := ls.timeCreated
         pAttrValueMetaData2^.ftimeDeleted := ls.timeDeleted
         pAttrValueMetaData2^.dwVersion := ls.dwVersion
         pAttrValueMetaData2^.ftimeLastOriginatingChange :=
             ls.timeChanged
         pAttrValueMetaData2^.uuidLastOriginatingDsaInvocationID := 
             ls.uuidOriginating
         pAttrValueMetaData2^.usnOriginatingChange := 
             ls.usnOriginating
         pAttrValueMetaData2^.usnLocalChange := 
             implementation-specific value maintained for each
             link attribute value
         pAttrValueMetaData2^.pszLastOriginatingDsaDN := 
             GetDNFromInvocationID(ls.uuidOriginating)
       endif
  
       i := i + 1
       j := j + 1
     endwhile
  
     if infoType = DS_REPL_INFO_METADATA_FOR_ATTR_VALUE then
         if i < valuesSeq.length - 1 then
           /* Since there are more entries to be returned, save the index
            * of the first value to be returned in the next call. */
           pmsgOut^.pAttrValueMetaData^.dwEnumerationContext := i
         else
           /* No more data is available. */
           pmsgOut^.pAttrValueMetaData^.dwEnumerationContext := 
               0xffffffff
         endif
         pmsgOut^.pAttrValueMetaData^.cNumEntries = j
     else
         if i < valuesSeq.length - 1 then
           /* Since there are more entries to be returned, save the index
            * of the first value to be returned in the next call. */
           pmsgOut^.pAttrValueMetaData2^.dwEnumerationContext := i
         else
           /* No more data is available. */
           pmsgOut^.pAttrValueMetaData2^.dwEnumerationContext := 
               0xffffffff
         endif
         pmsgOut^.pAttrValueMetaData2^.cNumEntries = j
       endif
   endif
 endif
  
 /* DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES */
 if infoType = DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES then
   i := 0
   foreach t in dc.kccFailedConnections
     pConnectionFailure :=
         ADR(pmsgOut^.pConnectionFailures^.rgDsaFailure[i])
     pConnectionFailure^.pszDsaDN := t.DsaDN
     pConnectionFailure^.uuidDsaObjGuid := t.UUIDDsa
     pConnectionFailure^.fTimeFirstFailure := t.TimeFirstFailure
     pConnectionFailure^.cNumFailures := t.FailureCount
     pConnectionFailure^.dwLastResult := t.LastResult
     i := i + 1
   endfor
   pmsgOut^.pConnectionFailures^.cNumEntries := i
 endif
  
 /* DS_REPL_INFO_KCC_DSA_LINK_FAILURES */
 if infoType = DS_REPL_INFO_KCC_DSA_LINK_FAILURES then
   i := 0
   foreach t in dc.kccFailedLinks
     pConnectionLink := ADR(pmsgOut^.pLinkFailures^.rgDsaFailure[i])
     pConnectionLink^.pszDsaDN := t.DsaDN
     pConnectionLink^.uuidDsaObjGuid := t.UUIDDsa
     pConnectionLink^.fTimeFirstFailure := t.TimeFirstFailure
     pConnectionLink^.cNumFailures := t.FailureCount
     pConnectionLink^.dwLastResult := t.LastResult
     i := i + 1
   endfor
   pmsgOut^.pConnectionLinks^.cNumEntries := i
 endif
  
 /* DS_REPL_INFO_PENDING_OPS */
 if infoType = DS_REPL_INFO_PENDING_OPS then
   i := 0
   foreach t in dc.replicationQueue
     pPendingOp := ADR(pmsgOut^.pPendingOps^.rgPendingOp[i])
     pPendingOp^.fTimeEnqueued := t.TimeEnqueued
     pPendingOp^.ulSerailNumber := t.SerialNumber
     pPendingOp^.ulPriority := t.Priority
     pPendingOp^.OpType := t.OperationType
     pPendingOp^.ulOptions := t.Options
     pPendingOp^.pszNamingContext := t.NamingContext
     pPendingOp^.pszDsaDN := t.DsaDN
     pPendingOp^.pszDsaAddress := t.DsaAddress
     pPendingOp^.uuidNamingContextObjGuid := t.UUIDNC
     pPendingOp^.uuidDsaObjGuid := t.UUIDDsa
     i := i + 1
   endfor
   pmsgOut^.pPendingOps^.cNumPendingOps := i
   pmsgOut^.pPendingOps^.fTimeCurrentOpStarted := time when current 
       operation was started
 endif
  
 /* DS_REPL_INFO_CLIENT_CONTEXTS */
 if infoType = DS_REPL_INFO_CLIENT_CONTEXTS then
   i := 0
   foreach t in dc.rpcClientContexts
     pClientContext := ADR(pmsgOut^.pClientContexts^.rgContext[i])
     pClientContext^.hCtx := t.BindingContext
     pClientContext^.lReferenceCount := t.RefCount
     pClientContext^.fIsBound := t.IsBound
     pClientContext^.uuidClient := t.UUIDClient
     pClientContext^.timeLastUsed := t.TimeLastUsed
     pClientContext^.IPAddr := t.IPAddress
     pClientContext^.pid := t.PID
     i := i + 1
   endfor
   pmsgOut^.pClientContexts^.cNumContexts := i
 endif
  
 /* DS_REPL_INFO_SERVER_OUTGOING_CALLS */
 if infoType = DS_REPL_INFO_SERVER_OUTGOING_CALLS then
   i := 0
   foreach t in dc.rpcOutgoingContexts
     pOutgoingContext = 
         ADR(pmsgOut^.pServerOutgoingCalls^.rgCall[i])
     pOutgoingContext^.pszServerName := t.ServerName
     pOutgoingContext^.fIsHandleBound := t.IsBound
     pOutgoingContext^.fIsHandleFromCache := t.HandleFromCache
     pOutgoingContext^.fIsHandleInCache := t.HandleInCache
     pOutgoingContext^.dwThreadId := t.ThreadId
     pOutgoingContext^.dwBindingTimeoutMins := t.BindingTimeOut
     pOutgoingContext^.dstimeCreated := t.CreateTime
     pOutgoingContext^.dwCallType := t.CallType
     i := i + 1
   endfor
   pmsgOut^.pServerOutgoingCalls^.cNumCalls := i
 endif
  
 return 0