Addressbook: Add tests for Daemon to read local subscription file

More HostTxtEntry 'remove' methods and tests
This commit is contained in:
zzz
2016-04-22 12:58:27 +00:00
parent 4f262f6140
commit 55de82bb50
5 changed files with 151 additions and 10 deletions

View File

@@ -177,6 +177,18 @@ class AddressBook implements Iterable<Map.Entry<String, HostTxtEntry>> {
this.subFile = null;
}
/**
* Test only.
*
* @param testsubfile path to a file containing the simulated fetch of a subscription
* @since 0.9.26
*/
public AddressBook(String testsubfile) {
this.location = testsubfile;
this.addresses = null;
this.subFile = new File(testsubfile);
}
/**
* Return an iterator over the addresses in the AddressBook.
* @since 0.8.7

View File

@@ -737,7 +737,24 @@ public class Daemon {
* others are ignored.
*/
public static void main(String[] args) {
_instance.run(args);
if (args != null && args.length > 0 && args[0].equals("test"))
_instance.test(args);
else
_instance.run(args);
}
/** @since 0.9.26 */
private static void test(String[] args) {
Properties ctxProps = new Properties();
String PROP_FORCE = "i2p.naming.blockfile.writeInAppContext";
ctxProps.setProperty(PROP_FORCE, "true");
I2PAppContext ctx = new I2PAppContext(ctxProps);
NamingService ns = getNamingService("hosts.txt");
File published = new File("test-published.txt");
Log log = new Log(new File("test-log.txt"));
SubscriptionList subscriptions = new SubscriptionList("test-sub.txt");
update(ns, published, subscriptions, log);
ctx.logManager().flush();
}
public void run(String[] args) {

View File

@@ -71,9 +71,16 @@ class HostTxtEntry {
* @throws IllegalArgumentException on dup key in sprops and other errors
*/
public HostTxtEntry(String name, String dest, String sprops) throws IllegalArgumentException {
this.name = name;
this.dest = dest;
this.props = parseProps(sprops);
this(name, dest, parseProps(sprops));
}
/**
* A 'remove' entry. Name and Dest will be null.
* @param sprops line part after the #!, non-null
* @throws IllegalArgumentException on dup key in sprops and other errors
*/
public HostTxtEntry(String sprops) throws IllegalArgumentException {
this(null, null, parseProps(sprops));
}
/**
@@ -132,19 +139,19 @@ class HostTxtEntry {
}
/**
* Write as a "remove" line #!olddest=dest#oldname=name#k1=v1#k2=v2...]
* Write as a "remove" line #!dest=dest#name=name#k1=v1#sig=sig...]
* Includes newline.
* Must have been constructed with non-null properties.
*/
public void writeRemove(BufferedWriter out) throws IOException {
if (props == null)
throw new IllegalStateException();
props.setProperty(PROP_OLDNAME, name);
props.setProperty(PROP_OLDDEST, dest);
props.setProperty(PROP_NAME, name);
props.setProperty(PROP_DEST, dest);
writeProps(out, false, false);
out.newLine();
props.remove(PROP_OLDNAME);
props.remove(PROP_OLDDEST);
props.remove(PROP_NAME);
props.remove(PROP_DEST);
}
/**
@@ -265,6 +272,50 @@ class HostTxtEntry {
return rv;
}
/**
* Verify with the "dest" property's public key using the "sig" property
*/
public boolean hasValidRemoveSig() {
if (props == null)
return false;
boolean rv = false;
// don't cache result
if (true) {
StringWriter buf = new StringWriter(1024);
String sig = props.getProperty(PROP_SIG);
String olddest = props.getProperty(PROP_DEST);
if (sig == null || olddest == null)
return false;
try {
writeProps(buf, true, true);
} catch (IOException ioe) {
// won't happen
return false;
}
byte[] sdata = Base64.decode(sig);
if (sdata == null)
return false;
Destination d;
try {
d = new Destination(olddest);
} catch (DataFormatException dfe) {
return false;
}
SigningPublicKey spk = d.getSigningPublicKey();
SigType type = spk.getType();
if (type == null)
return false;
Signature s;
try {
s = new Signature(type, sdata);
} catch (IllegalArgumentException iae) {
return false;
}
rv = DSAEngine.getInstance().verifySignature(s, DataHelper.getUTF8(buf.toString()), spk);
}
return rv;
}
@Override
public int hashCode() {
return dest.hashCode();
@@ -299,6 +350,30 @@ class HostTxtEntry {
signIt(spk, PROP_OLDSIG);
}
/**
* Sign as a "remove" line #!dest=dest#name=name#k1=v1#sig=sig...]
*/
public void signRemove(SigningPrivateKey spk) {
if (props == null)
throw new IllegalStateException();
if (props.containsKey(PROP_SIG))
throw new IllegalStateException();
props.setProperty(PROP_NAME, name);
props.setProperty(PROP_DEST, dest);
StringWriter buf = new StringWriter(1024);
try {
writeProps(buf, false, false);
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
props.remove(PROP_NAME);
props.remove(PROP_DEST);
Signature s = DSAEngine.getInstance().sign(DataHelper.getUTF8(buf.toString()), spk);
if (s == null)
throw new IllegalArgumentException("sig failed");
props.setProperty(PROP_SIG, s.toBase64());
}
/**
* for testing only
* @param sigprop The signature property to set
@@ -384,6 +459,22 @@ class HostTxtEntry {
throw new IllegalStateException("Inner fail 2");
if (!he2.hasValidSig())
throw new IllegalStateException("Outer fail 2");
// 'remove' tests (corrupts earlier sigs)
he.getProps().remove(PROP_SIG);
he.signRemove(priv);
//out.write("Remove entry:\n");
sw = new StringWriter(1024);
buf = new BufferedWriter(sw);
he.writeRemove(buf);
buf.flush();
out.write(sw.toString());
out.flush();
line = sw.toString().substring(2).trim();
HostTxtEntry he3 = new HostTxtEntry(line);
if (!he3.hasValidRemoveSig())
throw new IllegalStateException("Remove verify fail");
//out.write("Test passed\n");
//out.flush();
}

View File

@@ -75,7 +75,10 @@ class SubscriptionIterator implements Iterator<AddressBook> {
*/
public AddressBook next() {
Subscription sub = this.subIterator.next();
if (sub.getLastFetched() + this.delay < I2PAppContext.getGlobalContext().clock().now() &&
if (sub.getLocation().startsWith("file:")) {
// test only
return new AddressBook(sub.getLocation().substring(5));
} else if (sub.getLastFetched() + this.delay < I2PAppContext.getGlobalContext().clock().now() &&
I2PAppContext.getGlobalContext().portMapper().getPort(PortMapper.SVC_HTTP_PROXY) >= 0 &&
!I2PAppContext.getGlobalContext().getBooleanProperty("i2p.vmCommSystem")) {
//System.err.println("Fetching addressbook from " + sub.getLocation());

View File

@@ -100,6 +100,24 @@ class SubscriptionList implements Iterable<AddressBook> {
}
}
/**
* Testing only.
*
* @param hoststxt path to a local file used as the test 'subscription' input
* @since 0.9.26
*/
public SubscriptionList(String hoststxt) {
File dummy = new File("/dev/null");
this.etagsFile = dummy;
this.lastModifiedFile = dummy;
this.lastFetchedFile = dummy;
this.delay = 0;
this.proxyHost = "127.0.0.1";
this.proxyPort = 4444;
Subscription sub = new Subscription("file:" + hoststxt, null, null, null);
this.subscriptions = Collections.singletonList(sub);
}
/**
* Return an iterator over the AddressBooks represented by the Subscriptions
* in this SubscriptionList.