use of com.biglybt.core.dht.transport.DHTTransportReplyHandlerAdapter 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();
}
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] });
}
use of com.biglybt.core.dht.transport.DHTTransportReplyHandlerAdapter 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();
}
}
}
use of com.biglybt.core.dht.transport.DHTTransportReplyHandlerAdapter in project BiglyBT by BiglySoftware.
the class DHTDBImpl method doQuery.
protected void doQuery(final byte[] survey_my_id, final int total, final Map<DHTDBMapping, List<DHTTransportContact>> mapping_to_node_map, final LinkedList<Map.Entry<DHTTransportContact, ByteArrayHashMap<List<DHTDBMapping>>>> to_do, final Map<DHTTransportContact, Object[]> replies, DHTTransportContact done_contact, List<DHTDBMapping> done_mappings, List<byte[]> done_reply) {
Map.Entry<DHTTransportContact, ByteArrayHashMap<List<DHTDBMapping>>> entry;
synchronized (to_do) {
if (done_contact != null) {
replies.put(done_contact, new Object[] { done_mappings, done_reply });
}
if (to_do.size() == 0) {
if (replies.size() == total) {
queriesComplete(survey_my_id, mapping_to_node_map, replies);
}
return;
}
entry = to_do.removeFirst();
}
DHTTransportContact contact = entry.getKey();
boolean handled = false;
try {
if (contact.getProtocolVersion() >= DHTTransportUDP.PROTOCOL_VERSION_REPLICATION_CONTROL3) {
if (DEBUG_SURVEY) {
System.out.println("Hitting " + contact.getString());
}
final List<DHTDBMapping> mapping_list = new ArrayList<>();
ByteArrayHashMap<List<DHTDBMapping>> map = entry.getValue();
List<byte[]> prefixes = map.keys();
List<Object[]> encoded = new ArrayList<>(prefixes.size());
try {
this_mon.enter();
SurveyContactState contact_state = survey_state.get(new HashWrapper(contact.getID()));
for (byte[] prefix : prefixes) {
int prefix_len = prefix.length;
int suffix_len = QUERY_STORE_REQUEST_ENTRY_SIZE - prefix_len;
List<DHTDBMapping> mappings = map.get(prefix);
List<byte[]> l = new ArrayList<>(mappings.size());
encoded.add(new Object[] { prefix, l });
for (DHTDBMapping m : mappings) {
if (contact_state != null) {
if (contact_state.testMapping(m)) {
if (DEBUG_SURVEY) {
System.out.println(" skipping " + ByteFormatter.encodeString(m.getKey().getBytes()) + " as contact already has");
}
continue;
}
}
mapping_list.add(m);
byte[] k = m.getKey().getBytes();
byte[] suffix = new byte[suffix_len];
System.arraycopy(k, prefix_len, suffix, 0, suffix_len);
l.add(suffix);
}
}
if (Arrays.equals(contact.getID(), survey_my_id)) {
Debug.out("inconsistent - we shouldn't query ourselves!");
}
contact.sendQueryStore(new DHTTransportReplyHandlerAdapter() {
@Override
public void queryStoreReply(DHTTransportContact contact, List<byte[]> response) {
try {
if (DEBUG_SURVEY) {
System.out.println("response " + response.size());
for (int i = 0; i < response.size(); i++) {
System.out.println(" " + ByteFormatter.encodeString(response.get(i)));
}
}
} finally {
doQuery(survey_my_id, total, mapping_to_node_map, to_do, replies, contact, mapping_list, response);
}
}
@Override
public void failed(DHTTransportContact contact, Throwable error) {
try {
if (DEBUG_SURVEY) {
System.out.println("Failed: " + Debug.getNestedExceptionMessage(error));
}
} finally {
doQuery(survey_my_id, total, mapping_to_node_map, to_do, replies, contact, mapping_list, null);
}
}
}, QUERY_STORE_REQUEST_ENTRY_SIZE, encoded);
handled = true;
} finally {
this_mon.exit();
}
} else {
if (DEBUG_SURVEY) {
System.out.println("Not hitting " + contact.getString());
}
}
} finally {
if (!handled) {
final List<DHTDBMapping> mapping_list = new ArrayList<>();
ByteArrayHashMap<List<DHTDBMapping>> map = entry.getValue();
List<byte[]> prefixes = map.keys();
for (byte[] prefix : prefixes) {
mapping_list.addAll(map.get(prefix));
}
doQuery(survey_my_id, total, mapping_to_node_map, to_do, replies, contact, mapping_list, null);
}
}
}
Aggregations