From 3781604cd04141db98b536a6176d8e30dc78a00b Mon Sep 17 00:00:00 2001 From: weijun Date: Wed, 18 Sep 2013 18:22:49 +0800 Subject: [PATCH] 8012615: Realm.getRealmsList returns realms list in wrong Reviewed-by: valeriep, xuelei --- .../classes/sun/security/krb5/Config.java | 34 +- .../classes/sun/security/krb5/Realm.java | 519 +++++------------- .../krb5/internal/CredentialsUtil.java | 20 +- test/sun/security/krb5/ParseCAPaths.java | 85 ++- test/sun/security/krb5/krb5-capaths.conf | 79 +-- 5 files changed, 260 insertions(+), 477 deletions(-) diff --git a/src/share/classes/sun/security/krb5/Config.java b/src/share/classes/sun/security/krb5/Config.java index 577188602..c2640adbc 100644 --- a/src/share/classes/sun/security/krb5/Config.java +++ b/src/share/classes/sun/security/krb5/Config.java @@ -225,19 +225,19 @@ public class Config { * and has no sub-key at all (given "forwardable" is defined, otherwise, * this method has no knowledge if it's a value name or a section name), */ - @SuppressWarnings("unchecked") public String get(String... keys) { - Vector v = get0(keys); + Vector v = getString0(keys); if (v == null) return null; return v.lastElement(); } /** * Gets all values for the specified keys. - * @see #get(java.lang.String[]) + * @throws IllegalArgumentException if any of the keys is illegal + * (See {@link #get}) */ public String getAll(String... keys) { - Vector v = get0(keys); + Vector v = getString0(keys); if (v == null) return null; StringBuilder sb = new StringBuilder(); boolean first = true; @@ -252,17 +252,37 @@ public class Config { return sb.toString(); } - // Internal method. Returns the vector of strings for keys. + /** + * Returns true if keys exists, can be either final string(s) or sub-stanza + * @throws IllegalArgumentException if any of the keys is illegal + * (See {@link #get}) + */ + public boolean exists(String... keys) { + return get0(keys) != null; + } + + // Returns final string value(s) for given keys. + @SuppressWarnings("unchecked") + private Vector getString0(String... keys) { + try { + return (Vector)get0(keys); + } catch (ClassCastException cce) { + throw new IllegalArgumentException(cce); + } + } + + // Internal method. Returns the value for keys, which can be a sub-stanza + // or final string value(s). // The only method (except for toString) that reads stanzaTable directly. @SuppressWarnings("unchecked") - private Vector get0(String... keys) { + private Object get0(String... keys) { Object current = stanzaTable; try { for (String key: keys) { current = ((Hashtable)current).get(key); if (current == null) return null; } - return (Vector)current; + return current; } catch (ClassCastException cce) { throw new IllegalArgumentException(cce); } diff --git a/src/share/classes/sun/security/krb5/Realm.java b/src/share/classes/sun/security/krb5/Realm.java index bc7705c4f..27bedc3ec 100644 --- a/src/share/classes/sun/security/krb5/Realm.java +++ b/src/share/classes/sun/security/krb5/Realm.java @@ -34,10 +34,8 @@ package sun.security.krb5; import sun.security.krb5.internal.Krb5; import sun.security.util.*; import java.io.IOException; -import java.util.StringTokenizer; -import java.util.Vector; -import java.util.Stack; -import java.util.EmptyStackException; +import java.util.*; + import sun.security.krb5.internal.util.KerberosString; /** @@ -50,7 +48,6 @@ import sun.security.krb5.internal.util.KerberosString; */ public class Realm implements Cloneable { private final String realm; // not null nor empty - private static boolean DEBUG = Krb5.DEBUG; public Realm(String name) throws RealmException { realm = parseRealm(name); @@ -233,90 +230,53 @@ public class Realm implements Cloneable { } } - /* - * First leg of realms parsing. Used by getRealmsList. - */ - private static String[] doInitialParse(String cRealm, String sRealm) - throws KrbException { - if (cRealm == null || sRealm == null){ - throw new KrbException(Krb5.API_INVALID_ARG); - } - if (DEBUG) { - System.out.println(">>> Realm doInitialParse: cRealm=[" - + cRealm + "], sRealm=[" +sRealm + "]"); - } - if (cRealm.equals(sRealm)) { - String[] retList = null; - retList = new String[1]; - retList[0] = new String(cRealm); - - if (DEBUG) { - System.out.println(">>> Realm doInitialParse: " - + retList[0]); - } - return retList; - } - return null; - } - /** * Returns an array of realms that may be traversed to obtain * a TGT from the initiating realm cRealm to the target realm * sRealm. *
- * There may be an arbitrary number of intermediate realms - * between cRealm and sRealm. The realms may be organized - * organized hierarchically, or the paths between them may be - * specified in the [capaths] stanza of the caller's - * Kerberos configuration file. The configuration file is consulted - * first. Then a hirarchical organization is assumed if no realms - * are found in the configuration file. + * This method would read [capaths] to create a path, or generate a + * hierarchical path if [capaths] does not contain a sub-stanza for cRealm + * or the sub-stanza does not contain a tag for sRealm. *
- * The returned list, if not null, contains cRealm as the first - * entry. sRealm is not included unless it is mistakenly listed - * in the configuration file as an intermediary realm. + * The returned list would never be null, and it always contains + * cRealm as the head entry. sRealm is not included as the tail. * - * @param cRealm the initiating realm - * @param sRealm the target realm - * @returns array of realms - * @thows KrbException + * @param cRealm the initiating realm, not null + * @param sRealm the target realm, not null, not equals to cRealm + * @returns array of realms including at least cRealm as the first + * element */ - public static String[] getRealmsList(String cRealm, String sRealm) - throws KrbException { - String[] retList = doInitialParse(cRealm, sRealm); - if (retList != null && retList.length != 0) { - return retList; - } - /* - * Try [capaths]. - */ - retList = parseCapaths(cRealm, sRealm); - if (retList != null && retList.length != 0) { - return retList; - } - /* - * Now assume the realms are organized hierarchically. - */ - retList = parseHierarchy(cRealm, sRealm); - return retList; + public static String[] getRealmsList(String cRealm, String sRealm) { + try { + // Try [capaths] + return parseCapaths(cRealm, sRealm); + } catch (KrbException ke) { + // Now assume the realms are organized hierarchically. + return parseHierarchy(cRealm, sRealm); } + } /** - * Parses the [capaths] stanza of the configuration file - * for a list of realms to traverse - * to obtain credentials from the initiating realm cRealm to - * the target realm sRealm. - * @param cRealm the initiating realm - * @param sRealm the target realm - * @returns array of realms - * @ throws KrbException - */ - - /* - * parseCapaths works for a capaths organized such that - * for a given client realm C there is a tag C that - * contains subtags Ci ... Cn that completely define intermediate - * realms from C to target T. For example: + * Parses the [capaths] stanza of the configuration file for a + * list of realms to traverse to obtain credentials from the + * initiating realm cRealm to the target realm sRealm. + * + * For a given client realm C there is a tag C in [capaths] whose + * subtag S has a value which is a (possibly partial) path from C + * to S. When the path is partial, it contains only the tail of the + * full path. Values of other subtags will be used to build the full + * path. The value "." means a direct path from C to S. If realm S + * does not appear as a subtag, there is no path defined here. + * + * The implementation ignores all values which equals to C or S, or + * a "." in multiple values, or any duplicated realm names. + * + * When a path value has more than two realms, they can be specified + * with multiple key-value pairs each having a single value, but the + * order must not change. + * + * For example: * * [capaths] * TIVOLI.COM = { @@ -325,357 +285,130 @@ public class Realm implements Cloneable { * LDAPCENTRAL.NET = . * } * - * The tag TIVOLI.COM contains subtags IBM.COM, IBM_LDAPCENTRAL.COM - * and LDAPCENTRAL.NET that completely define the path from TIVOLI.COM - * to IBM.COM (TIVOLI.COM->LADAPCENTRAL.NET->IBM_LDAPCENTRAL.COM->IBM - * or TIVOLI.COM->MOONLITE.ORG->IBM.COM). + * TIVOLI.COM has a direct path to LDAPCENTRAL.NET, which has a direct + * path to IBM_LDAPCENTRAL.COM. It also has a partial path to IBM.COM + * being "IBM_LDAPCENTRAL.COM MOONLITE.ORG". Merging these info together, + * a full path from TIVOLI.COM to IBM.COM will be + * + * TIVOLI.COM -> LDAPCENTRAL.NET -> IBM_LDAPCENTRAL.COM + * -> IBM_LDAPCENTRAL.COM -> MOONLITE.ORG + * + * Please note the sRealm IBM.COM does not appear in the path. * - * A direct path is assumed for an intermediary whose entry is not - * "closed" by a "." In the above example, TIVOLI.COM is assumed - * to have a direct path to MOONLITE.ORG and MOONLITE.COM - * in turn to IBM.COM. + * @param cRealm the initiating realm + * @param sRealm the target realm, not the same as cRealm + * @returns array of realms including at least cRealm as the first + * element + * @throws KrbException if the config does not contain a sub-stanza + * for cRealm in [capaths] or the sub-stanza does not contain + * sRealm as a tag */ + private static String[] parseCapaths(String cRealm, String sRealm) + throws KrbException { - private static String[] parseCapaths(String cRealm, String sRealm) throws KrbException { - String[] retList = null; - - Config cfg = null; - try { - cfg = Config.getInstance(); - } catch (Exception exc) { - if (DEBUG) { - System.out.println ("Configuration information can not be " + - "obtained " + exc.getMessage()); - } - return null; - } - - String intermediaries = cfg.getAll("capaths", cRealm, sRealm); + // This line could throw a KrbException + Config cfg = Config.getInstance(); - if (intermediaries == null) { - if (DEBUG) { - System.out.println(">>> Realm parseCapaths: no cfg entry"); - } - return null; + if (!cfg.exists("capaths", cRealm, sRealm)) { + throw new KrbException("No conf"); } - String tempTarget = null, tempRealm = null; - Stack iStack = new Stack<>(); - - /* - * The half-established reversed-path, starting from the final target - * (sRealm), each item can be connected to by the next one. - * Might contains wrong item, if found, a bad track is performed - */ - Vector tempList = new Vector<>(8, 8); - tempList.add(sRealm); - - int count = 0; // For debug only - tempTarget = sRealm; - - out: do { - if (DEBUG) { - count++; - System.out.println(">>> Realm parseCapaths: loop " + - count + ": target=" + tempTarget); - } - - if (intermediaries != null && - !intermediaries.equals(".") && - !intermediaries.equals(cRealm)) { - if (DEBUG) { - System.out.println(">>> Realm parseCapaths: loop " + - count + ": intermediaries=[" + - intermediaries + "]"); - } + LinkedList path = new LinkedList<>(); - /* - * We have one or more space-separated intermediary realms. - * Stack them. A null is always added between intermedies of - * different targets. When this null is popped, it means none - * of the intermedies for this target is useful (because of - * infinite loop), the target is then removed from the partial - * tempList, and the next possible intermediary is tried. - */ - iStack.push(null); - String[] ints = intermediaries.split("\\s+"); - for (int i = ints.length-1; i>=0; i--) - { - tempRealm = ints[i]; - if (tempRealm.equals(PrincipalName.REALM_COMPONENT_SEPARATOR_STR)) { - break out; - } - if (!tempList.contains(tempRealm)) { - iStack.push(tempRealm); - if (DEBUG) { - System.out.println(">>> Realm parseCapaths: loop " + - count + - ": pushed realm on to stack: " + - tempRealm); - } - } else if (DEBUG) { - System.out.println(">>> Realm parseCapaths: loop " + - count + - ": ignoring realm: [" + - tempRealm + "]"); - } - } - } else { - if (DEBUG) { - System.out.println(">>> Realm parseCapaths: loop " + - count + - ": no intermediaries"); - } + String head = sRealm; + while (true) { + String value = cfg.getAll("capaths", cRealm, head); + if (value == null) { break; } - - /* - * Get next intermediary realm from the stack - */ - - try { - while ((tempTarget = iStack.pop()) == null) { - tempList.removeElementAt(tempList.size()-1); - if (DEBUG) { - System.out.println(">>> Realm parseCapaths: backtrack, remove tail"); - } + String[] more = value.split("\\s+"); + boolean changed = false; + for (int i=more.length-1; i>=0; i--) { + if (path.contains(more[i]) + || more[i].equals(".") + || more[i].equals(cRealm) + || more[i].equals(sRealm) + || more[i].equals(head)) { + // Ignore invalid values + continue; } - } catch (EmptyStackException exc) { - tempTarget = null; - } - - if (tempTarget == null) { - /* - * No more intermediaries. We're done. - */ - break; - } - - tempList.add(tempTarget); - - if (DEBUG) { - System.out.println(">>> Realm parseCapaths: loop " + count + - ": added intermediary to list: " + - tempTarget); - } - - intermediaries = cfg.getAll("capaths", cRealm, tempTarget); - - } while (true); - - if (tempList.isEmpty()) { - return null; - } - - // From (SREALM, T1, T2) to (CREALM, T2, T1) - retList = new String[tempList.size()]; - retList[0] = cRealm; - for (int i=1; i>> Realm parseCapaths [" + i + - "]=" + retList[i]); + changed = true; + path.addFirst(more[i]); } + if (!changed) break; + head = path.getFirst(); } - - return retList; - } + path.addFirst(cRealm); + return path.toArray(new String[path.size()]); + } /** * Build a list of realm that can be traversed * to obtain credentials from the initiating realm cRealm * for a service in the target realm sRealm. * @param cRealm the initiating realm - * @param sRealm the target realm - * @returns array of realms - * @throws KrbException + * @param sRealm the target realm, not the same as cRealm + * @returns array of realms including cRealm as the first element */ - private static String[] parseHierarchy(String cRealm, String sRealm) - throws KrbException - { - String[] retList = null; - - // Parse the components and determine common part, if any. - - String[] cComponents = null; - String[] sComponents = null; - - StringTokenizer strTok = - new StringTokenizer(cRealm, - PrincipalName.REALM_COMPONENT_SEPARATOR_STR); - - // Parse cRealm - - int cCount = strTok.countTokens(); - cComponents = new String[cCount]; - - for (cCount = 0; strTok.hasMoreTokens(); cCount++) { - cComponents[cCount] = strTok.nextToken(); - } - - if (DEBUG) { - System.out.println(">>> Realm parseHierarchy: cRealm has " + - cCount + " components:"); - int j = 0; - while (j < cCount) { - System.out.println(">>> Realm parseHierarchy: " + - "cComponents["+j+"]=" + cComponents[j++]); - } - } - - // Parse sRealm - - strTok = new StringTokenizer(sRealm, - PrincipalName.REALM_COMPONENT_SEPARATOR_STR); - - int sCount = strTok.countTokens(); - sComponents = new String[sCount]; - - for (sCount = 0; strTok.hasMoreTokens(); sCount++) { - sComponents[sCount] = strTok.nextToken(); - } - - if (DEBUG) { - System.out.println(">>> Realm parseHierarchy: sRealm has " + - sCount + " components:"); - int j = 0; - while (j < sCount) { - System.out.println(">>> Realm parseHierarchy: sComponents["+j+ - "]=" + sComponents[j++]); - } - } - - // Determine common components, if any. - - int commonComponents = 0; - - //while (sCount > 0 && cCount > 0 && - // sComponents[--sCount].equals(cComponents[--cCount])) + private static String[] parseHierarchy(String cRealm, String sRealm) { - for (sCount--, cCount--; sCount >=0 && cCount >= 0 && - sComponents[sCount].equals(cComponents[cCount]); - sCount--, cCount--) { - commonComponents++; - } - - int cCommonStart = -1; - int sCommonStart = -1; + String[] cComponents = cRealm.split("\\."); + String[] sComponents = sRealm.split("\\."); - int links = 0; + int cPos = cComponents.length; + int sPos = sComponents.length; - if (commonComponents > 0) { - sCommonStart = sCount+1; - cCommonStart = cCount+1; - - // components from common to ancestors - links += sCommonStart; - links += cCommonStart; - } else { - links++; + boolean hasCommon = false; + for (sPos--, cPos--; sPos >=0 && cPos >= 0 && + sComponents[sPos].equals(cComponents[cPos]); + sPos--, cPos--) { + hasCommon = true; } - if (DEBUG) { - if (commonComponents > 0) { - System.out.println(">>> Realm parseHierarchy: " + - commonComponents + " common component" + - (commonComponents > 1 ? "s" : " ")); - - System.out.println(">>> Realm parseHierarchy: common part " - + - "in cRealm (starts at index " + - cCommonStart + ")"); - System.out.println(">>> Realm parseHierarchy: common part in sRealm (starts at index " + - sCommonStart + ")"); - - - String commonPart = substring(cRealm, cCommonStart); - System.out.println(">>> Realm parseHierarchy: common part in cRealm=" + - commonPart); + // For those with common components: + // length pos + // SITES1.SALES.EXAMPLE.COM 4 1 + // EVERYWHERE.EXAMPLE.COM 3 0 - commonPart = substring(sRealm, sCommonStart); - System.out.println(">>> Realm parseHierarchy: common part in sRealm=" + - commonPart); + // For those without common components: + // length pos + // DEVEL.EXAMPLE.COM 3 2 + // PROD.EXAMPLE.ORG 3 2 - } else - System.out.println(">>> Realm parseHierarchy: no common part"); - } + LinkedList path = new LinkedList<>(); - if (DEBUG) { - System.out.println(">>> Realm parseHierarchy: total links=" + links); + // Un-common ones for client side + for (int i=0; i<=cPos; i++) { + path.addLast(subStringFrom(cComponents, i)); } - retList = new String[links]; - - retList[0] = new String(cRealm); - - if (DEBUG) { - System.out.println(">>> Realm parseHierarchy A: retList[0]=" + - retList[0]); + // Common one + if (hasCommon) { + path.addLast(subStringFrom(cComponents, cPos+1)); } - // For an initiator realm A.B.C.D.COM, - // build a list krbtgt/B.C.D.COM@A.B.C.D.COM up to the common part, - // ie the issuer realm is the immediate descendant - // of the target realm. - - String cTemp = null, sTemp = null; - int i; - for (i = 1, cCount = 0; i < links && cCount < cCommonStart; cCount++) { - sTemp = substring(cRealm, cCount+1); - //cTemp = substring(cRealm, cCount); - retList[i++] = new String(sTemp); - - if (DEBUG) { - System.out.println(">>> Realm parseHierarchy B: retList[" + - (i-1) +"]="+retList[i-1]); - } + // Un-common ones for server side + for (int i=sPos; i>=0; i--) { + path.addLast(subStringFrom(sComponents, i)); } + // Remove sRealm from path. Note that it might be added at last loop + // or as a common component, if sRealm is a parent of cRealm + path.removeLast(); - for (sCount = sCommonStart; i < links && sCount - 1 > 0; sCount--) { - sTemp = substring(sRealm, sCount-1); - //cTemp = substring(sRealm, sCount); - retList[i++] = new String(sTemp); - if (DEBUG) { - System.out.println(">>> Realm parseHierarchy D: retList[" + - (i-1) +"]="+retList[i-1]); - } - } - - return retList; + return path.toArray(new String[path.size()]); } - private static String substring(String realm, int componentIndex) - { - int i = 0 , j = 0, len = realm.length(); - - while(i < len && j != componentIndex) { - if (realm.charAt(i++) != PrincipalName.REALM_COMPONENT_SEPARATOR) - continue; - j++; - } - - return realm.substring(i); - } - - static int getRandIndex(int arraySize) { - return (int)(Math.random() * 16384.0) % arraySize; - } - - static void printNames(String[] names) { - if (names == null || names.length == 0) - return; - - int len = names.length; - int i = 0; - System.out.println("List length = " + len); - while (i < names.length) { - System.out.println("["+ i +"]=" + names[i]); - i++; + /** + * Creates a realm name using components from the given postion. + * For example, subStringFrom({"A", "B", "C"}, 1) is "B.C". + */ + private static String subStringFrom(String[] components, int from) { + StringBuilder sb = new StringBuilder(); + for (int i=from; i>> Credentials acquireServiceCreds: no realms list"); - } - return null; - } - int i = 0, k = 0; Credentials cTgt = null, newTgt = null, theTgt = null; PrincipalName tempService = null; @@ -206,16 +198,14 @@ public class CredentialsUtil { if (newTgt == null) { if (DEBUG) { System.out.println(">>> Credentials acquireServiceCreds: " - + "no tgt; searching backwards"); + + "no tgt; searching thru capath"); } /* - * No tgt found. Try to get one for a - * realm as close to the target as possible. - * That means traversing the realms list backwards. + * No tgt found. Let's go thru the realms list one by one. */ - for (newTgt = null, k = realms.length - 1; - newTgt == null && k > i; k--) { + for (newTgt = null, k = i+1; + newTgt == null && k < realms.length; k++) { tempService = PrincipalName.tgsService(realms[k], realms[i]); if (DEBUG) { System.out.println( diff --git a/test/sun/security/krb5/ParseCAPaths.java b/test/sun/security/krb5/ParseCAPaths.java index f8c30bd7b..18b434708 100644 --- a/test/sun/security/krb5/ParseCAPaths.java +++ b/test/sun/security/krb5/ParseCAPaths.java @@ -22,7 +22,7 @@ */ /* * @test - * @bug 6789935 + * @bug 6789935 8012615 * @run main/othervm ParseCAPaths * @summary cross-realm capath search error */ @@ -35,37 +35,75 @@ public class ParseCAPaths { public static void main(String[] args) throws Exception { System.setProperty("java.security.krb5.conf", System.getProperty("test.src", ".") +"/krb5-capaths.conf"); - //System.setProperty("sun.security.krb5.debug", "true"); - // Standard example + // MIT check("ANL.GOV", "TEST.ANL.GOV", "ANL.GOV"); check("ANL.GOV", "ES.NET", "ANL.GOV"); check("ANL.GOV", "PNL.GOV", "ANL.GOV", "ES.NET"); check("ANL.GOV", "NERSC.GOV", "ANL.GOV", "ES.NET"); + check("NERSC.GOV", "TEST.ANL.GOV", "NERSC.GOV", "ES.NET", "ANL.GOV"); + + // RedHat + // 3.6.2.1. Configuring a Shared Hierarchy of Names + check("AA.EXAMPLE.COM", "BB.EXAMPLE.COM", + "AA.EXAMPLE.COM", "EXAMPLE.COM"); + check("SITE1.SALES.EXAMPLE.COM", "EVERYWHERE.EXAMPLE.COM", + "SITE1.SALES.EXAMPLE.COM", "SALES.EXAMPLE.COM", + "EXAMPLE.COM"); + check("DEVEL.EXAMPLE.COM", "PROD.EXAMPLE.ORG", + "DEVEL.EXAMPLE.COM", "EXAMPLE.COM", "COM", + "ORG", "EXAMPLE.ORG"); + // 3.6.2.2. Configuring Paths in krb5.conf + check("A.EXAMPLE.COM", "B.EXAMPLE.COM", "A.EXAMPLE.COM"); + check("A.EXAMPLE.COM", "C.EXAMPLE.COM", + "A.EXAMPLE.COM", "B.EXAMPLE.COM"); + check("A.EXAMPLE.COM", "D.EXAMPLE.COM", + "A.EXAMPLE.COM", "B.EXAMPLE.COM", "C.EXAMPLE.COM"); + + // The original JDK example + check("TIVOLI.COM", "IBM.COM", "TIVOLI.COM", "LDAPCENTRAL.NET", + "IBM_LDAPCENTRAL.COM", "MOONLITE.ORG"); + // Hierachical - check("N1.N.COM", "N2.N.COM", "N1.N.COM", "N.COM"); // 2 common - check("N1.N.COM", "N2.N3.COM", "N1.N.COM", "N.COM", // 1 common + check("N1.N.COM", "N2.N.COM", "N1.N.COM", "N.COM"); + check("N1.N.COM", "N2.N3.COM", "N1.N.COM", "N.COM", "COM", "N3.COM"); - check("N1.COM", "N2.COM", "N1.COM", "COM"); // 1 common - check("N1", "N2", "N1"); // 0 common - // Extra garbages - check("A1.COM", "A4.COM", "A1.COM", "A2.COM"); + check("N1.COM", "N2.COM", "N1.COM", "COM"); + check("N1", "N2", "N1"); + check("N1.COM", "N2.ORG", "N1.COM", "COM", "ORG"); + check("N1.N.COM", "N.COM", "N1.N.COM"); + check("X.N1.N.COM", "N.COM", "X.N1.N.COM", "N1.N.COM"); + check("N.COM", "N1.N.COM", "N.COM"); + check("N.COM", "X.N1.N.COM", "N.COM", "N1.N.COM"); + check("A.B.C", "D.E.F", "A.B.C", "B.C", "C", "F", "E.F"); + + // Full path + check("A1.COM", "A2.COM", "A1.COM"); + check("A1.COM", "A3.COM", "A1.COM", "A2.COM"); + check("A1.COM", "A4.COM", "A1.COM", "A2.COM", "A3.COM"); + + // Shortest path + check("B1.COM", "B2.COM", "B1.COM"); check("B1.COM", "B3.COM", "B1.COM", "B2.COM"); + check("B1.COM", "B4.COM", "B1.COM", "B2.COM", "B3.COM"); + // Missing is "." + check("C1.COM", "C2.COM", "C1.COM", "COM"); check("C1.COM", "C3.COM", "C1.COM", "C2.COM"); - // Multiple path - check("D1.COM", "D4.COM", "D1.COM", "D2.COM"); - check("E1.COM", "E4.COM", "E1.COM", "E2.COM"); - check("F1.COM", "F4.COM", "F1.COM", "F9.COM"); - // Infinite loop - check("G1.COM", "G3.COM", "G1.COM", "COM"); - check("H1.COM", "H3.COM", "H1.COM"); + + // cRealm = . + check("D1.COM", "D2.COM", "D1.COM"); + + // Bad cases + check("E1.COM", "E2.COM", "E1.COM"); + check("E1.COM", "E3.COM", "E1.COM", "E4.COM"); + check("G1.COM", "G3.COM", "G1.COM", "G2.COM"); check("I1.COM", "I4.COM", "I1.COM", "I5.COM"); - // J2=J1 is the same as J2=. - check("J1.COM", "J2.COM", "J1.COM"); + // 7019384 check("A9.PRAGUE.XXX.CZ", "SERVIS.XXX.CZ", "A9.PRAGUE.XXX.CZ", "PRAGUE.XXX.CZ", "ROOT.XXX.CZ"); + if (failed != null) { throw failed; } @@ -75,6 +113,7 @@ public class ParseCAPaths { try { check2(from, to, paths); } catch (Exception e) { + System.out.println(" " + e.getMessage()); failed = e; } } @@ -84,18 +123,14 @@ public class ParseCAPaths { System.out.println(from + " -> " + to); System.out.println(" expected: " + Arrays.toString(paths)); String[] result = Realm.getRealmsList(from, to); - System.out.println(" result: " + Arrays.toString(result)); - if (result == null) { - if (paths.length == 0) { - // OK - } else { - throw new Exception("Shouldn't have a valid path."); - } + if (result == null || result.length == 0) { + throw new Exception("There is always a valid path."); } else if(result.length != paths.length) { throw new Exception("Length of path not correct"); } else { for (int i=0; i