use of com.biglybt.core.networkmanager.admin.NetworkAdminASNListener in project BiglyBT by BiglySoftware.
the class PEPeerControlImpl method doConnectionChecks.
private void doConnectionChecks() {
// if mixed networks then we have potentially two connections limits
// 1) general peer one - e.g. 100
// 2) general+reserved slots for non-public net - e.g. 103
// so we get to schedule 3 'extra' non-public connections
// every 1 second
boolean has_ipv6 = false;
boolean has_ipv4 = false;
boolean can_ipv6 = network_admin.hasIPV6Potential(true);
if (mainloop_loop_count % MAINLOOP_ONE_SECOND_INTERVAL == 0) {
// need to sync the rates periodically as when upload is disabled (for example)
// the we can end up with
// nothing requesting the rate in order for a change to be noticed
upload_limited_rate_group.getRateLimitBytesPerSecond();
download_limited_rate_group.getRateLimitBytesPerSecond();
final List<PEPeerTransport> peer_transports = peer_transports_cow;
int num_waiting_establishments = 0;
int udp_connections = 0;
for (int i = 0; i < peer_transports.size(); i++) {
final PEPeerTransport transport = peer_transports.get(i);
// update waiting count
final int state = transport.getConnectionState();
if (state == PEPeerTransport.CONNECTION_PENDING || state == PEPeerTransport.CONNECTION_CONNECTING) {
num_waiting_establishments++;
} else {
if (can_ipv6 && transport.getNetwork() == AENetworkClassifier.AT_PUBLIC) {
boolean is_ipv6 = transport.getIp().contains(":");
if (is_ipv6) {
has_ipv6 = true;
} else {
has_ipv4 = true;
}
}
}
if (!transport.isTCP()) {
udp_connections++;
}
}
int[] allowed_seeds_info = getMaxSeedConnections();
int base_allowed_seeds = allowed_seeds_info[0];
if (base_allowed_seeds > 0) {
int extra_seeds = allowed_seeds_info[1];
int to_disconnect = _seeds - base_allowed_seeds;
if (to_disconnect > 0) {
// seeds are limited by people trying to get a reasonable upload by connecting
// to leechers where possible. disconnect seeds from end of list to prevent
// cycling of seeds
Set<PEPeerTransport> to_retain = new HashSet<>();
if (extra_seeds > 0) {
for (PEPeerTransport transport : peer_transports) {
if (transport.isSeed() && transport.getNetwork() != AENetworkClassifier.AT_PUBLIC) {
to_retain.add(transport);
if (to_retain.size() == extra_seeds) {
break;
}
}
}
to_disconnect -= to_retain.size();
}
for (int i = peer_transports.size() - 1; i >= 0 && to_disconnect > 0; i--) {
final PEPeerTransport transport = peer_transports.get(i);
if (transport.isSeed()) {
if (!to_retain.contains(transport)) {
closeAndRemovePeer(transport, "Too many seeds", false);
to_disconnect--;
}
}
}
}
}
int[] allowed_info = getMaxNewConnectionsAllowed();
int allowed_base = allowed_info[0];
if (allowed_base < 0 || allowed_base > 1000) {
// ensure a very upper limit so it doesnt get out of control when using PEX
allowed_base = 1000;
allowed_info[0] = allowed_base;
}
if (adapter.isNATHealthy()) {
// if unfirewalled, leave slots avail for remote connections
// leave 5%
int free = getMaxConnections()[0] / 20;
allowed_base = allowed_base - free;
allowed_info[0] = allowed_base;
}
for (int i = 0; i < allowed_info.length; i++) {
int allowed = allowed_info[i];
if (allowed > 0) {
// try and connect only as many as necessary
final int wanted = TCPConnectionManager.MAX_SIMULTANEOUS_CONNECT_ATTEMPTS - num_waiting_establishments;
if (wanted > allowed) {
num_waiting_establishments += wanted - allowed;
}
int remaining = allowed;
int tcp_remaining = TCPNetworkManager.getSingleton().getConnectDisconnectManager().getMaxOutboundPermitted();
int udp_remaining = UDPNetworkManager.getSingleton().getConnectionManager().getMaxOutboundPermitted();
while (num_waiting_establishments < TCPConnectionManager.MAX_SIMULTANEOUS_CONNECT_ATTEMPTS && (tcp_remaining > 0 || udp_remaining > 0)) {
if (!is_running)
break;
final PeerItem item = peer_database.getNextOptimisticConnectPeer(i == 1);
if (item == null || !is_running)
break;
final PeerItem self = peer_database.getSelfPeer();
if (self != null && self.equals(item)) {
continue;
}
if (!isAlreadyConnected(item)) {
final String source = PeerItem.convertSourceString(item.getSource());
final boolean use_crypto = item.getHandshakeType() == PeerItemFactory.HANDSHAKE_TYPE_CRYPTO;
int tcp_port = item.getTCPPort();
int udp_port = item.getUDPPort();
if (udp_port == 0 && udp_probe_enabled) {
// for probing we assume udp port same as tcp
udp_port = tcp_port;
}
boolean prefer_udp_overall = prefer_udp || prefer_udp_default;
if (prefer_udp_overall && udp_port == 0) {
// see if we have previous record of this address as udp connectable
byte[] address = item.getIP().getBytes();
BloomFilter bloom = prefer_udp_bloom;
if (bloom != null && bloom.contains(address)) {
udp_port = tcp_port;
}
}
boolean tcp_ok = TCPNetworkManager.TCP_OUTGOING_ENABLED && tcp_port > 0 && tcp_remaining > 0;
boolean udp_ok = UDPNetworkManager.UDP_OUTGOING_ENABLED && udp_port > 0 && udp_remaining > 0;
if (tcp_ok && !(prefer_udp_overall && udp_ok)) {
if (makeNewOutgoingConnection(source, item.getAddressString(), tcp_port, udp_port, true, use_crypto, item.getCryptoLevel(), null) == null) {
tcp_remaining--;
num_waiting_establishments++;
remaining--;
}
} else if (udp_ok) {
if (makeNewOutgoingConnection(source, item.getAddressString(), tcp_port, udp_port, false, use_crypto, item.getCryptoLevel(), null) == null) {
udp_remaining--;
num_waiting_establishments++;
remaining--;
}
}
}
}
if (i == 0) {
if (UDPNetworkManager.UDP_OUTGOING_ENABLED && remaining > 0 && udp_remaining > 0 && udp_connections < MAX_UDP_CONNECTIONS) {
doUDPConnectionChecks(remaining);
}
}
}
}
}
// every 5 seconds
if (mainloop_loop_count % MAINLOOP_FIVE_SECOND_INTERVAL == 0) {
boolean do_dup_con_checks = dual_ipv4_ipv6_connection_action != 0 && (mainloop_loop_count % MAINLOOP_TEN_SECOND_INTERVAL == 0) && seeding_mode && has_ipv4 && has_ipv6 && !superSeedMode;
long piece_length = disk_mgr.getPieceLength();
final int DUP_CHECK_MIN_PIECES = 10;
final int min_done = Math.max(1, (int) ((piece_length * DUP_CHECK_MIN_PIECES * 1000) / disk_mgr.getTotalLength()));
final List<PEPeerTransport> peer_transports = peer_transports_cow;
List<PEPeerTransport> interesting_peers = new ArrayList<>(peer_transports.size());
for (int i = 0; i < peer_transports.size(); i++) {
final PEPeerTransport transport = peer_transports.get(i);
if (transport.doTimeoutChecks()) {
continue;
}
// keep-alive check
transport.doKeepAliveCheck();
// speed tuning check
transport.doPerformanceTuningCheck();
if (do_dup_con_checks) {
if (transport.getNetwork() == AENetworkClassifier.AT_PUBLIC) {
int done = transport.getPercentDoneInThousandNotation();
if (done < 1000 && done > min_done) {
interesting_peers.add(transport);
}
}
}
}
if (interesting_peers.size() > 1) {
Collections.sort(interesting_peers, new Comparator<PEPeerTransport>() {
public int compare(PEPeerTransport p1, PEPeerTransport p2) {
return (p1.getPercentDoneInThousandNotation() - p2.getPercentDoneInThousandNotation());
}
});
// look for duplicate connections from a peer over ipv4 + ipv6
int DUP_CHECK_TOLERANCE = Math.max(1, min_done / 2);
List<PEPeerTransport> to_ban = new ArrayList<>();
for (int i = 0; i < interesting_peers.size(); i++) {
PEPeerTransport peer1 = interesting_peers.get(i);
int p1_done = peer1.getPercentDoneInThousandNotation();
boolean p1_ipv6 = peer1.getIp().contains(":");
for (int j = i + 1; j < interesting_peers.size(); j++) {
PEPeerTransport peer2 = interesting_peers.get(j);
int p2_done = peer2.getPercentDoneInThousandNotation();
if (Math.abs(p2_done - p1_done) <= DUP_CHECK_TOLERANCE) {
BitFlags f1 = peer1.getAvailable();
BitFlags f2 = peer2.getAvailable();
if (f1 == null || f2 == null) {
continue;
}
boolean p2_ipv6 = peer2.getIp().contains(":");
if (p1_ipv6 == p2_ipv6) {
continue;
}
String cc_match = null;
PEPeerTransport[] peers = { peer1, peer2 };
for (PEPeerTransport peer : peers) {
String[] details = (String[]) peer.getUserData(DUP_PEER_CC_KEY);
if (details == null) {
try {
details = PeerUtils.getCountryDetails(peer);
} catch (Throwable e) {
}
if (details == null) {
details = new String[0];
}
peer.setUserData(DUP_PEER_CC_KEY, details);
}
if (details.length > 0) {
String cc = details[0];
if (cc_match == null) {
cc_match = cc;
} else if (!cc.equals(cc_match)) {
cc_match = null;
}
} else {
cc_match = null;
break;
}
}
if (cc_match == null) {
continue;
}
boolean[] b1 = f1.flags;
boolean[] b2 = f2.flags;
int same_pieces = 0;
for (int k = 0; k < b1.length; k++) {
if (b1[k] && b2[k]) {
same_pieces++;
}
}
int max_pieces = Math.max(f1.nbSet, f2.nbSet);
if (same_pieces < DUP_CHECK_MIN_PIECES || max_pieces < same_pieces || (same_pieces * 100) / max_pieces < 95) {
continue;
}
String[] ass = new String[2];
int hits = 0;
for (PEPeerTransport peer : peers) {
String as = (String) peer.getUserData(DUP_PEER_AS_KEY);
if (as == null) {
// prevent other lookups regardless
peer.setUserData(DUP_PEER_AS_KEY, "");
try {
network_admin.lookupASN(HostNameToIPResolver.syncResolve(peer.getIp()), new NetworkAdminASNListener() {
@Override
public void success(NetworkAdminASN asn) {
peer.setUserData(DUP_PEER_AS_KEY, asn.getAS());
}
@Override
public void failed(NetworkAdminException error) {
}
});
} catch (Throwable e) {
}
} else if (!as.isEmpty()) {
ass[hits++] = as;
}
}
if (hits == 2 && ass[0].equals(ass[1])) {
PEPeerTransport peer_to_ban;
if (dual_ipv4_ipv6_connection_action == 1) {
if (p1_ipv6) {
peer_to_ban = peer2;
} else {
peer_to_ban = peer1;
}
} else {
if (p1_ipv6) {
peer_to_ban = peer1;
} else {
peer_to_ban = peer2;
}
}
to_ban.add(peer_to_ban);
}
} else {
break;
}
}
}
for (PEPeerTransport peer : to_ban) {
String msg = "Duplicate IPv4 and IPv6 connection detected";
ip_filter.ban(peer.getIp(), getDisplayName() + ": " + msg, false);
closeAndRemovePeer(peer, msg, true);
}
}
}
// every 10 seconds check for connected + banned peers
if (mainloop_loop_count % MAINLOOP_TEN_SECOND_INTERVAL == 0) {
final long last_update = ip_filter.getLastUpdateTime();
if (last_update != ip_filter_last_update_time) {
ip_filter_last_update_time = last_update;
checkForBannedConnections();
}
}
// every 30 seconds
if (mainloop_loop_count % MAINLOOP_THIRTY_SECOND_INTERVAL == 0) {
// if we're at our connection limit, time out the least-useful
// one so we can establish a possibly-better new connection
optimisticDisconnectCount = 0;
int[] allowed = getMaxNewConnectionsAllowed();
if (allowed[0] + allowed[1] == 0) {
// we've reached limit
doOptimisticDisconnect(false, false, "");
}
}
// sweep over all peers in a 60 second timespan
float percentage = ((mainloop_loop_count % MAINLOOP_SIXTY_SECOND_INTERVAL) + 1F) / (1F * MAINLOOP_SIXTY_SECOND_INTERVAL);
int goal;
if (mainloop_loop_count % MAINLOOP_SIXTY_SECOND_INTERVAL == 0) {
goal = 0;
sweepList = peer_transports_cow;
} else {
goal = (int) Math.floor(percentage * sweepList.size());
}
for (int i = nextPEXSweepIndex; i < goal && i < sweepList.size(); i++) {
// System.out.println(mainloop_loop_count+" %:"+percentage+"
// start:"+nextPEXSweepIndex+" current:"+i+" <"+goal+"/"+sweepList.size());
final PEPeerTransport peer = sweepList.get(i);
peer.updatePeerExchange();
}
nextPEXSweepIndex = goal;
if (mainloop_loop_count % MAINLOOP_SIXTY_SECOND_INTERVAL == 0) {
List<PEPeerTransport> peer_transports = peer_transports_cow;
if (peer_transports.size() > 1) {
Map<String, List<PEPeerTransport>> peer_map = new HashMap<>();
for (PEPeerTransport peer : peer_transports) {
if (peer.isIncoming()) {
continue;
}
if (peer.getPeerState() == PEPeer.CONNECTING && peer.getConnectionState() == PEPeerTransport.CONNECTION_CONNECTING && peer.getLastMessageSentTime() != 0) {
String key = peer.getIp() + ":" + peer.getPort();
List<PEPeerTransport> list = peer_map.get(key);
if (list == null) {
list = new ArrayList<>(1);
peer_map.put(key, list);
}
list.add(peer);
}
}
for (List<PEPeerTransport> list : peer_map.values()) {
if (list.size() >= 2) {
long newest_time = Long.MIN_VALUE;
PEPeerTransport newest_peer = null;
for (PEPeerTransport peer : list) {
long last_sent = peer.getLastMessageSentTime();
if (last_sent > newest_time) {
newest_time = last_sent;
newest_peer = peer;
}
}
for (PEPeerTransport peer : list) {
if (peer != newest_peer) {
if (peer.getPeerState() == PEPeer.CONNECTING && peer.getConnectionState() == PEPeerTransport.CONNECTION_CONNECTING) {
closeAndRemovePeer(peer, "Removing old duplicate connection", false);
}
}
}
}
}
}
}
}
Aggregations