From 339e375958e626fbcbb396e14ef569851b74ba0c Mon Sep 17 00:00:00 2001 From: Peter Surda Date: Mon, 27 Feb 2017 23:31:12 +0100 Subject: [PATCH] Bootstrap provider mode and minor knownNodes changes - if knownNodes grows to 20000, instead of ignoring new nodes, forget the 1000 oldest ones - drop connection after sendaddr if too many connections, even if it's an outbound one - if maximum total connections are lower than maximum outbound connections, active bootstrap provider mode - in this mode, check all addresses received before announcing them - so basically it only annouces those addresses it successfully connected to --- src/class_receiveDataThread.py | 44 ++++++++++++++++++++++++---------- src/class_singleListener.py | 7 +++++- src/knownnodes.py | 11 +++++++++ 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index 83941612..3b9e6ec9 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -386,10 +386,13 @@ class receiveDataThread(threading.Thread): stream, 'advertisepeer', dataToSend)) self.sendaddr() # This is one large addr message to this one peer. - if not self.initiatedConnection and len(shared.connectedHostsList) > \ + if len(shared.connectedHostsList) > \ BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections", 200): logger.info ('We are connected to too many people. Closing connection.') - self.sendDataThreadQueue.put((0, 'sendRawData', protocol.assembleErrorMessage(fatal=2, errorText="Server full, please try again later."))) + if self.initiatedConnection: + self.sendDataThreadQueue.put((0, 'sendRawData', protocol.assembleErrorMessage(fatal=2, errorText="Thank you for providing a listening node."))) + else: + self.sendDataThreadQueue.put((0, 'sendRawData', protocol.assembleErrorMessage(fatal=2, errorText="Server full, please try again later."))) self.sendDataThreadQueue.put((0, 'shutdown','no data')) return self.sendBigInv() @@ -622,18 +625,28 @@ class receiveDataThread(threading.Thread): knownnodes.knownNodes[recaddrStream] = {} peerFromAddrMessage = state.Peer(hostStandardFormat, recaddrPort) if peerFromAddrMessage not in knownnodes.knownNodes[recaddrStream]: - if len(knownnodes.knownNodes[recaddrStream]) < 20000 and timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): # If we have more than 20000 nodes in our list already then just forget about adding more. Also, make sure that the time that someone else received a message from this node is within three hours from now. - with knownnodes.knownNodesLock: - knownnodes.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode + knownnodes.trimKnownNodes(recAddrStream) + # only if recent + if timeSomeoneElseReceivedMessageFromThisNode > (int(time.time()) - 10800) and timeSomeoneElseReceivedMessageFromThisNode < (int(time.time()) + 10800): logger.debug('added new node ' + str(peerFromAddrMessage) + ' to knownNodes in stream ' + str(recaddrStream)) - + # bootstrap provider? + if BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections') >= \ + BMConfigParser().safeGetInt('bitmessagesettings', 'maxtotalconnections', 200): + with knownnodes.knownNodesLock: + knownnodes.knownNodes[recaddrStream][peerFromAddrMessage] = int(time.time()) - 10800 + # normal mode + else: + with knownnodes.knownNodesLock: + knownnodes.knownNodes[recaddrStream][peerFromAddrMessage] = timeSomeoneElseReceivedMessageFromThisNode + hostDetails = ( + timeSomeoneElseReceivedMessageFromThisNode, + recaddrStream, recaddrServices, hostStandardFormat, recaddrPort) + protocol.broadcastToSendDataQueues(( + recaddrStream, 'advertisepeer', hostDetails)) shared.needToWriteKnownNodesToDisk = True - hostDetails = ( - timeSomeoneElseReceivedMessageFromThisNode, - recaddrStream, recaddrServices, hostStandardFormat, recaddrPort) - protocol.broadcastToSendDataQueues(( - recaddrStream, 'advertisepeer', hostDetails)) - else: + # only update if normal mode + elif BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections') < \ + BMConfigParser().safeGetInt('bitmessagesettings', 'maxtotalconnections', 200): timeLastReceivedMessageFromThisNode = knownnodes.knownNodes[recaddrStream][ peerFromAddrMessage] if (timeLastReceivedMessageFromThisNode < timeSomeoneElseReceivedMessageFromThisNode) and (timeSomeoneElseReceivedMessageFromThisNode < int(time.time())+900): # 900 seconds for wiggle-room in case other nodes' clocks aren't quite right. @@ -822,7 +835,12 @@ class receiveDataThread(threading.Thread): for stream in self.remoteStreams: knownnodes.knownNodes[stream][state.Peer(self.peer.host, self.remoteNodeIncomingPort)] = int(time.time()) if not self.initiatedConnection: - knownnodes.knownNodes[stream][state.Peer(self.peer.host, self.remoteNodeIncomingPort)] -= 7200 # penalise inbound, 2 hours + # bootstrap provider? + if BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections') >= \ + BMConfigParser().safeGetInt('bitmessagesettings', 'maxtotalconnections', 200): + knownnodes.knownNodes[stream][state.Peer(self.peer.host, self.remoteNodeIncomingPort)] -= 10800 # penalise inbound, 3 hours + else: + knownnodes.knownNodes[stream][state.Peer(self.peer.host, self.remoteNodeIncomingPort)] -= 7200 # penalise inbound, 2 hours shared.needToWriteKnownNodesToDisk = True self.sendverack() diff --git a/src/class_singleListener.py b/src/class_singleListener.py index 322f4b67..5fe202e9 100644 --- a/src/class_singleListener.py +++ b/src/class_singleListener.py @@ -139,7 +139,12 @@ class singleListener(threading.Thread, StoppableThread): # share the same external IP. This is here to prevent # connection flooding. # permit repeated connections from Tor - if HOST in shared.connectedHostsList and (".onion" not in BMConfigParser().get('bitmessagesettings', 'onionhostname') or not protocol.checkSocksIP(HOST)): + if HOST in shared.connectedHostsList and \ + (".onion" not in BMConfigParser().get('bitmessagesettings', 'onionhostname') or not protocol.checkSocksIP(HOST)): + # bootstrap provider? Then accept, we'll most likely drop it a little bit later + if BMConfigParser().safeGetInt('bitmessagesettings', 'maxoutboundconnections') >= \ + BMConfigParser().safeGetInt('bitmessagesettings', 'maxtotalconnections', 200): + break socketObject.close() logger.info('We are already connected to ' + str(HOST) + '. Ignoring connection.') else: diff --git a/src/knownnodes.py b/src/knownnodes.py index b68f135f..4979d7da 100644 --- a/src/knownnodes.py +++ b/src/knownnodes.py @@ -6,9 +6,20 @@ import state knownNodesLock = threading.Lock() knownNodes = {} +knownNodesMax = 20000 +knownNodesTrimAmount = 2000 + def saveKnownNodes(dirName = None): if dirName is None: dirName = state.appdata with knownNodesLock: with open(dirName + 'knownnodes.dat', 'wb') as output: pickle.dump(knownNodes, output) + +def trimKnownNodes(recAddrStream = 1): + if len(knownNodes[recAddrStream]) < knownNodesMax: + return + with knownNodesLock: + oldestList = sorted(knownNodes[recAddrStream], key=knownNodes[recAddrStream].get)[:knownNodeTrimAmount] + for oldest in oldestList: + del knownNodes[recAddrStream][oldest]