From 1c126204d9add5e14c4ce3d60c9c89b1a5260133 Mon Sep 17 00:00:00 2001
From: pgamba <pgamba@localhost>
Date: Thu, 10 Dec 2009 16:57:24 +0000
Subject: [PATCH] Fix #4395 ECL cookie older than server changelog db trim is not detected

---
 opends/src/server/org/opends/server/replication/server/ECLServerHandler.java |   87 ++++++++++++++++++++++++++++++++++++-------
 1 files changed, 72 insertions(+), 15 deletions(-)

diff --git a/opends/src/server/org/opends/server/replication/server/ECLServerHandler.java b/opends/src/server/org/opends/server/replication/server/ECLServerHandler.java
index 08d80ed..d6635ea 100644
--- a/opends/src/server/org/opends/server/replication/server/ECLServerHandler.java
+++ b/opends/src/server/org/opends/server/replication/server/ECLServerHandler.java
@@ -686,24 +686,24 @@
       boolean allowUnknownDomains)
   throws DirectoryException
   {
-    HashMap<String,ServerState> startStates = new HashMap<String,ServerState>();
+    HashMap<String,ServerState> startStatesFromProvidedCookie =
+      new HashMap<String,ServerState>();
 
     ReplicationServer rs = this.replicationServer;
 
     // Parse the provided cookie and overwrite startState from it.
     if ((providedCookie != null) && (providedCookie.length()!=0))
-      startStates =
+      startStatesFromProvidedCookie =
         MultiDomainServerState.splitGenStateToServerStates(providedCookie);
 
     try
     {
-      // Now traverse all domains and build all the initial contexts :
-      // - the global one : dumpState()
-      // - the domain by domain ones : domainCtxts
       Iterator<ReplicationServerDomain> rsdi = rs.getDomainIterator();
 
-      // Creates the table that will contain the real-time info by domain.
+      // Creates the table that will contain the real-time info for each
+      // and every domain.
       HashSet<DomainContext> tmpSet = new HashSet<DomainContext>();
+      String missingDomains = "";
       int i =0;
       if (rsdi != null)
       {
@@ -738,18 +738,45 @@
           }
           else
           {
-            newDomainCtxt.startState = startStates.remove(rsd.getBaseDn());
+            // let's take the start state for this domain from the provided
+            // cookie
+            newDomainCtxt.startState =
+              startStatesFromProvidedCookie.remove(rsd.getBaseDn());
+
             if ((providedCookie==null)||(providedCookie.length()==0)
                 ||allowUnknownDomains)
             {
+              // when there is no cookie provided in the request,
+              // let's start traversing this domain from the beginning of
+              // what we have in the replication changelog
               if (newDomainCtxt.startState == null)
                 newDomainCtxt.startState = new ServerState();
             }
             else
+            {
+              // when there is a cookie provided in the request,
               if (newDomainCtxt.startState == null)
-                throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
-                    ERR_INVALID_COOKIE_FULL_RESYNC_REQUIRED.get(
-                        "missing " + rsd.getBaseDn()));
+              {
+                missingDomains += (rsd.getBaseDn() + ":;");
+                continue;
+              }
+              else if (!newDomainCtxt.startState.isEmpty())
+              {
+                // when the provided startState is older than the replication
+                // changelogdb start state, it means that the replication
+                // changelog db has been trimed and the cookie is not valid
+                // anymore.
+                if (newDomainCtxt.startState.cover(rsd.getStartState())==false)
+                {
+                  // the provided start
+                  throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+                      ERR_RESYNC_REQUIRED_TOO_OLD_DOMAIN_IN_PROVIDED_COOKIE.get(
+                          newDomainCtxt.rsd.getBaseDn()));
+                }
+              }
+            }
+
+            // Set the stop state for the domain from the eligibleCN
             newDomainCtxt.stopState = rsd.getEligibleState(eligibleCN);
           }
           newDomainCtxt.currentState = new ServerState();
@@ -774,18 +801,34 @@
           i++;
         }
       }
-      if (!startStates.isEmpty())
+
+      if (missingDomains.length()>0)
       {
+        // If there are domain missing in the provided cookie,
+        // the request is rejected and a full resync is required.
         throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
-            ERR_INVALID_COOKIE_FULL_RESYNC_REQUIRED.get(
-                "unknown " + startStates.toString()));
+          ERR_RESYNC_REQUIRED_MISSING_DOMAIN_IN_PROVIDED_COOKIE.get(
+              missingDomains + " .Possible cookie:" +
+              (providedCookie + missingDomains)));
+      }
+
+      if (!startStatesFromProvidedCookie.isEmpty())
+      {
+        // After reading all the knows domains from the provided cookie, there
+        // is one (or several) domain that are not currently configured.
+        // This domain has probably been removed or replication disabled on it.
+        // The request is rejected and full resync is required.
+        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
+            ERR_RESYNC_REQUIRED_UNKNOWN_DOMAIN_IN_PROVIDED_COOKIE.get(
+                startStatesFromProvidedCookie.toString()));
       }
       domainCtxts = tmpSet.toArray(new DomainContext[0]);
 
       // the next record from the DraftCNdb should be the one
       startCookie = providedCookie;
 
-      // Initializes all domain with the next(first) elligible message
+      // Initializes each and every domain with the next(first) eligible message
+      // from the domain.
       for (int j=0; j<domainCtxts.length; j++)
       {
         domainCtxts[j].getNextEligibleMessageForDomain(operationId);
@@ -794,6 +837,10 @@
           domainCtxts[j].active = false;
       }
     }
+    catch(DirectoryException de)
+    {
+      throw de;
+    }
     catch(Exception e)
     {
       TRACER.debugCaught(DebugLogLevel.ERROR, e);
@@ -939,8 +986,18 @@
     isPersistent  = startECLSessionMsg.isPersistent();
     lastDraftCN   = startECLSessionMsg.getLastDraftChangeNumber();
     searchPhase   = INIT_PHASE;
-    previousCookie = new MultiDomainServerState(
+    try
+    {
+      previousCookie = new MultiDomainServerState(
         startECLSessionMsg.getCrossDomainServerState());
+    }
+    catch(Exception e)
+    {
+      TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      throw new DirectoryException(
+          ResultCode.UNWILLING_TO_PERFORM,
+          ERR_INVALID_COOKIE_SYNTAX.get());
+    }
     excludedServiceIDs = startECLSessionMsg.getExcludedServiceIDs();
     replicationServer.disableEligibility(excludedServiceIDs);
     eligibleCN = replicationServer.getEligibleCN();

--
Gitblit v1.10.0