/* $Id$ */ package org.noreply.fancydress.type3; import org.noreply.fancydress.crypto.*; import org.noreply.fancydress.status.*; import org.noreply.fancydress.directory.*; import org.noreply.fancydress.misc.Util; import org.noreply.fancydress.type3.routing.*; import java.net.InetAddress; import java.util.*; public class PathSpec { Directory dir; PathComponent[] pathComponents; boolean singleLeg; /** * Class to hold one path component. * * A path component is either a component as defined in the mixminion * path-spec, like a hop by nickname, one or more random hops, or * gaussian hops; or it is the crossover point of a two-leg path spec. */ private class PathComponent { public final static double GAUSS_STD_DEV = 1.5; public final static int TYPE_NICKNAME = 1; public final static int TYPE_RANDOM = 2; public final static int TYPE_RANDOM_GAUSS = 3; public final static int TYPE_CROSSOVER = 4; int type; Server byNickname; int numRandoms; /** * Constructor. * * @param dir A mixminion directory * @param component the string representation of that component */ public PathComponent(Directory dir, String component) throws Mix3BadArgumentsIllegalPathSpecException { String c = component.trim(); if (c.equals("")) throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: empty path component"); char b = c.charAt(0); Integer integer; switch (b) { case '*' : try { integer = new Integer(c.substring(1)); } catch (NumberFormatException e) { throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: illegal path component '"+c+"'"); } type = TYPE_RANDOM; numRandoms = integer.intValue(); break; case '~' : try { integer = new Integer(c.substring(1)); } catch (NumberFormatException e) { throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: illegal path component '"+c+"'"); } type = TYPE_RANDOM_GAUSS; numRandoms = integer.intValue(); break; case '?' : if (c.length() > 1) throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: illegal path component '"+c+"'"); type = TYPE_RANDOM; numRandoms = 1; break; case ':' : if (c.length() > 1) throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: illegal path component '"+c+"'"); type = TYPE_CROSSOVER; break; default : Server server = dir.getServer(c); if (server == null) throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: Nickname '"+c+"' not found"); if (!server.isUseable()) throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: Nickname '"+c+"' is not useable"); byNickname = server; type = TYPE_NICKNAME; break; } } /** * If this path component is the crossover point. * * @return true if this path component is the crossover point. */ public boolean isCrossover() { return (type == TYPE_CROSSOVER); } /** * If this path component is a ByNickname component. * * @return true if this path component is a ByNickname component. */ public boolean isByNickname() { return (type == TYPE_NICKNAME); } /** * Get a number of hops statisfyi8ng this path component. * * @throws Error if this path spec is the crossover point. */ public Server[] getHops() { Server[] result; switch (type) { case TYPE_NICKNAME : result = new Server[1]; result[0] = byNickname; break; case TYPE_RANDOM : result = new Server[numRandoms]; break; case TYPE_RANDOM_GAUSS : double d = CryptoPrimitives.normal(numRandoms, GAUSS_STD_DEV); int num = (int) (d+0.5); result = new Server[ num<0 ? 0 : num ]; break; case TYPE_CROSSOVER : throw new Error("getHops() may not be called on the crossover point "); default : throw new Error("Unkown type "+type); } return result; } } /** * Split a string on an optional crossover point, given by the * separator token (colon). * * @param tokens the list of tokens to which the one or two new tokens are added. * @param s the string to split. */ private void splitCrossover(ArrayList tokens, String s) throws Mix3BadArgumentsIllegalPathSpecException { int indexOf = s.indexOf(':'); if (indexOf != -1) { String s1 = s.substring(0, indexOf).trim(); String s2 = s.substring(indexOf+1).trim(); if (s1.equals("")) throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: Empty hop before crossover point."); if (s2.equals("")) throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: Empty hop after crossover point."); tokens.add(s1.trim()); tokens.add(s2.trim()); } else { if (s.equals("")) throw new Mix3BadArgumentsIllegalPathSpecException("Invalid path: Empty hop in path."); tokens.add(s); } } /** * Parses a pathspec and returns an array of path components. * * @param dir A mixminion directory * @param path the path specification to parse * @return An array of PathComponent objects representing this pathSpec. */ private PathComponent[] parsePath(Directory dir, String path) throws Mix3BadArgumentsIllegalPathSpecException { ArrayList nicks = new ArrayList(); String[] tokens = Util.tokenize(path, ','); for (int i=0; iFoo,Bar,?:Baz,*2,~1". * * @param path given path */ public PathSpec(Directory dir, String pathSpec, boolean singleLeg) throws Mix3BadArgumentsIllegalPathSpecException { this.dir = dir; this.singleLeg = singleLeg; if (singleLeg) { /* single legs do not have a crossover point */ int crossover = pathSpec.indexOf(':'); if (crossover >= 0) throw new Mix3BadArgumentsIllegalPathSpecException("Path is not a valid path: crossover point specified in single leg path."); } else { /* full paths may have up to one specified crossover point */ int crossover = pathSpec.indexOf(':'); if (crossover >= 0) if (pathSpec.indexOf(':', crossover+1) >= 0) throw new Mix3BadArgumentsIllegalPathSpecException("Path is not a valid path: more than one crossover points specified."); } this.pathComponents = parsePath(dir, pathSpec); } /** * A class holding a list of Servers and a Crossover point location. */ private class ServerlistWithCrossover { public Server[] servers; public int crossoverPoint; public ServerlistWithCrossover(Server[] servers, int crossoverPoint) { this.servers = servers; this.crossoverPoint = crossoverPoint; } private void fillInRandoms() throws Mix3PathProblemException { Server[] recommended = dir.getRecommendedServers(); for (int i=servers.length-1; i>=0 ; i--) { if (servers[i] != null) continue; ArrayList s = new ArrayList(); for (int r=0; rpayload. * * We build as many paths as payload.numPackets() requires. * * Also we filter acceptable exit nodes according to the payload's * constraints. * * @param payload the payload * @return paths constructed from the PathSpec * @throws IllegalArgumentException if this is a single leg path spec. */ public Path[] getPath(Payload payload) throws Mix3PathProblemException { if (singleLeg) throw new IllegalArgumentException("getPath() may not be called for single leg path specs."); Server[] validExitHops = payload.filterExithops( dir.getRecommendedServers() ); Server[] recommendedExitHops = payload.filterExithops( dir.getRecommendedServers() ); Server exitHop; boolean randomExitHop = ! pathComponents[pathComponents.length-1].isByNickname(); if (! randomExitHop) { exitHop = pathComponents[ pathComponents.length-1 ].getHops()[0]; /* Check whether exitHop is valid. */ boolean exitHopOK = false; for (int j=0; j