Search in sources :

Example 1 with HostTxtEntry

use of net.i2p.client.naming.HostTxtEntry 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");
    }
}
Also used : Destination(net.i2p.data.Destination) HostTxtEntry(net.i2p.client.naming.HostTxtEntry) Properties(java.util.Properties) OrderedProperties(net.i2p.util.OrderedProperties) DataFormatException(net.i2p.data.DataFormatException) OrderedProperties(net.i2p.util.OrderedProperties) ArrayList(java.util.ArrayList) List(java.util.List) HashMap(java.util.HashMap) Map(java.util.Map)

Example 2 with HostTxtEntry

use of net.i2p.client.naming.HostTxtEntry in project i2p.i2p by i2p.

the class Daemon method update.

/**
 * Update the router and published address books using remote data from the
 * subscribed address books listed in subscriptions.
 * Merging of the "master" addressbook is NOT supported.
 *
 * @param router
 *            The NamingService to update, generally the root NamingService from the context.
 * @param published
 *            The published AddressBook. This address book is published on
 *            the user's eepsite so that others may subscribe to it.
 *            May be null.
 *            If non-null, overwrite with the new addressbook.
 * @param subscriptions
 *            A SubscriptionList listing the remote address books to update
 *            from.
 * @param log
 *            The log to write changes and conflicts to.
 *            May be null.
 * @since 0.8.7
 */
public static void update(NamingService router, File published, SubscriptionList subscriptions, Log log) {
    // If the NamingService is a database, we look up as we go.
    // If it is a text file, we do things differently, to avoid O(n**2) behavior
    // when scanning large subscription results (i.e. those that return the whole file, not just the new entries) -
    // we load all the known hostnames into a Set one time.
    // This also has the advantage of not flushing the NamingService's LRU cache.
    String nsClass = router.getClass().getSimpleName();
    boolean isTextFile = nsClass.equals("HostsTxtNamingService") || nsClass.equals("SingleFileNamingService");
    Set<String> knownNames;
    if (isTextFile) {
        // load the hostname set
        Properties opts = new Properties();
        opts.setProperty("file", "hosts.txt");
        knownNames = router.getNames(opts);
    } else {
        knownNames = null;
    }
    NamingService publishedNS;
    if (published != null) {
        publishedNS = new SingleFileNamingService(I2PAppContext.getGlobalContext(), published.getAbsolutePath());
    } else {
        publishedNS = null;
    }
    Iterator<AddressBook> iter = subscriptions.iterator();
    while (iter.hasNext()) {
        // yes, the EepGet fetch() is done in next()
        long start = System.currentTimeMillis();
        AddressBook addressbook = iter.next();
        // SubscriptionIterator puts in a dummy AddressBook with no location if no fetch is done
        if (DEBUG && log != null && addressbook.getLocation() != null) {
            long end = System.currentTimeMillis();
            log.append("Fetch of " + addressbook.getLocation() + " took " + (end - start));
        }
        Iterator<Map.Entry<String, HostTxtEntry>> iter2 = addressbook.iterator();
        try {
            update(router, knownNames, publishedNS, addressbook, iter2, log);
        } finally {
            if (iter2 instanceof HostTxtIterator)
                ((HostTxtIterator) iter2).close();
            addressbook.delete();
        }
    }
    // subscriptions
    subscriptions.write();
}
Also used : HostTxtEntry(net.i2p.client.naming.HostTxtEntry) NamingService(net.i2p.client.naming.NamingService) SingleFileNamingService(net.i2p.client.naming.SingleFileNamingService) Properties(java.util.Properties) OrderedProperties(net.i2p.util.OrderedProperties) SingleFileNamingService(net.i2p.client.naming.SingleFileNamingService)

Example 3 with HostTxtEntry

use of net.i2p.client.naming.HostTxtEntry in project i2p.i2p by i2p.

the class HostTxtIterator method hasNext.

public boolean hasNext() {
    if (input == null)
        return false;
    if (next != null)
        return true;
    try {
        String inputLine;
        while ((inputLine = input.readLine()) != null) {
            HostTxtEntry he = HostTxtParser.parse(inputLine, true);
            if (he == null)
                continue;
            next = new MapEntry(he.getName(), he);
            return true;
        }
    } catch (IOException ioe) {
    }
    try {
        input.close();
    } catch (IOException ioe) {
    }
    input = null;
    next = null;
    return false;
}
Also used : HostTxtEntry(net.i2p.client.naming.HostTxtEntry) IOException(java.io.IOException)

Example 4 with HostTxtEntry

use of net.i2p.client.naming.HostTxtEntry in project i2p.i2p by i2p.

the class HostTxtIterator method next.

/**
 *  'remove' entries will be returned with a null key,
 *  and the value will contain a null name, null dest,
 *  and non-null props.
 */
public Map.Entry<String, HostTxtEntry> next() {
    if (!hasNext())
        throw new NoSuchElementException();
    Map.Entry<String, HostTxtEntry> rv = next;
    next = null;
    return rv;
}
Also used : HostTxtEntry(net.i2p.client.naming.HostTxtEntry) Map(java.util.Map) NoSuchElementException(java.util.NoSuchElementException)

Example 5 with HostTxtEntry

use of net.i2p.client.naming.HostTxtEntry in project i2p.i2p by i2p.

the class HostTxtParser method parse.

/**
 * Return a Map using the contents of BufferedReader input. input must have
 * a single key, value pair on each line, in the format: key=value. Lines
 * starting with '#' or ';' are considered comments, and ignored. Lines that
 * are obviously not in the format key=value are also ignored.
 * The key is converted to lower case.
 *
 * Returned map will not contain null ("remove") entries.
 *
 * @param input
 *            A BufferedReader with lines in key=value format to parse into
 *            a Map.
 * @return A Map containing the key, value pairs from input.
 * @throws IOException
 *             if the BufferedReader cannot be read.
 */
private static Map<String, HostTxtEntry> parse(BufferedReader input) throws IOException {
    try {
        Map<String, HostTxtEntry> result = new HashMap<String, HostTxtEntry>();
        String inputLine;
        while ((inputLine = input.readLine()) != null) {
            HostTxtEntry he = parse(inputLine, false);
            if (he == null)
                continue;
            result.put(he.getName(), he);
        }
        return result;
    } finally {
        try {
            input.close();
        } catch (IOException ioe) {
        }
    }
}
Also used : HashMap(java.util.HashMap) HostTxtEntry(net.i2p.client.naming.HostTxtEntry) IOException(java.io.IOException)

Aggregations

HostTxtEntry (net.i2p.client.naming.HostTxtEntry)10 IOException (java.io.IOException)4 HashMap (java.util.HashMap)3 Map (java.util.Map)3 Properties (java.util.Properties)3 OrderedProperties (net.i2p.util.OrderedProperties)3 Getopt (gnu.getopt.Getopt)1 BufferedReader (java.io.BufferedReader)1 File (java.io.File)1 FileInputStream (java.io.FileInputStream)1 InputStreamReader (java.io.InputStreamReader)1 OutputStreamWriter (java.io.OutputStreamWriter)1 ArrayList (java.util.ArrayList)1 List (java.util.List)1 NoSuchElementException (java.util.NoSuchElementException)1 I2PException (net.i2p.I2PException)1 I2PClient (net.i2p.client.I2PClient)1 NamingService (net.i2p.client.naming.NamingService)1 SingleFileNamingService (net.i2p.client.naming.SingleFileNamingService)1 SigType (net.i2p.crypto.SigType)1