use of net.i2p.data.Destination in project i2p.i2p by i2p.
the class HostTxtEntry method hasValidRemoveSig.
/**
* 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;
}
use of net.i2p.data.Destination in project i2p.i2p by i2p.
the class NamingService method lookupBase64.
/**
* If the host name is a valid Base64 encoded destination, return the
* decoded Destination. Useful as a "fallback" in custom naming
* implementations.
* This is misnamed as it isn't a "lookup" at all, but
* a simple conversion from a Base64 string to a Destination.
*
* @param hostname 516+ character Base 64
* @return Destination or null on error
*/
protected Destination lookupBase64(String hostname) {
try {
Destination result = new Destination();
result.fromBase64(hostname);
return result;
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Bad B64 dest [" + hostname + "]", dfe);
return null;
}
}
use of net.i2p.data.Destination in project i2p.i2p by i2p.
the class Daemon method update.
/**
* @param knownNames only non-null if router book is a text file
* @param publishedNS only non-null if we have a published address book
* @since 0.9.33 split out from above
*/
private static void update(NamingService router, Set<String> knownNames, NamingService publishedNS, AddressBook addressbook, Iterator<Map.Entry<String, HostTxtEntry>> iter, Log log) {
long start = System.currentTimeMillis();
int old = 0, nnew = 0, invalid = 0, conflict = 0, total = 0;
int deleted = 0;
while (iter.hasNext()) {
Map.Entry<String, HostTxtEntry> entry = iter.next();
total++;
// may be null for 'remove' entries
String key = entry.getKey();
boolean isKnown;
// NOT set for text file NamingService
Destination oldDest;
if (knownNames != null) {
oldDest = null;
isKnown = key != null ? knownNames.contains(key) : null;
} else {
oldDest = key != null ? router.lookup(key) : null;
isKnown = oldDest != null;
}
try {
HostTxtEntry he = entry.getValue();
Properties hprops = he.getProps();
boolean mustValidate = MUST_VALIDATE || hprops != null;
String action = hprops != null ? hprops.getProperty(HostTxtEntry.PROP_ACTION) : null;
if (key == null && !he.hasValidRemoveSig()) {
if (log != null) {
log.append("Bad signature of action " + action + " for key " + hprops.getProperty(HostTxtEntry.PROP_NAME) + ". From: " + addressbook.getLocation());
}
invalid++;
} else if (key != null && mustValidate && !he.hasValidSig()) {
if (log != null) {
log.append("Bad signature of action " + action + " for key " + key + ". From: " + addressbook.getLocation());
}
invalid++;
} else if (action != null || !isKnown) {
if (key != null && AddressBook.isValidKey(key)) {
Destination dest = new Destination(he.getDest());
Properties props = new OrderedProperties();
props.setProperty("s", addressbook.getLocation());
boolean allowExistingKeyInPublished = false;
if (mustValidate) {
// sig checked above
props.setProperty("v", "true");
}
if (hprops != null) {
// merge in all the received properties
for (Map.Entry<Object, Object> e : hprops.entrySet()) {
// Add prefix to indicate received property
props.setProperty(RCVD_PROP_PREFIX + e.getKey(), (String) e.getValue());
}
}
if (action != null) {
// Must handle isKnown in each case.
if (action.equals(HostTxtEntry.ACTION_ADDDEST)) {
// Add an alternate destination (new crypto) for existing hostname
// Requires new NamingService support if the key exists
String polddest = hprops.getProperty(HostTxtEntry.PROP_OLDDEST);
if (polddest != null) {
Destination pod = new Destination(polddest);
List<Destination> pod2 = router.lookupAll(key);
if (pod2 == null) {
// check inner sig anyway
if (!he.hasValidInnerSig()) {
logInner(log, action, key, addressbook);
invalid++;
continue;
}
} else if (pod2.contains(dest)) {
// we knew it before, with the same dest
old++;
continue;
} else if (pod2.contains(pod)) {
// checks out, so verify the inner sig
if (!he.hasValidInnerSig()) {
logInner(log, action, key, addressbook);
invalid++;
continue;
}
// TODO Requires NamingService support
// if (isTextFile), do we replace or not? check sigType.isAvailable()
boolean success = router.addDestination(key, dest, props);
if (log != null) {
if (success)
log.append("Additional address for " + key + " added to address book. From: " + addressbook.getLocation());
else
log.append("Failed to add additional address for " + key + " From: " + addressbook.getLocation());
}
// ditto
if (publishedNS != null) {
// FIXME this fails, no support in SFNS
success = publishedNS.addDestination(key, dest, props);
if (log != null && !success)
log.append("Add to published address book " + publishedNS.getName() + " failed for " + key);
}
nnew++;
continue;
} else {
// mismatch, disallow
logMismatch(log, action, key, pod2, he.getDest(), addressbook);
invalid++;
continue;
}
} else {
logMissing(log, action, key, addressbook);
invalid++;
continue;
}
} else if (action.equals(HostTxtEntry.ACTION_ADDNAME)) {
// Add an alias for an existing hostname, same dest
if (isKnown) {
// could be same or different dest
old++;
continue;
}
String poldname = hprops.getProperty(HostTxtEntry.PROP_OLDNAME);
if (poldname != null) {
List<Destination> pod = router.lookupAll(poldname);
if (pod == null) {
// we didn't have the old one, so we'll add the new one
} else if (pod.contains(dest)) {
// checks out, so we'll add the new one
} else {
// mismatch, disallow
logMismatch(log, action, key, pod, he.getDest(), addressbook);
invalid++;
continue;
}
} else {
logMissing(log, action, key, addressbook);
invalid++;
continue;
}
} else if (action.equals(HostTxtEntry.ACTION_ADDSUBDOMAIN)) {
// add a subdomain with verification
if (isKnown) {
old++;
continue;
}
String polddest = hprops.getProperty(HostTxtEntry.PROP_OLDDEST);
String poldname = hprops.getProperty(HostTxtEntry.PROP_OLDNAME);
if (polddest != null && poldname != null) {
// check for valid subdomain
if (!AddressBook.isValidKey(poldname) || key.indexOf('.' + poldname) <= 0) {
if (log != null)
log.append("Action: " + action + " failed because" + " old name " + poldname + " is invalid" + ". From: " + addressbook.getLocation());
invalid++;
continue;
}
Destination pod = new Destination(polddest);
List<Destination> pod2 = router.lookupAll(poldname);
if (pod2 == null) {
// check inner sig anyway
if (!he.hasValidInnerSig()) {
logInner(log, action, key, addressbook);
invalid++;
continue;
}
} else if (pod2.contains(pod)) {
// checks out, so verify the inner sig
if (!he.hasValidInnerSig()) {
logInner(log, action, key, addressbook);
invalid++;
continue;
}
} else {
// mismatch, disallow
logMismatch(log, action, key, pod2, polddest, addressbook);
invalid++;
continue;
}
} else {
logMissing(log, action, key, addressbook);
invalid++;
continue;
}
} else if (action.equals(HostTxtEntry.ACTION_CHANGEDEST)) {
// change destination on an existing entry
// This removes all previous destinations under that hostname,
// is this what we want?
String polddest = hprops.getProperty(HostTxtEntry.PROP_OLDDEST);
if (polddest != null) {
Destination pod = new Destination(polddest);
List<Destination> pod2 = router.lookupAll(key);
if (pod2 == null) {
// check inner sig anyway
if (!he.hasValidInnerSig()) {
logInner(log, action, key, addressbook);
invalid++;
continue;
}
} else if (pod2.contains(dest)) {
// we already have the new dest
old++;
continue;
} else if (pod2.contains(pod)) {
// checks out, so verify the inner sig
if (!he.hasValidInnerSig()) {
logInner(log, action, key, addressbook);
invalid++;
continue;
}
if (log != null) {
if (pod2.size() == 1)
log.append("Changing destination for " + key + ". From: " + addressbook.getLocation());
else
log.append("Replacing " + pod2.size() + " destinations for " + key + ". From: " + addressbook.getLocation());
}
allowExistingKeyInPublished = true;
props.setProperty("m", Long.toString(I2PAppContext.getGlobalContext().clock().now()));
} else {
// mismatch, disallow
logMismatch(log, action, key, pod2, polddest, addressbook);
invalid++;
continue;
}
} else {
logMissing(log, action, key, addressbook);
invalid++;
continue;
}
} else if (action.equals(HostTxtEntry.ACTION_CHANGENAME)) {
// is this what we want?
if (isKnown) {
old++;
continue;
}
String poldname = hprops.getProperty(HostTxtEntry.PROP_OLDNAME);
if (poldname != null) {
List<Destination> pod = router.lookupAll(poldname);
if (pod == null) {
// we didn't have the old name
} else if (pod.contains(dest)) {
// checks out, so we'll delete it
if (knownNames != null)
knownNames.remove(poldname);
boolean success = router.remove(poldname, dest);
if (success)
deleted++;
if (log != null) {
if (success)
log.append("Removed: " + poldname + " to be replaced with " + key + ". From: " + addressbook.getLocation());
else
log.append("Remove failed for: " + poldname + " to be replaced with " + key + ". From: " + addressbook.getLocation());
}
// now update the published addressbook
if (publishedNS != null) {
success = publishedNS.remove(poldname, dest);
if (log != null && !success)
log.append("Remove from published address book " + publishedNS.getName() + " failed for " + poldname);
}
} else {
// mismatch, disallow
logMismatch(log, action, key, pod, he.getDest(), addressbook);
continue;
}
} else {
logMissing(log, action, key, addressbook);
invalid++;
continue;
}
} else if (action.equals(HostTxtEntry.ACTION_REMOVE) || action.equals(HostTxtEntry.ACTION_REMOVEALL)) {
// w/o name=dest handled below
if (log != null)
log.append("Action: " + action + " with name=dest invalid" + ". From: " + addressbook.getLocation());
invalid++;
continue;
} else if (action.equals(HostTxtEntry.ACTION_UPDATE)) {
if (isKnown) {
allowExistingKeyInPublished = true;
props.setProperty("m", Long.toString(I2PAppContext.getGlobalContext().clock().now()));
}
} else {
if (log != null)
log.append("Action: " + action + " unrecognized" + ". From: " + addressbook.getLocation());
invalid++;
continue;
}
}
// action != null
boolean success = router.put(key, dest, props);
if (log != null) {
if (success)
log.append("New address " + key + " added to address book. From: " + addressbook.getLocation());
else
log.append("Save to naming service " + router + " failed for new key " + key);
}
// now update the published addressbook
if (publishedNS != null) {
if (allowExistingKeyInPublished)
success = publishedNS.put(key, dest, props);
else
success = publishedNS.putIfAbsent(key, dest, props);
if (log != null && !success) {
log.append("Save to published address book " + publishedNS.getName() + " failed for new key " + key);
}
}
if (knownNames != null) {
// keep track for later dup check
knownNames.add(key);
}
nnew++;
} else if (key == null) {
// isKnown is false
if (action != null) {
// Process commands. hprops is non-null.
if (action.equals(HostTxtEntry.ACTION_REMOVE)) {
// delete this entry
String polddest = hprops.getProperty(HostTxtEntry.PROP_DEST);
String poldname = hprops.getProperty(HostTxtEntry.PROP_NAME);
if (polddest != null && poldname != null) {
Destination pod = new Destination(polddest);
List<Destination> pod2 = router.lookupAll(poldname);
if (pod2 != null && pod2.contains(pod)) {
if (knownNames != null && pod2.size() == 1)
knownNames.remove(poldname);
boolean success = router.remove(poldname, pod);
if (success)
deleted++;
if (log != null) {
if (success)
log.append("Removed: " + poldname + " as requested" + ". From: " + addressbook.getLocation());
else
log.append("Remove failed for: " + poldname + " as requested" + ". From: " + addressbook.getLocation());
}
// now update the published addressbook
if (publishedNS != null) {
success = publishedNS.remove(poldname, pod);
if (log != null && !success)
log.append("Remove from published address book " + publishedNS.getName() + " failed for " + poldname);
}
} else if (pod2 != null) {
// mismatch, disallow
logMismatch(log, action, key, pod2, polddest, addressbook);
invalid++;
} else {
old++;
}
} else {
logMissing(log, action, "delete", addressbook);
invalid++;
}
} else if (action.equals(HostTxtEntry.ACTION_REMOVEALL)) {
// delete all entries with this destination
String polddest = hprops.getProperty(HostTxtEntry.PROP_DEST);
// oldname is optional, but nice because not all books support reverse lookup
if (polddest != null) {
Destination pod = new Destination(polddest);
String poldname = hprops.getProperty(HostTxtEntry.PROP_NAME);
if (poldname != null) {
List<Destination> pod2 = router.lookupAll(poldname);
if (pod2 != null && pod2.contains(pod)) {
if (knownNames != null)
knownNames.remove(poldname);
boolean success = router.remove(poldname, pod);
if (success)
deleted++;
if (log != null) {
if (success)
log.append("Removed: " + poldname + " as requested" + ". From: " + addressbook.getLocation());
else
log.append("Remove failed for: " + poldname + " as requested" + ". From: " + addressbook.getLocation());
}
// now update the published addressbook
if (publishedNS != null) {
success = publishedNS.remove(poldname, pod);
if (log != null && !success)
log.append("Remove from published address book " + publishedNS.getName() + " failed for " + poldname);
}
} else if (pod2 != null) {
// mismatch, disallow
logMismatch(log, action, key, pod2, polddest, addressbook);
invalid++;
} else {
old++;
}
}
// reverse lookup, delete all
List<String> revs = router.reverseLookupAll(pod);
if (revs != null) {
for (String rev : revs) {
if (knownNames != null)
knownNames.remove(rev);
boolean success = router.remove(rev, pod);
if (success)
deleted++;
if (log != null) {
if (success)
log.append("Removed: " + rev + " as requested" + ". From: " + addressbook.getLocation());
else
log.append("Remove failed for: " + rev + " as requested" + ". From: " + addressbook.getLocation());
}
// now update the published addressbook
if (publishedNS != null) {
success = publishedNS.remove(rev, pod);
if (log != null && !success)
log.append("Remove from published address book " + publishedNS.getName() + " failed for " + rev);
}
}
}
} else {
logMissing(log, action, "delete", addressbook);
invalid++;
}
} else {
if (log != null)
log.append("Action: " + action + " w/o name=dest unrecognized" + ". From: " + addressbook.getLocation());
invalid++;
}
continue;
} else {
if (log != null)
log.append("No action in command line" + ". From: " + addressbook.getLocation());
invalid++;
continue;
}
} else if (log != null) {
log.append("Bad hostname " + key + ". From: " + addressbook.getLocation());
invalid++;
}
/**
**
* } else if (false && DEBUG && log != null) {
* // lookup the conflict if we haven't yet (O(n**2) for text file)
* if (isTextFile)
* oldDest = router.lookup(key);
* if (oldDest != null && !oldDest.toBase64().equals(entry.getValue())) {
* log.append("Conflict for " + key + ". From: "
* + addressbook.getLocation()
* + ". Destination in remote address book is "
* + entry.getValue());
* conflict++;
* } else {
* old++;
* }
***
*/
} else {
old++;
}
} catch (DataFormatException dfe) {
if (log != null)
log.append("Invalid b64 for " + key + " From: " + addressbook.getLocation());
invalid++;
}
}
// entries
if (DEBUG && log != null && total > 0) {
log.append("Merge of " + addressbook.getLocation() + " into " + router + " took " + (System.currentTimeMillis() - start) + " ms with " + total + " total, " + nnew + " new, " + old + " old, " + deleted + " deleted, " + invalid + " invalid, " + conflict + " conflicts");
}
}
use of net.i2p.data.Destination in project i2p.i2p by i2p.
the class BlockfileNamingService method initNew.
/**
* Create a new database and initialize it from the local files
* privatehosts.txt, userhosts.txt, and hosts.txt,
* creating a skiplist in the database for each.
*/
private BlockFile initNew(RAIFile f) throws IOException {
long start = _context.clock().now();
_version = VERSION;
_destSerializer = _destSerializerV4;
_isVersion4 = true;
try {
BlockFile rv = new BlockFile(f, true);
SkipList<String, Properties> hdr = rv.makeIndex(INFO_SKIPLIST, _stringSerializer, _infoSerializer);
Properties info = new Properties();
info.setProperty(PROP_VERSION, VERSION);
info.setProperty(PROP_CREATED, Long.toString(_context.clock().now()));
String list = _context.getProperty(HostsTxtNamingService.PROP_HOSTS_FILE, HostsTxtNamingService.DEFAULT_HOSTS_FILE);
info.setProperty(PROP_LISTS, list);
hdr.put(PROP_INFO, info);
rv.makeIndex(REVERSE_SKIPLIST, _hashIndexSerializer, _infoSerializer);
int total = 0;
for (String hostsfile : getFilenames(list)) {
_lists.add(hostsfile);
File file = new File(_context.getRouterDir(), hostsfile);
if ((!file.exists()) || !(file.canRead()))
continue;
int count = 0;
BufferedReader in = null;
String sourceMsg = "Imported from " + hostsfile + " file";
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"), 16 * 1024);
String line = null;
while ((line = in.readLine()) != null) {
if (line.startsWith("#"))
continue;
int split = line.indexOf('=');
if (split <= 0)
continue;
String key = line.substring(0, split).toLowerCase(Locale.US);
if (line.indexOf('#') > 0) {
// trim off any end of line comment
line = line.substring(0, line.indexOf('#')).trim();
if (line.length() < split + 1)
continue;
}
String b64 = line.substring(split + 1).trim();
Destination d = lookupBase64(b64);
if (d != null) {
addEntry(rv, hostsfile, key, d, sourceMsg);
addReverseEntry(rv, key, d, _log);
count++;
} else {
_log.logAlways(Log.WARN, "Unable to import entry for " + key + " from file " + file + " - bad Base 64: " + b64);
}
}
} catch (IOException ioe) {
_log.error("Failed to read hosts from " + file, ioe);
} finally {
if (in != null)
try {
in.close();
} catch (IOException ioe) {
}
}
total += count;
_log.logAlways(Log.INFO, "Migrating " + count + " hosts from " + file + " to new hosts database");
}
if (_log.shouldLog(Log.INFO))
_log.info("DB init took " + DataHelper.formatDuration(_context.clock().now() - start));
if (total <= 0)
_log.logAlways(Log.WARN, "No hosts.txt files found, Initialized hosts database with zero entries");
return rv;
} catch (RuntimeException e) {
_log.error("Failed to initialize database", e);
throw new IOException(e.toString());
}
}
use of net.i2p.data.Destination in project i2p.i2p by i2p.
the class BlockfileNamingService method remove.
/**
* Remove a hostname's entry only if it contains the Destination d.
* If the NamingService supports multiple Destinations per hostname,
* and this is the only Destination, removes the entire entry.
* If aditional Destinations remain, it only removes the
* specified Destination from the entry.
*
* @param options NamingService-specific, may be null
* @return true if entry containing d was successfully removed.
* @since 0.9.26
*/
@Override
public boolean remove(String hostname, Destination d, Properties options) {
if (!_isVersion4) {
// super does a get-test-remove, so lock around that
synchronized (_bf) {
return super.remove(hostname, d, options);
}
}
List<Properties> storedOptions = new ArrayList<Properties>(4);
synchronized (_bf) {
// We use lookupAll2(), not lookupAll(), because if hostname starts with www.,
// we do not want to read in from the
// non-www hostname and then copy it to a new www hostname.
List<Destination> dests = lookupAll2(hostname, options, storedOptions);
if (dests == null)
return false;
for (int i = 0; i < dests.size(); i++) {
Destination dd = dests.get(i);
if (dd.equals(d)) {
// Found it. Remove and return.
if (dests.size() == 1)
return remove(hostname, options);
List<Destination> newDests = new ArrayList<Destination>(dests.size() - 1);
for (int j = 0; j < dests.size(); j++) {
if (j != i)
newDests.add(dests.get(j));
}
storedOptions.remove(i);
removeReverseEntry(hostname, d);
if (options != null) {
String list = options.getProperty("list");
if (list != null)
storedOptions.get(0).setProperty("list", list);
}
return put(hostname, newDests, storedOptions, false);
}
}
}
return false;
}
Aggregations