Add a 'trustedpeer' option to keys.dat
If this option is specified in keys.dat then Bitmessage will connect to the host specified there instead of connecting to the hosts in the list of known nodes. It will also stop listening for incoming connections and the timing attack mitigation will be disabled. The expected use case is for example where a user is running a daemon on a dedicated machine in their local network and they occasionally want to check for messages using a second instance of the client on their laptop. In that case it would be much faster to catch up with the messages by directly downloading from the dedicated machine over the LAN. There is no need to connect to multiple peers or to do the timing attack mitigation because the daemon is trusted. The host is specified as hostname:port. Eg, ‘192.168.1.8:8444’.
This commit is contained in:
parent
44f31d7bf9
commit
03263156de
|
@ -22,26 +22,37 @@ class outgoingSynSender(threading.Thread):
|
||||||
self.streamNumber = streamNumber
|
self.streamNumber = streamNumber
|
||||||
self.selfInitiatedConnections = selfInitiatedConnections
|
self.selfInitiatedConnections = selfInitiatedConnections
|
||||||
|
|
||||||
|
def _getPeer(self):
|
||||||
|
# If the user has specified a trusted peer then we'll only
|
||||||
|
# ever connect to that. Otherwise we'll pick a random one from
|
||||||
|
# the known nodes
|
||||||
|
shared.knownNodesLock.acquire()
|
||||||
|
if shared.trustedPeer:
|
||||||
|
peer = shared.trustedPeer
|
||||||
|
shared.knownNodes[self.streamNumber][peer] = time.time()
|
||||||
|
else:
|
||||||
|
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
|
||||||
|
shared.knownNodesLock.release()
|
||||||
|
|
||||||
|
return peer
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
|
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'):
|
while shared.safeConfigGetBoolean('bitmessagesettings', 'sendoutgoingconnections'):
|
||||||
while len(self.selfInitiatedConnections[self.streamNumber]) >= 8: # maximum number of outgoing connections = 8
|
maximumConnections = 1 if shared.trustedPeer else 8 # maximum number of outgoing connections = 8
|
||||||
|
while len(self.selfInitiatedConnections[self.streamNumber]) >= maximumConnections:
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
if shared.shutdown:
|
if shared.shutdown:
|
||||||
break
|
break
|
||||||
random.seed()
|
random.seed()
|
||||||
shared.knownNodesLock.acquire()
|
peer = self._getPeer()
|
||||||
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
|
|
||||||
shared.knownNodesLock.release()
|
|
||||||
shared.alreadyAttemptedConnectionsListLock.acquire()
|
shared.alreadyAttemptedConnectionsListLock.acquire()
|
||||||
while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
|
while peer in shared.alreadyAttemptedConnectionsList or peer.host in shared.connectedHostsList:
|
||||||
shared.alreadyAttemptedConnectionsListLock.release()
|
shared.alreadyAttemptedConnectionsListLock.release()
|
||||||
# print 'choosing new sample'
|
# print 'choosing new sample'
|
||||||
random.seed()
|
random.seed()
|
||||||
shared.knownNodesLock.acquire()
|
peer = self._getPeer()
|
||||||
peer, = random.sample(shared.knownNodes[self.streamNumber], 1)
|
|
||||||
shared.knownNodesLock.release()
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
# Clear out the shared.alreadyAttemptedConnectionsList every half
|
# Clear out the shared.alreadyAttemptedConnectionsList every half
|
||||||
# hour so that this program will again attempt a connection
|
# hour so that this program will again attempt a connection
|
||||||
|
|
|
@ -313,6 +313,14 @@ class receiveDataThread(threading.Thread):
|
||||||
print 'Sending huge inv message with', numberOfObjects, 'objects to just this one peer'
|
print 'Sending huge inv message with', numberOfObjects, 'objects to just this one peer'
|
||||||
self.sendDataThreadQueue.put((0, 'sendRawData', headerData + payload))
|
self.sendDataThreadQueue.put((0, 'sendRawData', headerData + payload))
|
||||||
|
|
||||||
|
def _sleepForTimingAttackMitigation(self, sleepTime):
|
||||||
|
# We don't need to do the timing attack mitigation if we are
|
||||||
|
# only connected to the trusted peer because we can trust the
|
||||||
|
# peer not to attack
|
||||||
|
if sleepTime > 0 and doTimingAttackMitigation and shared.trustedPeer == None:
|
||||||
|
with shared.printLock:
|
||||||
|
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
|
||||||
|
time.sleep(sleepTime)
|
||||||
|
|
||||||
# We have received a broadcast message
|
# We have received a broadcast message
|
||||||
def recbroadcast(self, data):
|
def recbroadcast(self, data):
|
||||||
|
@ -341,10 +349,7 @@ class receiveDataThread(threading.Thread):
|
||||||
|
|
||||||
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
|
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
|
||||||
(time.time() - self.messageProcessingStartTime)
|
(time.time() - self.messageProcessingStartTime)
|
||||||
if sleepTime > 0 and doTimingAttackMitigation:
|
self._sleepForTimingAttackMitigation(sleepTime)
|
||||||
with shared.printLock:
|
|
||||||
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
|
|
||||||
time.sleep(sleepTime)
|
|
||||||
|
|
||||||
# We have received a msg message.
|
# We have received a msg message.
|
||||||
def recmsg(self, data):
|
def recmsg(self, data):
|
||||||
|
@ -373,10 +378,7 @@ class receiveDataThread(threading.Thread):
|
||||||
|
|
||||||
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
|
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
|
||||||
(time.time() - self.messageProcessingStartTime)
|
(time.time() - self.messageProcessingStartTime)
|
||||||
if sleepTime > 0 and doTimingAttackMitigation:
|
self._sleepForTimingAttackMitigation(sleepTime)
|
||||||
with shared.printLock:
|
|
||||||
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
|
|
||||||
time.sleep(sleepTime)
|
|
||||||
|
|
||||||
# We have received a pubkey
|
# We have received a pubkey
|
||||||
def recpubkey(self, data):
|
def recpubkey(self, data):
|
||||||
|
@ -387,11 +389,7 @@ class receiveDataThread(threading.Thread):
|
||||||
lengthOfTimeWeShouldUseToProcessThisMessage = .1
|
lengthOfTimeWeShouldUseToProcessThisMessage = .1
|
||||||
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
|
sleepTime = lengthOfTimeWeShouldUseToProcessThisMessage - \
|
||||||
(time.time() - self.pubkeyProcessingStartTime)
|
(time.time() - self.pubkeyProcessingStartTime)
|
||||||
if sleepTime > 0 and doTimingAttackMitigation:
|
self._sleepForTimingAttackMitigation(sleepTime)
|
||||||
with shared.printLock:
|
|
||||||
print 'Timing attack mitigation: Sleeping for', sleepTime, 'seconds.'
|
|
||||||
time.sleep(sleepTime)
|
|
||||||
|
|
||||||
|
|
||||||
# We have received an inv message
|
# We have received an inv message
|
||||||
def recinv(self, data):
|
def recinv(self, data):
|
||||||
|
|
|
@ -22,6 +22,11 @@ class singleListener(threading.Thread):
|
||||||
self.selfInitiatedConnections = selfInitiatedConnections
|
self.selfInitiatedConnections = selfInitiatedConnections
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
# If there is a trusted peer then we don't want to accept
|
||||||
|
# incoming connections so we'll just abandon the thread
|
||||||
|
if shared.trustedPeer:
|
||||||
|
return
|
||||||
|
|
||||||
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
|
while shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
helper_bootstrap.dns()
|
helper_bootstrap.dns()
|
||||||
|
|
|
@ -12,6 +12,17 @@ from namecoin import ensureNamecoinOptions
|
||||||
|
|
||||||
storeConfigFilesInSameDirectoryAsProgramByDefault = False # The user may de-select Portable Mode in the settings if they want the config files to stay in the application data folder.
|
storeConfigFilesInSameDirectoryAsProgramByDefault = False # The user may de-select Portable Mode in the settings if they want the config files to stay in the application data folder.
|
||||||
|
|
||||||
|
def _loadTrustedPeer():
|
||||||
|
try:
|
||||||
|
trustedPeer = shared.config.get('bitmessagesettings', 'trustedpeer')
|
||||||
|
except ConfigParser.Error:
|
||||||
|
# This probably means the trusted peer wasn't specified so we
|
||||||
|
# can just leave it as None
|
||||||
|
return
|
||||||
|
|
||||||
|
host, port = trustedPeer.split(':')
|
||||||
|
shared.trustedPeer = shared.Peer(host, int(port))
|
||||||
|
|
||||||
def loadConfig():
|
def loadConfig():
|
||||||
if shared.appdata:
|
if shared.appdata:
|
||||||
shared.config.read(shared.appdata + 'keys.dat')
|
shared.config.read(shared.appdata + 'keys.dat')
|
||||||
|
@ -122,6 +133,8 @@ def loadConfig():
|
||||||
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
|
with open(shared.appdata + 'keys.dat', 'wb') as configfile:
|
||||||
shared.config.write(configfile)
|
shared.config.write(configfile)
|
||||||
|
|
||||||
|
_loadTrustedPeer()
|
||||||
|
|
||||||
def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
|
def isOurOperatingSystemLimitedToHavingVeryFewHalfOpenConnections():
|
||||||
try:
|
try:
|
||||||
VER_THIS=StrictVersion(platform.version())
|
VER_THIS=StrictVersion(platform.version())
|
||||||
|
|
|
@ -92,6 +92,18 @@ namecoinDefaultRpcPort = "8336"
|
||||||
# binary distributions vs source distributions.
|
# binary distributions vs source distributions.
|
||||||
frozen = getattr(sys,'frozen', None)
|
frozen = getattr(sys,'frozen', None)
|
||||||
|
|
||||||
|
# If the trustedpeer option is specified in keys.dat then this will
|
||||||
|
# contain a Peer which will be connected to instead of using the
|
||||||
|
# addresses advertised by other peers. The client will only connect to
|
||||||
|
# this peer and the timing attack mitigation will be disabled in order
|
||||||
|
# to download data faster. The expected use case is where the user has
|
||||||
|
# a fast connection to a trusted server where they run a BitMessage
|
||||||
|
# daemon permanently. If they then run a second instance of the client
|
||||||
|
# on a local machine periodically when they want to check for messages
|
||||||
|
# it will sync with the network a lot faster without compromising
|
||||||
|
# security.
|
||||||
|
trustedPeer = None
|
||||||
|
|
||||||
def isInSqlInventory(hash):
|
def isInSqlInventory(hash):
|
||||||
queryreturn = sqlQuery('''select hash from inventory where hash=?''', hash)
|
queryreturn = sqlQuery('''select hash from inventory where hash=?''', hash)
|
||||||
return queryreturn != []
|
return queryreturn != []
|
||||||
|
|
Loading…
Reference in New Issue
Block a user