Search in sources :

Example 1 with DHTTransportValue

use of com.biglybt.core.dht.transport.DHTTransportValue in project BiglyBT by BiglySoftware.

the class DHTDBImpl method store.

/*
	private long store_ops;
	private long store_ops_bad1;
	private long store_ops_bad2;

	private void
	logStoreOps()
	{
		System.out.println( "sops (" + control.getTransport().getNetwork() + ")=" + store_ops + ",bad1=" + store_ops_bad1 + ",bad2=" + store_ops_bad2 );
	}
	*/
@Override
public byte store(DHTTransportContact sender, HashWrapper key, DHTTransportValue[] values) {
    if (total_size + (total_values * 4) > MAX_TOTAL_SIZE) {
        DHTLog.log("Not storing " + DHTLog.getString2(key.getHash()) + " as maximum storage limit exceeded");
        return (DHT.DT_SIZE);
    }
    try {
        this_mon.enter();
        if (sleeping || suspended) {
            return (DHT.DT_NONE);
        }
        checkCacheExpiration(false);
        DHTDBMapping mapping = stored_values.get(key);
        if (mapping == null) {
            mapping = new DHTDBMapping(this, key, false);
            stored_values.put(key, mapping);
            addToPrefixMap(mapping);
        }
        for (DHTTransportValue value : values) {
            DHTDBValueImpl mapping_value = new DHTDBValueImpl(sender, value, false);
            mapping.add(mapping_value);
        }
        return (mapping.getDiversificationType());
    } finally {
        this_mon.exit();
    }
}
Also used : DHTTransportValue(com.biglybt.core.dht.transport.DHTTransportValue)

Example 2 with DHTTransportValue

use of com.biglybt.core.dht.transport.DHTTransportValue in project BiglyBT by BiglySoftware.

the class DHTDBImpl method republishCachedMappings.

protected int[] republishCachedMappings() {
    if (suspended) {
        logger.log("Cache republish skipped as suspended");
        return (new int[] { 0, 0, 0 });
    }
    // first refresh any leaves that have not performed at least one lookup in the
    // last period
    router.refreshIdleLeaves(cache_republish_interval);
    final Map<HashWrapper, List<DHTDBValueImpl>> republish = new HashMap<>();
    List<DHTDBMapping> republish_via_survey = new ArrayList<>();
    long now = System.currentTimeMillis();
    try {
        this_mon.enter();
        checkCacheExpiration(true);
        for (Entry<HashWrapper, DHTDBMapping> entry : stored_values.entrySet()) {
            HashWrapper key = entry.getKey();
            DHTDBMapping mapping = entry.getValue();
            if (mapping.getDiversificationType() != DHT.DT_NONE) {
                continue;
            }
            Iterator<DHTDBValueImpl> it2 = mapping.getValues();
            boolean all_rf_values = it2.hasNext();
            List<DHTDBValueImpl> values = new ArrayList<>();
            while (it2.hasNext()) {
                DHTDBValueImpl value = it2.next();
                if (value.isLocal()) {
                    all_rf_values = false;
                } else {
                    if (value.getReplicationFactor() == DHT.REP_FACT_DEFAULT) {
                        all_rf_values = false;
                    }
                    if (now < value.getStoreTime()) {
                        // deal with clock changes
                        value.setStoreTime(now);
                    } else if (now - value.getStoreTime() <= cache_republish_interval) {
                    // System.out.println( "skipping store" );
                    } else {
                        values.add(value);
                    }
                }
            }
            if (all_rf_values) {
                // if surveying is disabled then we swallow values here to prevent them
                // from being replicated using the existing technique and muddying the waters
                // handled by the survey process
                values.clear();
                republish_via_survey.add(mapping);
            }
            if (values.size() > 0) {
                republish.put(key, values);
            }
        }
    } finally {
        this_mon.exit();
    }
    if (republish_via_survey.size() > 0) {
        // we still check for being too far away here
        List<HashWrapper> stop_caching = new ArrayList<>();
        for (DHTDBMapping mapping : republish_via_survey) {
            HashWrapper key = mapping.getKey();
            byte[] lookup_id = key.getHash();
            List<DHTTransportContact> contacts = control.getClosestKContactsList(lookup_id, false);
            // if we are no longer one of the K closest contacts then we shouldn't
            // cache the value
            boolean keep_caching = false;
            for (int j = 0; j < contacts.size(); j++) {
                if (router.isID(contacts.get(j).getID())) {
                    keep_caching = true;
                    break;
                }
            }
            if (!keep_caching) {
                DHTLog.log("Dropping cache entry for " + DHTLog.getString(lookup_id) + " as now too far away");
                stop_caching.add(key);
            }
        }
        if (stop_caching.size() > 0) {
            try {
                this_mon.enter();
                for (int i = 0; i < stop_caching.size(); i++) {
                    DHTDBMapping mapping = stored_values.remove(stop_caching.get(i));
                    if (mapping != null) {
                        removeFromPrefixMap(mapping);
                        mapping.destroy();
                    }
                }
            } finally {
                this_mon.exit();
            }
        }
    }
    final int[] values_published = { 0 };
    final int[] keys_published = { 0 };
    final int[] republish_ops = { 0 };
    final HashSet<DHTTransportContact> anti_spoof_done = new HashSet<>();
    if (republish.size() > 0) {
        // System.out.println( "cache replublish" );
        // The approach is to refresh all leaves in the smallest subtree, thus populating the tree with
        // sufficient information to directly know which nodes to republish the values
        // to.
        // However, I'm going to rely on the "refresh idle leaves" logic above
        // (that's required to keep the DHT alive in general) to ensure that all
        // k-buckets are reasonably up-to-date
        Iterator<Map.Entry<HashWrapper, List<DHTDBValueImpl>>> it1 = republish.entrySet().iterator();
        List<HashWrapper> stop_caching = new ArrayList<>();
        // build a map of contact -> list of keys to republish
        Map<HashWrapper, Object[]> contact_map = new HashMap<>();
        while (it1.hasNext()) {
            Map.Entry<HashWrapper, List<DHTDBValueImpl>> entry = it1.next();
            HashWrapper key = entry.getKey();
            byte[] lookup_id = key.getHash();
            // just use the closest contacts - if some have failed then they'll
            // get flushed out by this operation. Grabbing just the live ones
            // is a bad idea as failures may rack up against the live ones due
            // to network problems and kill them, leaving the dead ones!
            List<DHTTransportContact> contacts = control.getClosestKContactsList(lookup_id, false);
            // if we are no longer one of the K closest contacts then we shouldn't
            // cache the value
            boolean keep_caching = false;
            for (int j = 0; j < contacts.size(); j++) {
                if (router.isID(contacts.get(j).getID())) {
                    keep_caching = true;
                    break;
                }
            }
            if (!keep_caching) {
                DHTLog.log("Dropping cache entry for " + DHTLog.getString(lookup_id) + " as now too far away");
                stop_caching.add(key);
            // we carry on and do one last publish
            }
            for (int j = 0; j < contacts.size(); j++) {
                DHTTransportContact contact = contacts.get(j);
                if (router.isID(contact.getID())) {
                    // ignore ourselves
                    continue;
                }
                Object[] data = contact_map.get(new HashWrapper(contact.getID()));
                if (data == null) {
                    data = new Object[] { contact, new ArrayList<HashWrapper>() };
                    contact_map.put(new HashWrapper(contact.getID()), data);
                }
                ((List<HashWrapper>) data[1]).add(key);
            }
        }
        Iterator<Object[]> it2 = contact_map.values().iterator();
        final int con_tot = contact_map.size();
        int con_num = 0;
        while (it2.hasNext()) {
            con_num++;
            final int f_con_num = con_num;
            final Object[] data = it2.next();
            final DHTTransportContact contact = (DHTTransportContact) data[0];
            // move to anti-spoof on cache forwards - gotta do a find-node first
            // to get the random id
            final AESemaphore sem = new AESemaphore("DHTDB:cacheForward");
            contact.sendFindNode(new DHTTransportReplyHandlerAdapter() {

                @Override
                public void findNodeReply(DHTTransportContact _contact, DHTTransportContact[] _contacts) {
                    anti_spoof_done.add(_contact);
                    try {
                        // System.out.println( "cacheForward: pre-store findNode OK" );
                        List<HashWrapper> keys = (List<HashWrapper>) data[1];
                        byte[][] store_keys = new byte[keys.size()][];
                        DHTTransportValue[][] store_values = new DHTTransportValue[store_keys.length][];
                        keys_published[0] += store_keys.length;
                        for (int i = 0; i < store_keys.length; i++) {
                            HashWrapper wrapper = keys.get(i);
                            store_keys[i] = wrapper.getHash();
                            List<DHTDBValueImpl> values = republish.get(wrapper);
                            store_values[i] = new DHTTransportValue[values.size()];
                            values_published[0] += store_values[i].length;
                            for (int j = 0; j < values.size(); j++) {
                                DHTDBValueImpl value = values.get(j);
                                // we reduce the cache distance by 1 here as it is incremented by the
                                // recipients
                                store_values[i][j] = value.getValueForRelay(local_contact);
                            }
                        }
                        List<DHTTransportContact> contacts = new ArrayList<>();
                        contacts.add(contact);
                        republish_ops[0]++;
                        control.putDirectEncodedKeys(store_keys, "Republish cache: " + f_con_num + " of " + con_tot, store_values, contacts);
                    } finally {
                        sem.release();
                    }
                }

                @Override
                public void failed(DHTTransportContact _contact, Throwable _error) {
                    try {
                        // System.out.println( "cacheForward: pre-store findNode Failed" );
                        DHTLog.log("cacheForward: pre-store findNode failed " + DHTLog.getString(_contact) + " -> failed: " + _error.getMessage());
                        router.contactDead(_contact.getID(), false);
                    } finally {
                        sem.release();
                    }
                }
            }, contact.getProtocolVersion() >= DHTTransportUDP.PROTOCOL_VERSION_ANTI_SPOOF2 ? new byte[0] : new byte[20], DHT.FLAG_LOOKUP_FOR_STORE);
            sem.reserve();
            if (Logger.isClosingTakingTooLong()) {
                break;
            }
        }
        try {
            this_mon.enter();
            for (int i = 0; i < stop_caching.size(); i++) {
                DHTDBMapping mapping = stored_values.remove(stop_caching.get(i));
                if (mapping != null) {
                    removeFromPrefixMap(mapping);
                    mapping.destroy();
                }
            }
        } finally {
            this_mon.exit();
        }
    }
    DHTStorageBlock[] direct_key_blocks = getDirectKeyBlocks();
    if (direct_key_blocks.length > 0) {
        for (int i = 0; i < direct_key_blocks.length; i++) {
            final DHTStorageBlock key_block = direct_key_blocks[i];
            List contacts = control.getClosestKContactsList(key_block.getKey(), false);
            boolean forward_it = false;
            for (int j = 0; j < contacts.size(); j++) {
                final DHTTransportContact contact = (DHTTransportContact) contacts.get(j);
                if (router.isID(contact.getID())) {
                    forward_it = true;
                    break;
                }
            }
            for (int j = 0; forward_it && j < contacts.size(); j++) {
                final DHTTransportContact contact = (DHTTransportContact) contacts.get(j);
                if (key_block.hasBeenSentTo(contact)) {
                    continue;
                }
                if (router.isID(contact.getID())) {
                    // ignore ourselves
                    continue;
                }
                if (contact.getProtocolVersion() >= DHTTransportUDP.PROTOCOL_VERSION_BLOCK_KEYS) {
                    final Runnable task = new Runnable() {

                        @Override
                        public void run() {
                            contact.sendKeyBlock(new DHTTransportReplyHandlerAdapter() {

                                @Override
                                public void keyBlockReply(DHTTransportContact _contact) {
                                    DHTLog.log("key block forward ok " + DHTLog.getString(_contact));
                                    key_block.sentTo(_contact);
                                }

                                @Override
                                public void failed(DHTTransportContact _contact, Throwable _error) {
                                    DHTLog.log("key block forward failed " + DHTLog.getString(_contact) + " -> failed: " + _error.getMessage());
                                }
                            }, key_block.getRequest(), key_block.getCertificate());
                        }
                    };
                    if (anti_spoof_done.contains(contact)) {
                        task.run();
                    } else {
                        contact.sendFindNode(new DHTTransportReplyHandlerAdapter() {

                            @Override
                            public void findNodeReply(DHTTransportContact contact, DHTTransportContact[] contacts) {
                                task.run();
                            }

                            @Override
                            public void failed(DHTTransportContact _contact, Throwable _error) {
                                // System.out.println( "nodeAdded: pre-store findNode Failed" );
                                DHTLog.log("pre-kb findNode failed " + DHTLog.getString(_contact) + " -> failed: " + _error.getMessage());
                                router.contactDead(_contact.getID(), false);
                            }
                        }, contact.getProtocolVersion() >= DHTTransportUDP.PROTOCOL_VERSION_ANTI_SPOOF2 ? new byte[0] : new byte[20], DHT.FLAG_LOOKUP_FOR_STORE);
                    }
                }
            }
        }
    }
    return (new int[] { values_published[0], keys_published[0], republish_ops[0] });
}
Also used : Entry(java.util.Map.Entry) DHTTransportValue(com.biglybt.core.dht.transport.DHTTransportValue) DHTTransportContact(com.biglybt.core.dht.transport.DHTTransportContact) DHTTransportReplyHandlerAdapter(com.biglybt.core.dht.transport.DHTTransportReplyHandlerAdapter)

Example 3 with DHTTransportValue

use of com.biglybt.core.dht.transport.DHTTransportValue in project BiglyBT by BiglySoftware.

the class DHTDBImpl method queriesComplete.

protected void queriesComplete(byte[] survey_my_id, Map<DHTDBMapping, List<DHTTransportContact>> mapping_to_node_map, Map<DHTTransportContact, Object[]> replies) {
    Map<SurveyContactState, List<DHTDBMapping>> store_ops = new HashMap<>();
    try {
        this_mon.enter();
        if (!Arrays.equals(survey_my_id, router.getID())) {
            logger.log("Survey abandoned - router changed");
            return;
        }
        if (DEBUG_SURVEY) {
            System.out.println("Queries complete (replies=" + replies.size() + ")");
        }
        Map<DHTDBMapping, int[]> totals = new HashMap<>();
        for (Map.Entry<DHTTransportContact, Object[]> entry : replies.entrySet()) {
            DHTTransportContact contact = entry.getKey();
            HashWrapper hw = new HashWrapper(contact.getID());
            SurveyContactState contact_state = survey_state.get(hw);
            if (contact_state != null) {
                contact_state.updateContactDetails(contact);
            } else {
                contact_state = new SurveyContactState(contact);
                survey_state.put(hw, contact_state);
            }
            contact_state.updateUseTime();
            Object[] temp = entry.getValue();
            List<DHTDBMapping> mappings = (List<DHTDBMapping>) temp[0];
            List<byte[]> reply = (List<byte[]>) temp[1];
            if (reply == null) {
                contact_state.contactFailed();
            } else {
                contact_state.contactOK();
                if (mappings.size() != reply.size()) {
                    Debug.out("Inconsistent: mappings=" + mappings.size() + ", reply=" + reply.size());
                    continue;
                }
                Iterator<DHTDBMapping> it1 = mappings.iterator();
                Iterator<byte[]> it2 = reply.iterator();
                while (it1.hasNext()) {
                    DHTDBMapping mapping = it1.next();
                    byte[] rep = it2.next();
                    if (rep == null) {
                        contact_state.removeMapping(mapping);
                    } else {
                        // must match against our short-key mapping for consistency
                        DHTDBMapping mapping_to_check = stored_values_prefix_map.get(mapping.getShortKey());
                        if (mapping_to_check == null) {
                        // deleted
                        } else {
                            byte[] k = mapping_to_check.getKey().getBytes();
                            int rep_len = rep.length;
                            if (rep_len < 2 || rep_len >= k.length) {
                                Debug.out("Invalid rep_len: " + rep_len);
                                continue;
                            }
                            boolean match = true;
                            int offset = k.length - rep_len;
                            for (int i = 0; i < rep_len; i++) {
                                if (rep[i] != k[i + offset]) {
                                    match = false;
                                    break;
                                }
                            }
                            if (match) {
                                contact_state.addMapping(mapping);
                            } else {
                                contact_state.removeMapping(mapping);
                            }
                        }
                    }
                }
                Set<DHTDBMapping> contact_mappings = contact_state.getMappings();
                for (DHTDBMapping m : contact_mappings) {
                    int[] t = totals.get(m);
                    if (t == null) {
                        // one for local node + 1 for them
                        t = new int[] { 2 };
                        totals.put(m, t);
                    } else {
                        t[0]++;
                    }
                }
            }
        }
        for (Map.Entry<DHTDBMapping, List<DHTTransportContact>> entry : mapping_to_node_map.entrySet()) {
            DHTDBMapping mapping = entry.getKey();
            List<DHTTransportContact> contacts = entry.getValue();
            int[] t = totals.get(mapping);
            int copies;
            if (t == null) {
                // us!
                copies = 1;
            } else {
                copies = t[0];
            }
            Iterator<DHTDBValueImpl> values = mapping.getValues();
            if (values.hasNext()) {
                int max_replication_factor = -1;
                while (values.hasNext()) {
                    DHTDBValueImpl value = values.next();
                    int rf = value.getReplicationFactor();
                    if (rf > max_replication_factor) {
                        max_replication_factor = rf;
                    }
                }
                if (max_replication_factor == 0) {
                    continue;
                }
                if (max_replication_factor > router.getK()) {
                    max_replication_factor = router.getK();
                }
                if (copies < max_replication_factor) {
                    int required = max_replication_factor - copies;
                    List<SurveyContactState> potential_targets = new ArrayList<>();
                    List<byte[]> addresses = new ArrayList<>(contacts.size());
                    for (DHTTransportContact c : contacts) {
                        if (c.getProtocolVersion() < DHTTransportUDP.PROTOCOL_VERSION_REPLICATION_CONTROL3) {
                            continue;
                        }
                        addresses.add(AddressUtils.getAddressBytes(c.getAddress()));
                        SurveyContactState contact_state = survey_state.get(new HashWrapper(c.getID()));
                        if (contact_state != null && !contact_state.testMapping(mapping)) {
                            potential_targets.add(contact_state);
                        }
                    }
                    Set<HashWrapper> bad_addresses = new HashSet<>();
                    for (byte[] a1 : addresses) {
                        for (byte[] a2 : addresses) {
                            if (a1 == a2 || a1.length != a2.length || a1.length != 4) {
                                continue;
                            }
                            if (a1[0] == a2[0] && a1[1] == a2[1]) {
                                log("/16 match on " + ByteFormatter.encodeString(a1) + "/" + ByteFormatter.encodeString(a2));
                                bad_addresses.add(new HashWrapper(a1));
                                bad_addresses.add(new HashWrapper(a2));
                            }
                        }
                    }
                    final byte[] key = mapping.getKey().getBytes();
                    Collections.sort(potential_targets, new Comparator<SurveyContactState>() {

                        @Override
                        public int compare(SurveyContactState o1, SurveyContactState o2) {
                            boolean o1_bad = o1.getConsecFails() >= 2;
                            boolean o2_bad = o2.getConsecFails() >= 2;
                            if (o1_bad == o2_bad) {
                                if (false) {
                                    long res = o2.getCreationTime() - o1.getCreationTime();
                                    if (res < 0) {
                                        return (-1);
                                    } else if (res > 0) {
                                        return (1);
                                    } else {
                                        return (0);
                                    }
                                } else {
                                    return (control.computeAndCompareDistances(o1.getContact().getID(), o2.getContact().getID(), key));
                                }
                            } else {
                                if (o1_bad) {
                                    return (1);
                                } else {
                                    return (-1);
                                }
                            }
                        }
                    });
                    int avail = Math.min(required, potential_targets.size());
                    for (int i = 0; i < avail; i++) {
                        SurveyContactState target = potential_targets.get(i);
                        if (bad_addresses.size() > 0 && bad_addresses.contains(new HashWrapper(AddressUtils.getAddressBytes(target.getContact().getAddress())))) {
                            // make it look like this target has the mapping as we don't want to store it there but we want to treat it as
                            // if it has it, effectively reducing availability but not skewing storage in favour of potentially malicious nodes
                            target.addMapping(mapping);
                        } else {
                            List<DHTDBMapping> m = store_ops.get(target);
                            if (m == null) {
                                m = new ArrayList<>();
                                store_ops.put(target, m);
                            }
                            m.add(mapping);
                        }
                    }
                }
            }
        }
    } finally {
        this_mon.exit();
        survey_in_progress = false;
    }
    logger.log("Survey complete - " + store_ops.size() + " store ops");
    if (DEBUG_SURVEY) {
        System.out.println("Store ops: " + store_ops.size());
    }
    for (Map.Entry<SurveyContactState, List<DHTDBMapping>> store_op : store_ops.entrySet()) {
        final SurveyContactState contact = store_op.getKey();
        final List<DHTDBMapping> keys = store_op.getValue();
        final byte[][] store_keys = new byte[keys.size()][];
        final DHTTransportValue[][] store_values = new DHTTransportValue[store_keys.length][];
        for (int i = 0; i < store_keys.length; i++) {
            DHTDBMapping mapping = keys.get(i);
            store_keys[i] = mapping.getKey().getBytes();
            List<DHTTransportValue> v = new ArrayList<>();
            Iterator<DHTDBValueImpl> it = mapping.getValues();
            while (it.hasNext()) {
                DHTDBValueImpl value = it.next();
                if (!value.isLocal()) {
                    v.add(value.getValueForRelay(local_contact));
                }
            }
            store_values[i] = v.toArray(new DHTTransportValue[v.size()]);
        }
        final DHTTransportContact d_contact = contact.getContact();
        final Runnable store_exec = new Runnable() {

            @Override
            public void run() {
                if (DEBUG_SURVEY) {
                    System.out.println("Storing " + keys.size() + " on " + d_contact.getString() + " - rand=" + d_contact.getRandomID());
                }
                control.putDirectEncodedKeys(store_keys, "Replication forward", store_values, d_contact, new DHTOperationAdapter() {

                    @Override
                    public void complete(boolean timeout) {
                        try {
                            this_mon.enter();
                            if (timeout) {
                                contact.contactFailed();
                            } else {
                                contact.contactOK();
                                for (DHTDBMapping m : keys) {
                                    contact.addMapping(m);
                                }
                            }
                        } finally {
                            this_mon.exit();
                        }
                    }
                });
            }
        };
        if (d_contact.getRandomIDType() != DHTTransportContact.RANDOM_ID_TYPE1) {
            Debug.out("derp");
        }
        if (d_contact.getRandomID() == 0) {
            d_contact.sendFindNode(new DHTTransportReplyHandlerAdapter() {

                @Override
                public void findNodeReply(DHTTransportContact _contact, DHTTransportContact[] _contacts) {
                    store_exec.run();
                }

                @Override
                public void failed(DHTTransportContact _contact, Throwable _error) {
                    try {
                        this_mon.enter();
                        contact.contactFailed();
                    } finally {
                        this_mon.exit();
                    }
                }
            }, d_contact.getProtocolVersion() >= DHTTransportUDP.PROTOCOL_VERSION_ANTI_SPOOF2 ? new byte[0] : new byte[20], DHT.FLAG_LOOKUP_FOR_STORE);
        } else {
            store_exec.run();
        }
    }
}
Also used : DHTTransportValue(com.biglybt.core.dht.transport.DHTTransportValue) DHTTransportContact(com.biglybt.core.dht.transport.DHTTransportContact) DHTTransportReplyHandlerAdapter(com.biglybt.core.dht.transport.DHTTransportReplyHandlerAdapter)

Aggregations

DHTTransportValue (com.biglybt.core.dht.transport.DHTTransportValue)3 DHTTransportContact (com.biglybt.core.dht.transport.DHTTransportContact)2 DHTTransportReplyHandlerAdapter (com.biglybt.core.dht.transport.DHTTransportReplyHandlerAdapter)2 Entry (java.util.Map.Entry)1