commit
98cdf287c4
2
setup.py
2
setup.py
|
@ -83,7 +83,7 @@ if __name__ == "__main__":
|
|||
'qrcode': ['qrcode'],
|
||||
'pyopencl': ['pyopencl'],
|
||||
'notify2': ['notify2'],
|
||||
'sound:platform_system=="Windows"': ['winsound']
|
||||
'sound;platform_system=="Windows"': ['winsound']
|
||||
},
|
||||
classifiers=[
|
||||
"License :: OSI Approved :: MIT License"
|
||||
|
|
|
@ -342,13 +342,21 @@ class Main:
|
|||
sleep(1)
|
||||
|
||||
def daemonize(self):
|
||||
grandfatherPid = os.getpid()
|
||||
parentPid = None
|
||||
try:
|
||||
if os.fork():
|
||||
# unlock
|
||||
shared.thisapp.cleanup()
|
||||
# wait until grandchild ready
|
||||
while True:
|
||||
sleep(1)
|
||||
os._exit(0)
|
||||
except AttributeError:
|
||||
# fork not implemented
|
||||
pass
|
||||
else:
|
||||
parentPid = os.getpid()
|
||||
shared.thisapp.lock() # relock
|
||||
os.umask(0)
|
||||
try:
|
||||
|
@ -358,6 +366,11 @@ class Main:
|
|||
pass
|
||||
try:
|
||||
if os.fork():
|
||||
# unlock
|
||||
shared.thisapp.cleanup()
|
||||
# wait until child ready
|
||||
while True:
|
||||
sleep(1)
|
||||
os._exit(0)
|
||||
except AttributeError:
|
||||
# fork not implemented
|
||||
|
@ -374,6 +387,10 @@ class Main:
|
|||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
if parentPid:
|
||||
# signal ready
|
||||
os.kill(parentPid, signal.SIGTERM)
|
||||
os.kill(grandfatherPid, signal.SIGTERM)
|
||||
|
||||
def setSignalHandler(self):
|
||||
signal.signal(signal.SIGINT, helper_generic.signal_handler)
|
||||
|
|
|
@ -69,7 +69,7 @@ import queues
|
|||
import shutdown
|
||||
import state
|
||||
from statusbar import BMStatusBar
|
||||
import throttle
|
||||
from network.asyncore_pollchoose import set_rates
|
||||
from version import softwareVersion
|
||||
import sound
|
||||
|
||||
|
@ -2287,16 +2287,16 @@ class MyForm(settingsmixin.SMainWindow):
|
|||
int(float(self.settingsDialogInstance.ui.lineEditMaxDownloadRate.text()))))
|
||||
BMConfigParser().set('bitmessagesettings', 'maxuploadrate', str(
|
||||
int(float(self.settingsDialogInstance.ui.lineEditMaxUploadRate.text()))))
|
||||
except:
|
||||
except ValueError:
|
||||
QMessageBox.about(self, _translate("MainWindow", "Number needed"), _translate(
|
||||
"MainWindow", "Your maximum download and upload rate must be numbers. Ignoring what you typed."))
|
||||
else:
|
||||
set_rates(BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"),
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate"))
|
||||
|
||||
BMConfigParser().set('bitmessagesettings', 'maxoutboundconnections', str(
|
||||
int(float(self.settingsDialogInstance.ui.lineEditMaxOutboundConnections.text()))))
|
||||
|
||||
throttle.SendThrottle().resetLimit()
|
||||
throttle.ReceiveThrottle().resetLimit()
|
||||
|
||||
BMConfigParser().set('bitmessagesettings', 'namecoinrpctype',
|
||||
self.settingsDialogInstance.getNamecoinType())
|
||||
BMConfigParser().set('bitmessagesettings', 'namecoinrpchost', str(
|
||||
|
|
|
@ -51,7 +51,7 @@ def signal_handler(signal, frame):
|
|||
raise SystemExit
|
||||
if "PoolWorker" in current_process().name:
|
||||
raise SystemExit
|
||||
if current_thread().name != "PyBitmessage":
|
||||
if current_thread().name not in ("PyBitmessage", "MainThread"):
|
||||
return
|
||||
logger.error("Got signal %i", signal)
|
||||
if BMConfigParser().safeGetBoolean('bitmessagesettings', 'daemon'):
|
||||
|
|
|
@ -75,7 +75,7 @@ class AdvancedDispatcher(asyncore.dispatcher):
|
|||
def writable(self):
|
||||
self.uploadChunk = AdvancedDispatcher._buf_len
|
||||
if asyncore.maxUploadRate > 0:
|
||||
self.uploadChunk = asyncore.uploadBucket
|
||||
self.uploadChunk = int(asyncore.uploadBucket)
|
||||
self.uploadChunk = min(self.uploadChunk, len(self.write_buf))
|
||||
return asyncore.dispatcher.writable(self) and \
|
||||
(self.connecting or (self.connected and self.uploadChunk > 0))
|
||||
|
@ -83,7 +83,7 @@ class AdvancedDispatcher(asyncore.dispatcher):
|
|||
def readable(self):
|
||||
self.downloadChunk = AdvancedDispatcher._buf_len
|
||||
if asyncore.maxDownloadRate > 0:
|
||||
self.downloadChunk = asyncore.downloadBucket
|
||||
self.downloadChunk = int(asyncore.downloadBucket)
|
||||
try:
|
||||
if self.expectBytes > 0 and not self.fullyEstablished:
|
||||
self.downloadChunk = min(self.downloadChunk, self.expectBytes - len(self.read_buf))
|
||||
|
|
|
@ -112,6 +112,8 @@ uploadBucket = 0
|
|||
sentBytes = 0
|
||||
|
||||
def read(obj):
|
||||
if not can_receive():
|
||||
return
|
||||
try:
|
||||
obj.handle_read_event()
|
||||
except _reraised_exceptions:
|
||||
|
@ -120,6 +122,8 @@ def read(obj):
|
|||
obj.handle_error()
|
||||
|
||||
def write(obj):
|
||||
if not can_send():
|
||||
return
|
||||
try:
|
||||
obj.handle_write_event()
|
||||
except _reraised_exceptions:
|
||||
|
@ -129,19 +133,25 @@ def write(obj):
|
|||
|
||||
def set_rates(download, upload):
|
||||
global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp
|
||||
maxDownloadRate = float(download)
|
||||
maxUploadRate = float(upload)
|
||||
maxDownloadRate = float(download) * 1024
|
||||
maxUploadRate = float(upload) * 1024
|
||||
downloadBucket = maxDownloadRate
|
||||
uploadBucket = maxUploadRate
|
||||
downloadTimestamp = time.time()
|
||||
uploadTimestamp = time.time()
|
||||
|
||||
def can_receive():
|
||||
return maxDownloadRate == 0 or downloadBucket > 0
|
||||
|
||||
def can_send():
|
||||
return maxUploadRate == 0 or uploadBucket > 0
|
||||
|
||||
def update_received(download=0):
|
||||
global receivedBytes, downloadBucket, downloadTimestamp
|
||||
currentTimestamp = time.time()
|
||||
receivedBytes += download
|
||||
if maxDownloadRate > 0:
|
||||
bucketIncrease = int(maxDownloadRate * (currentTimestamp - downloadTimestamp))
|
||||
bucketIncrease = maxDownloadRate * (currentTimestamp - downloadTimestamp)
|
||||
downloadBucket += bucketIncrease
|
||||
if downloadBucket > maxDownloadRate:
|
||||
downloadBucket = int(maxDownloadRate)
|
||||
|
@ -153,7 +163,7 @@ def update_sent(upload=0):
|
|||
currentTimestamp = time.time()
|
||||
sentBytes += upload
|
||||
if maxUploadRate > 0:
|
||||
bucketIncrease = int(maxUploadRate * (currentTimestamp - uploadTimestamp))
|
||||
bucketIncrease = maxUploadRate * (currentTimestamp - uploadTimestamp)
|
||||
uploadBucket += bucketIncrease
|
||||
if uploadBucket > maxUploadRate:
|
||||
uploadBucket = int(maxUploadRate)
|
||||
|
@ -170,9 +180,9 @@ def _exception(obj):
|
|||
|
||||
def readwrite(obj, flags):
|
||||
try:
|
||||
if flags & select.POLLIN:
|
||||
if flags & select.POLLIN and can_receive():
|
||||
obj.handle_read_event()
|
||||
if flags & select.POLLOUT:
|
||||
if flags & select.POLLOUT and can_send():
|
||||
obj.handle_write_event()
|
||||
if flags & select.POLLPRI:
|
||||
obj.handle_expt_event()
|
||||
|
|
|
@ -8,23 +8,28 @@ from network.dandelion import Dandelion
|
|||
import protocol
|
||||
import state
|
||||
|
||||
class BMObjectInsufficientPOWError(Exception): pass
|
||||
class BMObjectInsufficientPOWError(Exception):
|
||||
errorCodes = ("Insufficient proof of work")
|
||||
|
||||
|
||||
class BMObjectInvalidDataError(Exception): pass
|
||||
class BMObjectInvalidDataError(Exception):
|
||||
errorCodes = ("Data invalid")
|
||||
|
||||
|
||||
class BMObjectExpiredError(Exception): pass
|
||||
class BMObjectExpiredError(Exception):
|
||||
errorCodes = ("Object expired")
|
||||
|
||||
|
||||
class BMObjectUnwantedStreamError(Exception): pass
|
||||
class BMObjectUnwantedStreamError(Exception):
|
||||
errorCodes = ("Object in unwanted stream")
|
||||
|
||||
|
||||
class BMObjectInvalidError(Exception): pass
|
||||
class BMObjectInvalidError(Exception):
|
||||
errorCodes = ("Invalid object")
|
||||
|
||||
|
||||
class BMObjectAlreadyHaveError(Exception):
|
||||
pass
|
||||
errorCodes = ("Already have this object")
|
||||
|
||||
|
||||
class BMObject(object):
|
||||
|
|
|
@ -24,13 +24,16 @@ import shared
|
|||
import state
|
||||
import protocol
|
||||
|
||||
class BMProtoError(ProxyError): pass
|
||||
class BMProtoError(ProxyError):
|
||||
errorCodes = ("Protocol error")
|
||||
|
||||
|
||||
class BMProtoInsufficientDataError(BMProtoError): pass
|
||||
class BMProtoInsufficientDataError(BMProtoError):
|
||||
errorCodes = ("Insufficient data")
|
||||
|
||||
|
||||
class BMProtoExcessiveDataError(BMProtoError): pass
|
||||
class BMProtoExcessiveDataError(BMProtoError):
|
||||
errorCodes = ("Too much data")
|
||||
|
||||
|
||||
class BMProto(AdvancedDispatcher, ObjectTracker):
|
||||
|
@ -494,6 +497,20 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
|
|||
return False
|
||||
except:
|
||||
pass
|
||||
if not self.isOutbound:
|
||||
# incoming from a peer we're connected to as outbound, or server full
|
||||
# report the same error to counter deanonymisation
|
||||
if state.Peer(self.destination.host, self.peerNode.port) in \
|
||||
network.connectionpool.BMConnectionPool().inboundConnections or \
|
||||
len(network.connectionpool.BMConnectionPool().inboundConnections) + \
|
||||
len(network.connectionpool.BMConnectionPool().outboundConnections) > \
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"):
|
||||
self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
|
||||
errorText="Server full, please try again later."))
|
||||
logger.debug ("Closed connection to %s due to server full or duplicate inbound/outbound.",
|
||||
str(self.destination))
|
||||
return False
|
||||
if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce):
|
||||
self.append_write_buf(protocol.assembleErrorMessage(fatal=2,
|
||||
errorText="I'm connected to myself. Closing connection."))
|
||||
|
|
|
@ -22,8 +22,8 @@ import state
|
|||
class BMConnectionPool(object):
|
||||
def __init__(self):
|
||||
asyncore.set_rates(
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate") * 1024,
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate") * 1024)
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"),
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate"))
|
||||
self.outboundConnections = {}
|
||||
self.inboundConnections = {}
|
||||
self.listeningSockets = {}
|
||||
|
@ -65,12 +65,18 @@ class BMConnectionPool(object):
|
|||
def getConnectionByAddr(self, addr):
|
||||
if addr in self.inboundConnections:
|
||||
return self.inboundConnections[addr]
|
||||
if addr.host in self.inboundConnections:
|
||||
return self.inboundConnections[addr.host]
|
||||
try:
|
||||
if addr.host in self.inboundConnections:
|
||||
return self.inboundConnections[addr.host]
|
||||
except AttributeError:
|
||||
pass
|
||||
if addr in self.outboundConnections:
|
||||
return self.outboundConnections[addr]
|
||||
if addr.host in self.udpSockets:
|
||||
return self.udpSockets[addr.host]
|
||||
try:
|
||||
if addr.host in self.udpSockets:
|
||||
return self.udpSockets[addr.host]
|
||||
except AttributeError:
|
||||
pass
|
||||
raise KeyError
|
||||
|
||||
def isAlreadyConnected(self, nodeid):
|
||||
|
|
|
@ -43,7 +43,7 @@ class DownloadThread(threading.Thread, StoppableThread):
|
|||
connections = BMConnectionPool().inboundConnections.values() + BMConnectionPool().outboundConnections.values()
|
||||
random.shuffle(connections)
|
||||
try:
|
||||
requestChunk = max(int(DownloadThread.maxRequestChunk / len(connections)), 1)
|
||||
requestChunk = max(int(min(DownloadThread.maxRequestChunk, len(missingObjects)) / len(connections)), 1)
|
||||
except ZeroDivisionError:
|
||||
requestChunk = 1
|
||||
for i in connections:
|
||||
|
@ -63,15 +63,14 @@ class DownloadThread(threading.Thread, StoppableThread):
|
|||
except KeyError:
|
||||
continue
|
||||
random.shuffle(request)
|
||||
if len(request) > requestChunk - downloadPending:
|
||||
request = request[:max(1, requestChunk - downloadPending)]
|
||||
if not request:
|
||||
continue
|
||||
if len(request) > requestChunk - downloadPending:
|
||||
request = request[:requestChunk - downloadPending]
|
||||
# mark them as pending
|
||||
for k in request:
|
||||
i.objectsNewToMe[k] = False
|
||||
missingObjects[k] = now
|
||||
|
||||
payload = bytearray()
|
||||
payload.extend(addresses.encodeVarint(len(request)))
|
||||
for chunk in request:
|
||||
|
|
|
@ -29,6 +29,7 @@ class ObjectTracker(object):
|
|||
invInitialCapacity = 50000
|
||||
invErrorRate = 0.03
|
||||
trackingExpires = 3600
|
||||
initialTimeOffset = 60
|
||||
|
||||
def __init__(self):
|
||||
self.objectsNewToMe = {}
|
||||
|
@ -87,7 +88,7 @@ class ObjectTracker(object):
|
|||
if hashId in Dandelion().hashMap:
|
||||
Dandelion().fluffTrigger(hashId)
|
||||
if hashId not in missingObjects:
|
||||
missingObjects[hashId] = time.time()
|
||||
missingObjects[hashId] = time.time() - ObjectTracker.initialTimeOffset
|
||||
with self.objectsNewToMeLock:
|
||||
self.objectsNewToMe[hashId] = True
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import state
|
|||
class ProxyError(Exception):
|
||||
errorCodes = ("UnknownError")
|
||||
|
||||
def __init__(self, code):
|
||||
def __init__(self, code=-1):
|
||||
self.code = code
|
||||
try:
|
||||
self.message = self.__class__.errorCodes[self.code]
|
||||
|
|
|
@ -180,7 +180,7 @@ class TCPConnection(BMProto, TLSDispatcher):
|
|||
payload += hash
|
||||
objectCount += 1
|
||||
if objectCount >= BMProto.maxObjectCount:
|
||||
self.sendChunk()
|
||||
sendChunk()
|
||||
payload = b''
|
||||
objectCount = 0
|
||||
|
||||
|
@ -292,7 +292,10 @@ class TCPServer(AdvancedDispatcher):
|
|||
if len(network.connectionpool.BMConnectionPool().inboundConnections) + \
|
||||
len(network.connectionpool.BMConnectionPool().outboundConnections) > \
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"):
|
||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10:
|
||||
# 10 is a sort of buffer, in between it will go through the version handshake
|
||||
# and return an error to the peer
|
||||
logger.warning("Server full, dropping connection")
|
||||
sock.close()
|
||||
return
|
||||
try:
|
||||
|
|
|
@ -9,14 +9,12 @@ from helper_sql import sqlQuery, sqlStoredProcedure
|
|||
from helper_threading import StoppableThread
|
||||
from knownnodes import saveKnownNodes
|
||||
from inventory import Inventory
|
||||
import protocol
|
||||
from queues import addressGeneratorQueue, objectProcessorQueue, UISignalQueue, workerQueue
|
||||
import shared
|
||||
import state
|
||||
|
||||
def doCleanShutdown():
|
||||
state.shutdown = 1 #Used to tell proof of work worker threads and the objectProcessorThread to exit.
|
||||
protocol.broadcastToSendDataQueues((0, 'shutdown', 'no data'))
|
||||
objectProcessorQueue.put(('checkShutdownVariable', 'no data'))
|
||||
for thread in threading.enumerate():
|
||||
if thread.isAlive() and isinstance(thread, StoppableThread):
|
||||
|
|
|
@ -78,6 +78,15 @@ class singleinstance:
|
|||
return
|
||||
if self.daemon and self.lockPid == os.getpid():
|
||||
# these are the two initial forks while daemonizing
|
||||
try:
|
||||
if sys.platform == 'win32':
|
||||
if hasattr(self, 'fd'):
|
||||
os.close(self.fd)
|
||||
else:
|
||||
fcntl.lockf(self.fp, fcntl.LOCK_UN)
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
return
|
||||
print "Cleaning up lockfile"
|
||||
try:
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
softwareName = 'PyBitmessage'
|
||||
softwareVersion = '0.6.2'
|
||||
|
|
Reference in New Issue
Block a user