flake8 in network package

This commit is contained in:
Dmitri Bogomolov 2018-07-17 14:28:56 +03:00
parent 0eb4f46923
commit 996e71ae6f
Signed by untrusted user: g1itch
GPG Key ID: 720A756F18DEED13
7 changed files with 578 additions and 371 deletions

View File

@ -5,26 +5,25 @@ import struct
import time import time
from binascii import hexlify from binascii import hexlify
import addresses
import connectionpool
import knownnodes
import protocol
import state
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
from inventory import Inventory from inventory import Inventory
import knownnodes
from network.advanceddispatcher import AdvancedDispatcher from network.advanceddispatcher import AdvancedDispatcher
from network.dandelion import Dandelion from network.dandelion import Dandelion
from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, \ from network.bmobject import (
BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError,
import network.connectionpool BMObjectExpiredError, BMObjectUnwantedStreamError,
BMObjectInvalidError, BMObjectAlreadyHaveError)
from network.node import Node from network.node import Node
from network.objectracker import ObjectTracker
from network.proxy import ProxyError from network.proxy import ProxyError
from objectracker import missingObjects from objectracker import missingObjects, ObjectTracker
from randomtrackingdict import RandomTrackingDict
import addresses
from queues import objectProcessorQueue, portCheckerQueue, invQueue, addrQueue from queues import objectProcessorQueue, portCheckerQueue, invQueue, addrQueue
import state from randomtrackingdict import RandomTrackingDict
import protocol
class BMProtoError(ProxyError): class BMProtoError(ProxyError):
@ -73,62 +72,70 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
self.object = None self.object = None
def state_bm_header(self): def state_bm_header(self):
self.magic, self.command, self.payloadLength, self.checksum = protocol.Header.unpack(self.read_buf[:protocol.Header.size]) self.magic, self.command, self.payloadLength, self.checksum = \
protocol.Header.unpack(self.read_buf[:protocol.Header.size])
self.command = self.command.rstrip('\x00') self.command = self.command.rstrip('\x00')
if self.magic != 0xE9BEB4D9: if self.magic != 0xE9BEB4D9:
# skip 1 byte in order to sync # skip 1 byte in order to sync
self.set_state("bm_header", length=1) self.set_state("bm_header", length=1)
self.bm_proto_reset() self.bm_proto_reset()
logger.debug("Bad magic") logger.debug('Bad magic')
if self.socket.type == socket.SOCK_STREAM: if self.socket.type == socket.SOCK_STREAM:
self.close_reason = "Bad magic" self.close_reason = "Bad magic"
self.set_state("close") self.set_state("close")
return False return False
if self.payloadLength > BMProto.maxMessageSize: if self.payloadLength > BMProto.maxMessageSize:
self.invalid = True self.invalid = True
self.set_state("bm_command", length=protocol.Header.size, expectBytes=self.payloadLength) self.set_state(
"bm_command",
length=protocol.Header.size, expectBytes=self.payloadLength)
return True return True
def state_bm_command(self): def state_bm_command(self):
self.payload = self.read_buf[:self.payloadLength] self.payload = self.read_buf[:self.payloadLength]
if self.checksum != hashlib.sha512(self.payload).digest()[0:4]: if self.checksum != hashlib.sha512(self.payload).digest()[0:4]:
logger.debug("Bad checksum, ignoring") logger.debug('Bad checksum, ignoring')
self.invalid = True self.invalid = True
retval = True retval = True
if not self.fullyEstablished and self.command not in ("error", "version", "verack"): if not self.fullyEstablished and self.command not in (
logger.error("Received command %s before connection was fully established, ignoring", self.command) "error", "version", "verack"):
logger.error(
'Received command %s before connection was fully'
' established, ignoring', self.command)
self.invalid = True self.invalid = True
if not self.invalid: if not self.invalid:
try: try:
retval = getattr(self, "bm_command_" + str(self.command).lower())() retval = getattr(
self, "bm_command_" + str(self.command).lower())()
except AttributeError: except AttributeError:
# unimplemented command # unimplemented command
logger.debug("unimplemented command %s", self.command) logger.debug('unimplemented command %s', self.command)
except BMProtoInsufficientDataError: except BMProtoInsufficientDataError:
logger.debug("packet length too short, skipping") logger.debug('packet length too short, skipping')
except BMProtoExcessiveDataError: except BMProtoExcessiveDataError:
logger.debug("too much data, skipping") logger.debug('too much data, skipping')
except BMObjectInsufficientPOWError: except BMObjectInsufficientPOWError:
logger.debug("insufficient PoW, skipping") logger.debug('insufficient PoW, skipping')
except BMObjectInvalidDataError: except BMObjectInvalidDataError:
logger.debug("object invalid data, skipping") logger.debug('object invalid data, skipping')
except BMObjectExpiredError: except BMObjectExpiredError:
logger.debug("object expired, skipping") logger.debug('object expired, skipping')
except BMObjectUnwantedStreamError: except BMObjectUnwantedStreamError:
logger.debug("object not in wanted stream, skipping") logger.debug('object not in wanted stream, skipping')
except BMObjectInvalidError: except BMObjectInvalidError:
logger.debug("object invalid, skipping") logger.debug('object invalid, skipping')
except BMObjectAlreadyHaveError: except BMObjectAlreadyHaveError:
logger.debug("%s:%i already got object, skipping", self.destination.host, self.destination.port) logger.debug(
'%(host)s:%(port)i already got object, skipping',
self.destination._asdict())
except struct.error: except struct.error:
logger.debug("decoding error, skipping") logger.debug('decoding error, skipping')
elif self.socket.type == socket.SOCK_DGRAM: elif self.socket.type == socket.SOCK_DGRAM:
# broken read, ignore # broken read, ignore
pass pass
else: else:
#print "Skipping command %s due to invalid data" % (self.command) logger.debug('Closing due to invalid command %s', self.command)
logger.debug("Closing due to invalid command %s", self.command) self.close_reason = "Invalid command %s" % self.command
self.close_reason = "Invalid command %s" % (self.command)
self.set_state("close") self.set_state("close")
return False return False
if retval: if retval:
@ -138,7 +145,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return True return True
def decode_payload_string(self, length): def decode_payload_string(self, length):
value = self.payload[self.payloadOffset:self.payloadOffset+length] value = self.payload[self.payloadOffset:self.payloadOffset + length]
self.payloadOffset += length self.payloadOffset += length
return value return value
@ -148,6 +155,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return value return value
def decode_payload_node(self): def decode_payload_node(self):
# protocol.checkIPAddress()
services, host, port = self.decode_payload_content("Q16sH") services, host, port = self.decode_payload_content("Q16sH")
if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF': if host[0:12] == '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
host = socket.inet_ntop(socket.AF_INET, str(host[12:16])) host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
@ -157,13 +165,13 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
else: else:
host = socket.inet_ntop(socket.AF_INET6, str(host)) host = socket.inet_ntop(socket.AF_INET6, str(host))
if host == "": if host == "":
# This can happen on Windows systems which are not 64-bit compatible # This can happen on Windows systems which are not 64-bit
# so let us drop the IPv6 address. # compatible so let us drop the IPv6 address.
host = socket.inet_ntop(socket.AF_INET, str(host[12:16])) host = socket.inet_ntop(socket.AF_INET, str(host[12:16]))
return Node(services, host, port) return Node(services, host, port)
def decode_payload_content(self, pattern = "v"): def decode_payload_content(self, pattern="v"):
# L = varint indicating the length of the next array # L = varint indicating the length of the next array
# l = varint indicating the length of the next item # l = varint indicating the length of the next item
# v = varint (or array) # v = varint (or array)
@ -182,13 +190,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return self.decode_payload_node() return self.decode_payload_node()
if char == "H": if char == "H":
self.payloadOffset += 2 self.payloadOffset += 2
return struct.unpack(">H", self.payload[self.payloadOffset-2:self.payloadOffset])[0] return struct.unpack(">H", self.payload[
self.payloadOffset - 2:self.payloadOffset])[0]
if char == "I": if char == "I":
self.payloadOffset += 4 self.payloadOffset += 4
return struct.unpack(">I", self.payload[self.payloadOffset-4:self.payloadOffset])[0] return struct.unpack(">I", self.payload[
self.payloadOffset - 4:self.payloadOffset])[0]
if char == "Q": if char == "Q":
self.payloadOffset += 8 self.payloadOffset += 8
return struct.unpack(">Q", self.payload[self.payloadOffset-8:self.payloadOffset])[0] return struct.unpack(">Q", self.payload[
self.payloadOffset - 8:self.payloadOffset])[0]
size = None size = None
isArray = False isArray = False
@ -201,16 +212,11 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# retval (array) # retval (array)
parserStack = [[1, 1, False, pattern, 0, []]] parserStack = [[1, 1, False, pattern, 0, []]]
#try:
# sys._getframe(200)
# logger.error("Stack depth warning, pattern: %s", pattern)
# return
#except ValueError:
# pass
while True: while True:
i = parserStack[-1][3][parserStack[-1][4]] i = parserStack[-1][3][parserStack[-1][4]]
if i in "0123456789" and (size is None or parserStack[-1][3][parserStack[-1][4]-1] not in "lL"): if i in "0123456789" and (
size is None or parserStack[-1][3][parserStack[-1][4] - 1]
not in "lL"):
try: try:
size = size * 10 + int(i) size = size * 10 + int(i)
except TypeError: except TypeError:
@ -218,34 +224,40 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
isArray = False isArray = False
elif i in "Ll" and size is None: elif i in "Ll" and size is None:
size = self.decode_payload_varint() size = self.decode_payload_varint()
if i == "L": isArray = i == "L"
isArray = True
else:
isArray = False
elif size is not None: elif size is not None:
if isArray: if isArray:
parserStack.append([size, size, isArray, parserStack[-1][3][parserStack[-1][4]:], 0, []]) parserStack.append([
size, size, isArray,
parserStack[-1][3][parserStack[-1][4]:], 0, []
])
parserStack[-2][4] = len(parserStack[-2][3]) parserStack[-2][4] = len(parserStack[-2][3])
else: else:
for j in range(parserStack[-1][4], len(parserStack[-1][3])): for j in range(parserStack[-1][4], len(parserStack[-1][3])):
if parserStack[-1][3][j] not in "lL0123456789": if parserStack[-1][3][j] not in "lL0123456789":
break break
parserStack.append([size, size, isArray, parserStack[-1][3][parserStack[-1][4]:j+1], 0, []]) parserStack.append([
size, size, isArray,
parserStack[-1][3][parserStack[-1][4]:j + 1], 0, []
])
parserStack[-2][4] += len(parserStack[-1][3]) - 1 parserStack[-2][4] += len(parserStack[-1][3]) - 1
size = None size = None
continue continue
elif i == "s": elif i == "s":
#if parserStack[-2][2]: # if parserStack[-2][2]:
# parserStack[-1][5].append(self.payload[self.payloadOffset:self.payloadOffset + parserStack[-1][0]]) # parserStack[-1][5].append(self.payload[
#else: # self.payloadOffset:self.payloadOffset + parserStack[-1][0]])
parserStack[-1][5] = self.payload[self.payloadOffset:self.payloadOffset + parserStack[-1][0]] # else:
parserStack[-1][5] = self.payload[
self.payloadOffset:self.payloadOffset + parserStack[-1][0]]
self.payloadOffset += parserStack[-1][0] self.payloadOffset += parserStack[-1][0]
parserStack[-1][1] = 0 parserStack[-1][1] = 0
parserStack[-1][2] = True parserStack[-1][2] = True
#del parserStack[-1] # del parserStack[-1]
size = None size = None
elif i in "viHIQ": elif i in "viHIQ":
parserStack[-1][5].append(decode_simple(self, parserStack[-1][3][parserStack[-1][4]])) parserStack[-1][5].append(decode_simple(
self, parserStack[-1][3][parserStack[-1][4]]))
size = None size = None
else: else:
size = None size = None
@ -256,25 +268,33 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
parserStack[depth][4] = 0 parserStack[depth][4] = 0
if depth > 0: if depth > 0:
if parserStack[depth][2]: if parserStack[depth][2]:
parserStack[depth - 1][5].append(parserStack[depth][5]) parserStack[depth - 1][5].append(
parserStack[depth][5])
else: else:
parserStack[depth - 1][5].extend(parserStack[depth][5]) parserStack[depth - 1][5].extend(
parserStack[depth][5])
parserStack[depth][5] = [] parserStack[depth][5] = []
if parserStack[depth][1] <= 0: if parserStack[depth][1] <= 0:
if depth == 0: if depth == 0:
# we're done, at depth 0 counter is at 0 and pattern is done parsing # we're done, at depth 0 counter is at 0
# and pattern is done parsing
return parserStack[depth][5] return parserStack[depth][5]
del parserStack[-1] del parserStack[-1]
continue continue
break break
break break
if self.payloadOffset > self.payloadLength: if self.payloadOffset > self.payloadLength:
logger.debug("Insufficient data %i/%i", self.payloadOffset, self.payloadLength) logger.debug(
'Insufficient data %i/%i',
self.payloadOffset, self.payloadLength)
raise BMProtoInsufficientDataError() raise BMProtoInsufficientDataError()
def bm_command_error(self): def bm_command_error(self):
fatalStatus, banTime, inventoryVector, errorText = self.decode_payload_content("vvlsls") fatalStatus, banTime, inventoryVector, errorText = \
logger.error("%s:%i error: %i, %s", self.destination.host, self.destination.port, fatalStatus, errorText) self.decode_payload_content("vvlsls")
logger.error(
'%s:%i error: %i, %s', self.destination.host,
self.destination.port, fatalStatus, errorText)
return True return True
def bm_command_getdata(self): def bm_command_getdata(self):
@ -291,10 +311,9 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
items = self.decode_payload_content("l32s") items = self.decode_payload_content("l32s")
if len(items) > BMProto.maxObjectCount: if len(items) > BMProto.maxObjectCount:
logger.error("Too many items in %sinv message!", "d" if dandelion else "") logger.error(
'Too many items in %sinv message!', 'd' if dandelion else '')
raise BMProtoExcessiveDataError() raise BMProtoExcessiveDataError()
else:
pass
# ignore dinv if dandelion turned off # ignore dinv if dandelion turned off
if dandelion and not state.dandelion: if dandelion and not state.dandelion:
@ -320,31 +339,41 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
def bm_command_object(self): def bm_command_object(self):
objectOffset = self.payloadOffset objectOffset = self.payloadOffset
nonce, expiresTime, objectType, version, streamNumber = self.decode_payload_content("QQIvv") nonce, expiresTime, objectType, version, streamNumber = \
self.object = BMObject(nonce, expiresTime, objectType, version, streamNumber, self.payload, self.payloadOffset) self.decode_payload_content("QQIvv")
self.object = BMObject(
nonce, expiresTime, objectType, version, streamNumber,
self.payload, self.payloadOffset)
if len(self.payload) - self.payloadOffset > BMProto.maxObjectPayloadSize: if len(self.payload) - self.payloadOffset > BMProto.maxObjectPayloadSize:
logger.info('The payload length of this object is too large (%d bytes). Ignoring it.' % (len(self.payload) - self.payloadOffset)) logger.info(
'The payload length of this object is too large (%d bytes).'
' Ignoring it.', len(self.payload) - self.payloadOffset)
raise BMProtoExcessiveDataError() raise BMProtoExcessiveDataError()
try: try:
self.object.checkProofOfWorkSufficient() self.object.checkProofOfWorkSufficient()
self.object.checkEOLSanity() self.object.checkEOLSanity()
self.object.checkAlreadyHave() self.object.checkAlreadyHave()
except (BMObjectExpiredError, BMObjectAlreadyHaveError, BMObjectInsufficientPOWError) as e: except (BMObjectExpiredError, BMObjectAlreadyHaveError,
BMObjectInsufficientPOWError):
BMProto.stopDownloadingObject(self.object.inventoryHash) BMProto.stopDownloadingObject(self.object.inventoryHash)
raise e raise
try: try:
self.object.checkStream() self.object.checkStream()
except (BMObjectUnwantedStreamError,) as e: except BMObjectUnwantedStreamError:
BMProto.stopDownloadingObject(self.object.inventoryHash, BMConfigParser().get("inventory", "acceptmismatch")) acceptmismatch = BMConfigParser().get(
if not BMConfigParser().get("inventory", "acceptmismatch"): "inventory", "acceptmismatch")
raise e BMProto.stopDownloadingObject(
self.object.inventoryHash, acceptmismatch)
if not acceptmismatch:
raise
try: try:
self.object.checkObjectByType() self.object.checkObjectByType()
objectProcessorQueue.put((self.object.objectType, buffer(self.object.data))) objectProcessorQueue.put((
except BMObjectInvalidError as e: self.object.objectType, buffer(self.object.data)))
except BMObjectInvalidError:
BMProto.stopDownloadingObject(self.object.inventoryHash, True) BMProto.stopDownloadingObject(self.object.inventoryHash, True)
else: else:
try: try:
@ -356,9 +385,15 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
Dandelion().removeHash(self.object.inventoryHash, "cycle detection") Dandelion().removeHash(self.object.inventoryHash, "cycle detection")
Inventory()[self.object.inventoryHash] = ( Inventory()[self.object.inventoryHash] = (
self.object.objectType, self.object.streamNumber, buffer(self.payload[objectOffset:]), self.object.expiresTime, buffer(self.object.tag)) self.object.objectType, self.object.streamNumber,
self.handleReceivedObject(self.object.streamNumber, self.object.inventoryHash) buffer(self.payload[objectOffset:]), self.object.expiresTime,
invQueue.put((self.object.streamNumber, self.object.inventoryHash, self.destination)) buffer(self.object.tag)
)
self.handleReceivedObject(
self.object.streamNumber, self.object.inventoryHash)
invQueue.put((
self.object.streamNumber, self.object.inventoryHash,
self.destination))
return True return True
def _decode_addr(self): def _decode_addr(self):
@ -409,104 +444,125 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
def bm_command_verack(self): def bm_command_verack(self):
self.verackReceived = True self.verackReceived = True
if self.verackSent: if not self.verackSent:
if self.isSSL: return True
self.set_state("tls_init", length=self.payloadLength, expectBytes=0) self.set_state(
return False "tls_init" if self.isSSL else "connection_fully_established",
self.set_state("connection_fully_established", length=self.payloadLength, expectBytes=0) length=self.payloadLength, expectBytes=0)
return False return False
return True
def bm_command_version(self): def bm_command_version(self):
self.remoteProtocolVersion, self.services, self.timestamp, self.sockNode, self.peerNode, self.nonce, \ (self.remoteProtocolVersion, self.services, self.timestamp,
self.userAgent, self.streams = self.decode_payload_content("IQQiiQlsLv") self.sockNode, self.peerNode, self.nonce, self.userAgent,
self.streams) = self.decode_payload_content("IQQiiQlsLv")
self.nonce = struct.pack('>Q', self.nonce) self.nonce = struct.pack('>Q', self.nonce)
self.timeOffset = self.timestamp - int(time.time()) self.timeOffset = self.timestamp - int(time.time())
logger.debug("remoteProtocolVersion: %i", self.remoteProtocolVersion) logger.debug('remoteProtocolVersion: %i', self.remoteProtocolVersion)
logger.debug("services: 0x%08X", self.services) logger.debug('services: 0x%08X', self.services)
logger.debug("time offset: %i", self.timestamp - int(time.time())) logger.debug('time offset: %i', self.timestamp - int(time.time()))
logger.debug("my external IP: %s", self.sockNode.host) logger.debug('my external IP: %s', self.sockNode.host)
logger.debug("remote node incoming address: %s:%i", self.destination.host, self.peerNode.port) logger.debug(
logger.debug("user agent: %s", self.userAgent) 'remote node incoming address: %s:%i',
logger.debug("streams: [%s]", ",".join(map(str,self.streams))) self.destination.host, self.peerNode.port)
logger.debug('user agent: %s', self.userAgent)
logger.debug('streams: [%s]', ','.join(map(str, self.streams)))
if not self.peerValidityChecks(): if not self.peerValidityChecks():
# TODO ABORT # TODO ABORT
return True return True
self.append_write_buf(protocol.CreatePacket('verack')) self.append_write_buf(protocol.CreatePacket('verack'))
self.verackSent = True self.verackSent = True
if not self.isOutbound: if not self.isOutbound:
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \ self.append_write_buf(protocol.assembleVersionMessage(
network.connectionpool.BMConnectionPool().streams, True, nodeid=self.nodeid)) self.destination.host, self.destination.port,
#print "%s:%i: Sending version" % (self.destination.host, self.destination.port) connectionpool.BMConnectionPool().streams, True,
nodeid=self.nodeid))
logger.debug(
'%(host)s:%(port)i sending version',
self.destination._asdict())
if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and if ((self.services & protocol.NODE_SSL == protocol.NODE_SSL) and
protocol.haveSSL(not self.isOutbound)): protocol.haveSSL(not self.isOutbound)):
self.isSSL = True self.isSSL = True
if self.verackReceived: if not self.verackReceived:
if self.isSSL: return True
self.set_state("tls_init", length=self.payloadLength, expectBytes=0) self.set_state(
return False "tls_init" if self.isSSL else "connection_fully_established",
self.set_state("connection_fully_established", length=self.payloadLength, expectBytes=0) length=self.payloadLength, expectBytes=0)
return False return False
return True
def peerValidityChecks(self): def peerValidityChecks(self):
if self.remoteProtocolVersion < 3: if self.remoteProtocolVersion < 3:
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(
errorText="Your is using an old protocol. Closing connection.")) errorText="Your is using an old protocol. Closing connection.",
logger.debug ('Closing connection to old protocol version %s, node: %s', fatal=2))
str(self.remoteProtocolVersion), str(self.destination)) logger.debug(
'Closing connection to old protocol version %s, node: %s',
self.remoteProtocolVersion, self.destination)
return False return False
if self.timeOffset > BMProto.maxTimeOffset: if self.timeOffset > BMProto.maxTimeOffset:
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(
errorText="Your time is too far in the future compared to mine. Closing connection.")) errorText="Your time is too far in the future compared to mine."
logger.info("%s's time is too far in the future (%s seconds). Closing connection to it.", " Closing connection.", fatal=2))
self.destination, self.timeOffset) logger.info(
"%s's time is too far in the future (%s seconds)."
" Closing connection to it.", self.destination, self.timeOffset)
BMProto.timeOffsetWrongCount += 1 BMProto.timeOffsetWrongCount += 1
return False return False
elif self.timeOffset < -BMProto.maxTimeOffset: elif self.timeOffset < -BMProto.maxTimeOffset:
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(
errorText="Your time is too far in the past compared to mine. Closing connection.")) errorText="Your time is too far in the past compared to mine."
logger.info("%s's time is too far in the past (timeOffset %s seconds). Closing connection to it.", " Closing connection.", fatal=2))
self.destination, self.timeOffset) logger.info(
"%s's time is too far in the past (timeOffset %s seconds)."
" Closing connection to it.", self.destination, self.timeOffset)
BMProto.timeOffsetWrongCount += 1 BMProto.timeOffsetWrongCount += 1
return False return False
else: else:
BMProto.timeOffsetWrongCount = 0 BMProto.timeOffsetWrongCount = 0
if not self.streams: if not self.streams:
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(
errorText="We don't have shared stream interests. Closing connection.")) errorText="We don't have shared stream interests."
logger.debug ('Closed connection to %s because there is no overlapping interest in streams.', " Closing connection.", fatal=2))
str(self.destination)) logger.debug(
'Closed connection to %s because there is no overlapping interest'
' in streams.', self.destination)
return False return False
if self.destination in network.connectionpool.BMConnectionPool().inboundConnections: if self.destination in connectionpool.BMConnectionPool().inboundConnections:
try: try:
if not protocol.checkSocksIP(self.destination.host): if not protocol.checkSocksIP(self.destination.host):
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.append_write_buf(protocol.assembleErrorMessage(
errorText="Too many connections from your IP. Closing connection.")) errorText="Too many connections from your IP."
logger.debug ('Closed connection to %s because we are already connected to that IP.', " Closing connection.", fatal=2))
str(self.destination)) logger.debug(
'Closed connection to %s because we are already connected'
' to that IP.', self.destination)
return False return False
except: except:
pass pass
if not self.isOutbound: if not self.isOutbound:
# incoming from a peer we're connected to as outbound, or server full # incoming from a peer we're connected to as outbound,
# report the same error to counter deanonymisation # or server full report the same error to counter deanonymisation
if state.Peer(self.destination.host, self.peerNode.port) in \ if (
network.connectionpool.BMConnectionPool().inboundConnections or \ state.Peer(self.destination.host, self.peerNode.port) in
len(network.connectionpool.BMConnectionPool().inboundConnections) + \ connectionpool.BMConnectionPool().inboundConnections or
len(network.connectionpool.BMConnectionPool().outboundConnections) > \ len(connectionpool.BMConnectionPool().inboundConnections) +
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ len(connectionpool.BMConnectionPool().outboundConnections) >
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections"): BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") +
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections")
errorText="Server full, please try again later.")) ):
logger.debug ("Closed connection to %s due to server full or duplicate inbound/outbound.", self.append_write_buf(protocol.assembleErrorMessage(
str(self.destination)) errorText="Server full, please try again later.", fatal=2))
logger.debug(
'Closed connection to %s due to server full'
' or duplicate inbound/outbound.', self.destination)
return False return False
if network.connectionpool.BMConnectionPool().isAlreadyConnected(self.nonce): if connectionpool.BMConnectionPool().isAlreadyConnected(
self.append_write_buf(protocol.assembleErrorMessage(fatal=2, self.nonce):
errorText="I'm connected to myself. Closing connection.")) self.append_write_buf(protocol.assembleErrorMessage(
logger.debug ("Closed connection to %s because I'm connected to myself.", errorText="I'm connected to myself. Closing connection.",
str(self.destination)) fatal=2))
logger.debug(
"Closed connection to %s because I'm connected to myself.",
self.destination)
return False return False
return True return True
@ -519,7 +575,8 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
return b'' return b''
retval = b'' retval = b''
for i in range(0, len(peerList), BMProto.maxAddrCount): for i in range(0, len(peerList), BMProto.maxAddrCount):
payload = addresses.encodeVarint(len(peerList[i:i + BMProto.maxAddrCount])) payload = addresses.encodeVarint(
len(peerList[i:i + BMProto.maxAddrCount]))
for address in peerList[i:i + BMProto.maxAddrCount]: for address in peerList[i:i + BMProto.maxAddrCount]:
stream, peer, timestamp = address stream, peer, timestamp = address
payload += struct.pack( payload += struct.pack(
@ -534,8 +591,10 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
@staticmethod @staticmethod
def stopDownloadingObject(hashId, forwardAnyway=False): def stopDownloadingObject(hashId, forwardAnyway=False):
for connection in network.connectionpool.BMConnectionPool().inboundConnections.values() + \ for connection in (
network.connectionpool.BMConnectionPool().outboundConnections.values(): connectionpool.BMConnectionPool().inboundConnections.values() +
connectionpool.BMConnectionPool().outboundConnections.values()
):
try: try:
del connection.objectsNewToMe[hashId] del connection.objectsNewToMe[hashId]
except KeyError: except KeyError:
@ -557,12 +616,15 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
# already disconnected # already disconnected
return return
try: try:
logger.debug("%s:%i: closing, %s", self.destination.host, self.destination.port, self.close_reason) logger.debug(
'%s:%i: closing, %s', self.destination.host,
self.destination.port, self.close_reason)
except AttributeError: except AttributeError:
try: try:
logger.debug("%s:%i: closing", self.destination.host, self.destination.port) logger.debug(
'%(host)s:%(port)i: closing', self.destination._asdict())
except AttributeError: except AttributeError:
logger.debug("Disconnected socket closing") logger.debug('Disconnected socket closing')
AdvancedDispatcher.handle_close(self) AdvancedDispatcher.handle_close(self)

View File

@ -1,62 +1,70 @@
from ConfigParser import NoOptionError, NoSectionError
import errno import errno
import re
import socket import socket
import time import time
import random from ConfigParser import NoOptionError, NoSectionError
import re
from bmconfigparser import BMConfigParser import asyncore_pollchoose as asyncore
from debug import logger
import helper_bootstrap import helper_bootstrap
import knownnodes
from network.proxy import Proxy
from network.tcp import TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection
from network.udp import UDPSocket
from network.connectionchooser import chooseConnection
import network.asyncore_pollchoose as asyncore
import protocol
from singleton import Singleton
import state
import helper_random import helper_random
import knownnodes
import protocol
import state
from bmconfigparser import BMConfigParser
from connectionchooser import chooseConnection
from debug import logger
from proxy import Proxy
from singleton import Singleton
from tcp import (
TCPServer, Socks5BMConnection, Socks4aBMConnection, TCPConnection)
from udp import UDPSocket
@Singleton @Singleton
class BMConnectionPool(object): class BMConnectionPool(object):
def __init__(self): def __init__(self):
asyncore.set_rates( asyncore.set_rates(
BMConfigParser().safeGetInt("bitmessagesettings", "maxdownloadrate"), BMConfigParser().safeGetInt(
BMConfigParser().safeGetInt("bitmessagesettings", "maxuploadrate")) "bitmessagesettings", "maxdownloadrate"),
BMConfigParser().safeGetInt(
"bitmessagesettings", "maxuploadrate")
)
self.outboundConnections = {} self.outboundConnections = {}
self.inboundConnections = {} self.inboundConnections = {}
self.listeningSockets = {} self.listeningSockets = {}
self.udpSockets = {} self.udpSockets = {}
self.streams = [] self.streams = []
self.lastSpawned = 0 self.lastSpawned = 0
self.spawnWait = 2 self.spawnWait = 2
self.bootstrapped = False self.bootstrapped = False
def connectToStream(self, streamNumber): def connectToStream(self, streamNumber):
self.streams.append(streamNumber) self.streams.append(streamNumber)
def getConnectionByAddr(self, addr): def getConnectionByAddr(self, addr):
if addr in self.inboundConnections: try:
return self.inboundConnections[addr] return self.inboundConnections[addr]
try: except KeyError:
if addr.host in self.inboundConnections:
return self.inboundConnections[addr.host]
except AttributeError:
pass pass
if addr in self.outboundConnections:
return self.outboundConnections[addr]
try: try:
if addr.host in self.udpSockets: return self.inboundConnections[addr.host]
return self.udpSockets[addr.host] except (KeyError, AttributeError):
except AttributeError: pass
try:
return self.outboundConnections[addr]
except KeyError:
pass
try:
return self.udpSockets[addr.host]
except (KeyError, AttributeError):
pass pass
raise KeyError raise KeyError
def isAlreadyConnected(self, nodeid): def isAlreadyConnected(self, nodeid):
for i in self.inboundConnections.values() + self.outboundConnections.values(): for i in (
self.inboundConnections.values() +
self.outboundConnections.values()
):
try: try:
if nodeid == i.nodeid: if nodeid == i.nodeid:
return True return True
@ -73,13 +81,15 @@ class BMConnectionPool(object):
if connection.destination.host in self.inboundConnections: if connection.destination.host in self.inboundConnections:
self.inboundConnections[connection.destination] = connection self.inboundConnections[connection.destination] = connection
else: else:
self.inboundConnections[connection.destination.host] = connection self.inboundConnections[connection.destination.host] = \
connection
def removeConnection(self, connection): def removeConnection(self, connection):
if isinstance(connection, UDPSocket): if isinstance(connection, UDPSocket):
del self.udpSockets[connection.listening.host] del self.udpSockets[connection.listening.host]
elif isinstance(connection, TCPServer): elif isinstance(connection, TCPServer):
del self.listeningSockets[state.Peer(connection.destination.host, connection.destination.port)] del self.listeningSockets[state.Peer(
connection.destination.host, connection.destination.port)]
elif connection.isOutbound: elif connection.isOutbound:
try: try:
del self.outboundConnections[connection.destination] del self.outboundConnections[connection.destination]
@ -96,14 +106,18 @@ class BMConnectionPool(object):
connection.handle_close() connection.handle_close()
def getListeningIP(self): def getListeningIP(self):
if BMConfigParser().safeGet("bitmessagesettings", "onionhostname").endswith(".onion"): if BMConfigParser().safeGet(
host = BMConfigParser().safeGet("bitmessagesettings", "onionbindip") "bitmessagesettings", "onionhostname").endswith(".onion"):
host = BMConfigParser().safeGet(
"bitmessagesettings", "onionbindip")
else: else:
host = '127.0.0.1' host = '127.0.0.1'
if BMConfigParser().safeGetBoolean("bitmessagesettings", "sockslisten") or \ if (BMConfigParser().safeGetBoolean(
BMConfigParser().get("bitmessagesettings", "socksproxytype") == "none": "bitmessagesettings", "sockslisten") or
BMConfigParser().get(
"bitmessagesettings", "socksproxytype") == "none"):
# python doesn't like bind + INADDR_ANY? # python doesn't like bind + INADDR_ANY?
#host = socket.INADDR_ANY # host = socket.INADDR_ANY
host = BMConfigParser().get("network", "bind") host = BMConfigParser().get("network", "bind")
return host return host
@ -130,13 +144,18 @@ class BMConnectionPool(object):
# defaults to empty loop if outbound connections are maxed # defaults to empty loop if outbound connections are maxed
spawnConnections = False spawnConnections = False
acceptConnections = True acceptConnections = True
if BMConfigParser().safeGetBoolean('bitmessagesettings', 'dontconnect'): if BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'dontconnect'):
acceptConnections = False acceptConnections = False
elif BMConfigParser().safeGetBoolean('bitmessagesettings', 'sendoutgoingconnections'): elif BMConfigParser().safeGetBoolean(
'bitmessagesettings', 'sendoutgoingconnections'):
spawnConnections = True spawnConnections = True
if BMConfigParser().get('bitmessagesettings', 'socksproxytype')[0:5] == 'SOCKS' and \ if (BMConfigParser().get(
(not BMConfigParser().getboolean('bitmessagesettings', 'sockslisten') and \ 'bitmessagesettings', 'socksproxytype')[:5] == 'SOCKS' and
".onion" not in BMConfigParser().get('bitmessagesettings', 'onionhostname')): not BMConfigParser().getboolean(
'bitmessagesettings', 'sockslisten') and
".onion" not in BMConfigParser().get(
'bitmessagesettings', 'onionhostname')):
acceptConnections = False acceptConnections = False
if spawnConnections: if spawnConnections:
@ -144,23 +163,38 @@ class BMConnectionPool(object):
helper_bootstrap.dns() helper_bootstrap.dns()
if not self.bootstrapped: if not self.bootstrapped:
self.bootstrapped = True self.bootstrapped = True
Proxy.proxy = (BMConfigParser().safeGet("bitmessagesettings", "sockshostname"), Proxy.proxy = (
BMConfigParser().safeGetInt("bitmessagesettings", "socksport")) BMConfigParser().safeGet(
"bitmessagesettings", "sockshostname"),
BMConfigParser().safeGetInt(
"bitmessagesettings", "socksport")
)
# TODO AUTH # TODO AUTH
# TODO reset based on GUI settings changes # TODO reset based on GUI settings changes
try: try:
if not BMConfigParser().get("network", "onionsocksproxytype").startswith("SOCKS"): if not BMConfigParser().get(
"network", "onionsocksproxytype"
).startswith("SOCKS"):
raise NoOptionError raise NoOptionError
Proxy.onionproxy = (BMConfigParser().get("network", "onionsockshostname"), Proxy.onionproxy = (
BMConfigParser().getint("network", "onionsocksport")) BMConfigParser().get(
"network", "onionsockshostname"),
BMConfigParser().getint(
"network", "onionsocksport")
)
except (NoOptionError, NoSectionError): except (NoOptionError, NoSectionError):
Proxy.onionproxy = None Proxy.onionproxy = None
established = sum(1 for c in self.outboundConnections.values() if (c.connected and c.fullyEstablished)) established = sum(
1 for c in self.outboundConnections.values()
if (c.connected and c.fullyEstablished))
pending = len(self.outboundConnections) - established pending = len(self.outboundConnections) - established
if established < BMConfigParser().safeGetInt("bitmessagesettings", "maxoutboundconnections"): if established < BMConfigParser().safeGetInt(
for i in range(state.maximumNumberOfHalfOpenConnections - pending): "bitmessagesettings", "maxoutboundconnections"):
for i in range(
state.maximumNumberOfHalfOpenConnections - pending):
try: try:
chosen = chooseConnection(helper_random.randomchoice(self.streams)) chosen = chooseConnection(
helper_random.randomchoice(self.streams))
except ValueError: except ValueError:
continue continue
if chosen in self.outboundConnections: if chosen in self.outboundConnections:
@ -170,22 +204,25 @@ class BMConnectionPool(object):
# don't connect to self # don't connect to self
if chosen in state.ownAddresses: if chosen in state.ownAddresses:
continue continue
#for c in self.outboundConnections:
# if chosen == c.destination:
# continue
#for c in self.inboundConnections:
# if chosen.host == c.destination.host:
# continue
try: try:
if chosen.host.endswith(".onion") and Proxy.onionproxy is not None: if (chosen.host.endswith(".onion") and
if BMConfigParser().get("network", "onionsocksproxytype") == "SOCKS5": Proxy.onionproxy is not None):
if BMConfigParser().get(
"network", "onionsocksproxytype"
) == "SOCKS5":
self.addConnection(Socks5BMConnection(chosen)) self.addConnection(Socks5BMConnection(chosen))
elif BMConfigParser().get("network", "onionsocksproxytype") == "SOCKS4a": elif BMConfigParser().get(
"network", "onionsocksproxytype"
) == "SOCKS4a":
self.addConnection(Socks4aBMConnection(chosen)) self.addConnection(Socks4aBMConnection(chosen))
elif BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS5": elif BMConfigParser().safeGet(
"bitmessagesettings", "socksproxytype"
) == "SOCKS5":
self.addConnection(Socks5BMConnection(chosen)) self.addConnection(Socks5BMConnection(chosen))
elif BMConfigParser().safeGet("bitmessagesettings", "socksproxytype") == "SOCKS4a": elif BMConfigParser().safeGet(
"bitmessagesettings", "socksproxytype"
) == "SOCKS4a":
self.addConnection(Socks4aBMConnection(chosen)) self.addConnection(Socks4aBMConnection(chosen))
else: else:
self.addConnection(TCPConnection(chosen)) self.addConnection(TCPConnection(chosen))
@ -199,8 +236,8 @@ class BMConnectionPool(object):
self.lastSpawned = time.time() self.lastSpawned = time.time()
else: else:
for i in ( for i in (
self.inboundConnections.values() + self.inboundConnections.values() +
self.outboundConnections.values() self.outboundConnections.values()
): ):
# FIXME: rating will be increased after next connection # FIXME: rating will be increased after next connection
i.handle_close() i.handle_close()
@ -210,14 +247,20 @@ class BMConnectionPool(object):
if BMConfigParser().safeGet("network", "bind") == '': if BMConfigParser().safeGet("network", "bind") == '':
self.startListening() self.startListening()
else: else:
for bind in re.sub("[^\w.]+", " ", BMConfigParser().safeGet("network", "bind")).split(): for bind in re.sub(
"[^\w.]+", " ",
BMConfigParser().safeGet("network", "bind")
).split():
self.startListening(bind) self.startListening(bind)
logger.info('Listening for incoming connections.') logger.info('Listening for incoming connections.')
if not self.udpSockets: if not self.udpSockets:
if BMConfigParser().safeGet("network", "bind") == '': if BMConfigParser().safeGet("network", "bind") == '':
self.startUDPSocket() self.startUDPSocket()
else: else:
for bind in re.sub("[^\w.]+", " ", BMConfigParser().safeGet("network", "bind")).split(): for bind in re.sub(
"[^\w.]+", " ",
BMConfigParser().safeGet("network", "bind")
).split():
self.startUDPSocket(bind) self.startUDPSocket(bind)
self.startUDPSocket(False) self.startUDPSocket(False)
logger.info('Starting UDP socket(s).') logger.info('Starting UDP socket(s).')
@ -239,7 +282,10 @@ class BMConnectionPool(object):
asyncore.loop(timeout=loopTime, count=1000) asyncore.loop(timeout=loopTime, count=1000)
reaper = [] reaper = []
for i in self.inboundConnections.values() + self.outboundConnections.values(): for i in (
self.inboundConnections.values() +
self.outboundConnections.values()
):
minTx = time.time() - 20 minTx = time.time() - 20
if i.fullyEstablished: if i.fullyEstablished:
minTx -= 300 - 20 minTx -= 300 - 20
@ -247,9 +293,15 @@ class BMConnectionPool(object):
if i.fullyEstablished: if i.fullyEstablished:
i.append_write_buf(protocol.CreatePacket('ping')) i.append_write_buf(protocol.CreatePacket('ping'))
else: else:
i.close_reason = "Timeout (%is)" % (time.time() - i.lastTx) i.close_reason = "Timeout (%is)" % (
time.time() - i.lastTx)
i.set_state("close") i.set_state("close")
for i in self.inboundConnections.values() + self.outboundConnections.values() + self.listeningSockets.values() + self.udpSockets.values(): for i in (
self.inboundConnections.values() +
self.outboundConnections.values() +
self.listeningSockets.values() +
self.udpSockets.values()
):
if not (i.accepting or i.connecting or i.connected): if not (i.accepting or i.connecting or i.connected):
reaper.append(i) reaper.append(i)
else: else:

View File

@ -3,12 +3,11 @@ from random import choice, sample, expovariate
from threading import RLock from threading import RLock
from time import time from time import time
from bmconfigparser import BMConfigParser import connectionpool
import network.connectionpool import state
from debug import logging from debug import logging
from queues import invQueue from queues import invQueue
from singleton import Singleton from singleton import Singleton
import state
# randomise routes after 600 seconds # randomise routes after 600 seconds
REASSIGN_INTERVAL = 600 REASSIGN_INTERVAL = 600
@ -21,6 +20,7 @@ MAX_STEMS = 2
Stem = namedtuple('Stem', ['child', 'stream', 'timeout']) Stem = namedtuple('Stem', ['child', 'stream', 'timeout'])
@Singleton @Singleton
class Dandelion(): class Dandelion():
def __init__(self): def __init__(self):
@ -39,27 +39,29 @@ class Dandelion():
start = time() start = time()
if average == 0: if average == 0:
average = FLUFF_TRIGGER_MEAN_DELAY average = FLUFF_TRIGGER_MEAN_DELAY
return start + expovariate(1.0/average) + FLUFF_TRIGGER_FIXED_DELAY return start + expovariate(1.0 / average) + FLUFF_TRIGGER_FIXED_DELAY
def addHash(self, hashId, source=None, stream=1): def addHash(self, hashId, source=None, stream=1):
if not state.dandelion: if not state.dandelion:
return return
with self.lock: with self.lock:
self.hashMap[hashId] = Stem( self.hashMap[hashId] = Stem(
self.getNodeStem(source), self.getNodeStem(source),
stream, stream,
self.poissonTimeout()) self.poissonTimeout())
def setHashStream(self, hashId, stream=1): def setHashStream(self, hashId, stream=1):
with self.lock: with self.lock:
if hashId in self.hashMap: if hashId in self.hashMap:
self.hashMap[hashId] = Stem( self.hashMap[hashId] = Stem(
self.hashMap[hashId].child, self.hashMap[hashId].child,
stream, stream,
self.poissonTimeout()) self.poissonTimeout())
def removeHash(self, hashId, reason="no reason specified"): def removeHash(self, hashId, reason="no reason specified"):
logging.debug("%s entering fluff mode due to %s.", ''.join('%02x'%ord(i) for i in hashId), reason) logging.debug(
"%s entering fluff mode due to %s.",
''.join('%02x' % ord(i) for i in hashId), reason)
with self.lock: with self.lock:
try: try:
del self.hashMap[hashId] del self.hashMap[hashId]
@ -79,21 +81,30 @@ class Dandelion():
self.stem.append(connection) self.stem.append(connection)
for k in (k for k, v in self.nodeMap.iteritems() if v is None): for k in (k for k, v in self.nodeMap.iteritems() if v is None):
self.nodeMap[k] = connection self.nodeMap[k] = connection
for k, v in {k: v for k, v in self.hashMap.iteritems() if v.child is None}.iteritems(): for k, v in {
self.hashMap[k] = Stem(connection, v.stream, self.poissonTimeout()) k: v for k, v in self.hashMap.iteritems()
if v.child is None
}.iteritems():
self.hashMap[k] = Stem(
connection, v.stream, self.poissonTimeout())
invQueue.put((v.stream, k, v.child)) invQueue.put((v.stream, k, v.child))
def maybeRemoveStem(self, connection): def maybeRemoveStem(self, connection):
# is the stem active? # is the stem active?
with self.lock: with self.lock:
if connection in self.stem: if connection in self.stem:
self.stem.remove(connection) self.stem.remove(connection)
# active mappings to pointing to the removed node # active mappings to pointing to the removed node
for k in (k for k, v in self.nodeMap.iteritems() if v == connection): for k in (
k for k, v in self.nodeMap.iteritems() if v == connection
):
self.nodeMap[k] = None self.nodeMap[k] = None
for k, v in {k: v for k, v in self.hashMap.iteritems() if v.child == connection}.iteritems(): for k, v in {
self.hashMap[k] = Stem(None, v.stream, self.poissonTimeout()) k: v for k, v in self.hashMap.iteritems()
if v.child == connection
}.iteritems():
self.hashMap[k] = Stem(
None, v.stream, self.poissonTimeout())
def pickStem(self, parent=None): def pickStem(self, parent=None):
try: try:
@ -136,10 +147,13 @@ class Dandelion():
with self.lock: with self.lock:
try: try:
# random two connections # random two connections
self.stem = sample(network.connectionpool.BMConnectionPool().outboundConnections.values(), MAX_STEMS) self.stem = sample(
connectionpool.BMConnectionPool(
).outboundConnections.values(), MAX_STEMS)
# not enough stems available # not enough stems available
except ValueError: except ValueError:
self.stem = network.connectionpool.BMConnectionPool().outboundConnections.values() self.stem = connectionpool.BMConnectionPool(
).outboundConnections.values()
self.nodeMap = {} self.nodeMap = {}
# hashMap stays to cater for pending stems # hashMap stays to cater for pending stems
self.refresh = time() + REASSIGN_INTERVAL self.refresh = time() + REASSIGN_INTERVAL

View File

@ -1,27 +1,28 @@
import socket import socket
import time import time
from advanceddispatcher import AdvancedDispatcher
import asyncore_pollchoose as asyncore import asyncore_pollchoose as asyncore
import state
from advanceddispatcher import AdvancedDispatcher
from bmconfigparser import BMConfigParser from bmconfigparser import BMConfigParser
from debug import logger from debug import logger
import network.connectionpool
import state
class ProxyError(Exception): class ProxyError(Exception):
errorCodes = ("UnknownError") errorCodes = ("Unknown error",)
def __init__(self, code=-1): def __init__(self, code=-1):
self.code = code self.code = code
try: try:
self.message = self.__class__.errorCodes[self.code] self.message = self.errorCodes[code]
except IndexError: except IndexError:
self.message = self.__class__.errorCodes[-1] self.message = self.errorCodes[-1]
super(ProxyError, self).__init__(self.message) super(ProxyError, self).__init__(self.message)
class GeneralProxyError(ProxyError): class GeneralProxyError(ProxyError):
errorCodes = ("Success", errorCodes = (
"Success",
"Invalid data", "Invalid data",
"Not connected", "Not connected",
"Not available", "Not available",
@ -30,12 +31,13 @@ class GeneralProxyError(ProxyError):
"Timed out", "Timed out",
"Network unreachable", "Network unreachable",
"Connection refused", "Connection refused",
"Host unreachable") "Host unreachable"
)
class Proxy(AdvancedDispatcher): class Proxy(AdvancedDispatcher):
# these are global, and if you change config during runtime, all active/new # these are global, and if you change config during runtime,
# instances should change too # all active/new instances should change too
_proxy = ("127.0.0.1", 9050) _proxy = ("127.0.0.1", 9050)
_auth = None _auth = None
_onion_proxy = None _onion_proxy = None
@ -48,8 +50,9 @@ class Proxy(AdvancedDispatcher):
@proxy.setter @proxy.setter
def proxy(self, address): def proxy(self, address):
if not isinstance(address, tuple) or (len(address) < 2) or \ if (not isinstance(address, tuple) or len(address) < 2 or
(not isinstance(address[0], str) or not isinstance(address[1], int)): not isinstance(address[0], str) or
not isinstance(address[1], int)):
raise ValueError raise ValueError
self.__class__._proxy = address self.__class__._proxy = address
@ -67,8 +70,10 @@ class Proxy(AdvancedDispatcher):
@onion_proxy.setter @onion_proxy.setter
def onion_proxy(self, address): def onion_proxy(self, address):
if address is not None and (not isinstance(address, tuple) or (len(address) < 2) or \ if address is not None and (
(not isinstance(address[0], str) or not isinstance(address[1], int))): not isinstance(address, tuple) or len(address) < 2 or
not isinstance(address[0], str) or
not isinstance(address[1], int)):
raise ValueError raise ValueError
self.__class__._onion_proxy = address self.__class__._onion_proxy = address
@ -88,15 +93,21 @@ class Proxy(AdvancedDispatcher):
self.isOutbound = True self.isOutbound = True
self.fullyEstablished = False self.fullyEstablished = False
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
if BMConfigParser().safeGetBoolean("bitmessagesettings", "socksauthentication"): if BMConfigParser().safeGetBoolean(
self.auth = (BMConfigParser().safeGet("bitmessagesettings", "socksusername"), "bitmessagesettings", "socksauthentication"):
BMConfigParser().safeGet("bitmessagesettings", "sockspassword")) self.auth = (
BMConfigParser().safeGet(
"bitmessagesettings", "socksusername"),
BMConfigParser().safeGet(
"bitmessagesettings", "sockspassword")
)
else: else:
self.auth = None self.auth = None
if address.host.endswith(".onion") and self.onion_proxy is not None: self.connect(
self.connect(self.onion_proxy) self.onion_proxy
else: if address.host.endswith(".onion") and self.onion_proxy else
self.connect(self.proxy) self.proxy
)
def handle_connect(self): def handle_connect(self):
self.set_state("init") self.set_state("init")
@ -104,7 +115,9 @@ class Proxy(AdvancedDispatcher):
AdvancedDispatcher.handle_connect(self) AdvancedDispatcher.handle_connect(self)
except socket.error as e: except socket.error as e:
if e.errno in asyncore._DISCONNECTED: if e.errno in asyncore._DISCONNECTED:
logger.debug("%s:%i: Connection failed: %s", self.destination.host, self.destination.port, str(e)) logger.debug(
"%s:%i: Connection failed: %s",
self.destination.host, self.destination.port, e)
return return
self.state_init() self.state_init()

View File

@ -3,12 +3,17 @@ import struct
from proxy import Proxy, ProxyError, GeneralProxyError from proxy import Proxy, ProxyError, GeneralProxyError
class Socks4aError(ProxyError): class Socks4aError(ProxyError):
errorCodes = ("Request granted", errorCodes = (
"Request granted",
"Request rejected or failed", "Request rejected or failed",
"Request rejected because SOCKS server cannot connect to identd on the client", "Request rejected because SOCKS server cannot connect to identd"
"Request rejected because the client program and identd report different user-ids", " on the client",
"Unknown error") "Request rejected because the client program and identd report"
" different user-ids",
"Unknown error"
)
class Socks4a(Proxy): class Socks4a(Proxy):
@ -40,14 +45,15 @@ class Socks4a(Proxy):
self.boundaddr = self.read_buf[4:] self.boundaddr = self.read_buf[4:]
self.__proxysockname = (self.boundaddr, self.boundport) self.__proxysockname = (self.boundaddr, self.boundport)
if self.ipaddr: if self.ipaddr:
self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) self.__proxypeername = (
socket.inet_ntoa(self.ipaddr), self.destination[1])
else: else:
self.__proxypeername = (self.destination[0], self.destport) self.__proxypeername = (self.destination[0], self.destport)
self.set_state("proxy_handshake_done", length=8) self.set_state("proxy_handshake_done", length=8)
return True return True
def proxy_sock_name(self): def proxy_sock_name(self):
return socket.inet_ntoa(self.__proxysockname[0]) return socket.inet_ntoa(self.__proxysockname[0])
class Socks4aConnection(Socks4a): class Socks4aConnection(Socks4a):
@ -57,7 +63,8 @@ class Socks4aConnection(Socks4a):
def state_auth_done(self): def state_auth_done(self):
# Now we can request the actual connection # Now we can request the actual connection
rmtrslv = False rmtrslv = False
self.append_write_buf(struct.pack('>BBH', 0x04, 0x01, self.destination[1])) self.append_write_buf(
struct.pack('>BBH', 0x04, 0x01, self.destination[1]))
# If the given destination address is an IP address, we'll # If the given destination address is an IP address, we'll
# use the IPv4 address request even if remote resolving was specified. # use the IPv4 address request even if remote resolving was specified.
try: try:
@ -69,10 +76,12 @@ class Socks4aConnection(Socks4a):
# Resolve remotely # Resolve remotely
rmtrslv = True rmtrslv = True
self.ipaddr = None self.ipaddr = None
self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) self.append_write_buf(
struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01))
else: else:
# Resolve locally # Resolve locally
self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) self.ipaddr = socket.inet_aton(
socket.gethostbyname(self.destination[0]))
self.append_write_buf(self.ipaddr) self.append_write_buf(self.ipaddr)
if self._auth: if self._auth:
self.append_write_buf(self._auth[0]) self.append_write_buf(self._auth[0])
@ -98,7 +107,8 @@ class Socks4aResolver(Socks4a):
def state_auth_done(self): def state_auth_done(self):
# Now we can request the actual connection # Now we can request the actual connection
self.append_write_buf(struct.pack('>BBH', 0x04, 0xF0, self.destination[1])) self.append_write_buf(
struct.pack('>BBH', 0x04, 0xF0, self.destination[1]))
self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01)) self.append_write_buf(struct.pack("BBBB", 0x00, 0x00, 0x00, 0x01))
if self._auth: if self._auth:
self.append_write_buf(self._auth[0]) self.append_write_buf(self._auth[0])

View File

@ -12,26 +12,30 @@ from proxy import GeneralProxyError, Proxy, ProxyError
class Socks5AuthError(ProxyError): class Socks5AuthError(ProxyError):
"""Thrown when the socks5 protocol encounters an authentication error""" """Rised when the socks5 protocol encounters an authentication error"""
errorCodes = ("Succeeded", errorCodes = (
"Authentication is required", "Succeeded",
"All offered authentication methods were rejected", "Authentication is required",
"Unknown username or invalid password", "All offered authentication methods were rejected",
"Unknown error") "Unknown username or invalid password",
"Unknown error"
)
class Socks5Error(ProxyError): class Socks5Error(ProxyError):
"""Thrown when socks5 protocol encounters an error""" """Rised when socks5 protocol encounters an error"""
errorCodes = ("Succeeded", errorCodes = (
"General SOCKS server failure", "Succeeded",
"Connection not allowed by ruleset", "General SOCKS server failure",
"Network unreachable", "Connection not allowed by ruleset",
"Host unreachable", "Network unreachable",
"Connection refused", "Host unreachable",
"TTL expired", "Connection refused",
"Command not supported", "TTL expired",
"Address type not supported", "Command not supported",
"Unknown error") "Address type not supported",
"Unknown error"
)
class Socks5(Proxy): class Socks5(Proxy):
@ -42,7 +46,7 @@ class Socks5(Proxy):
self.destport = address[1] self.destport = address[1]
def state_init(self): def state_init(self):
"""Protocol initialisation (before connection is established)""" """Protocol initialization (before connection is established)"""
if self._auth: if self._auth:
self.append_write_buf(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02)) self.append_write_buf(struct.pack('BBBB', 0x05, 0x02, 0x00, 0x02))
else: else:
@ -61,9 +65,11 @@ class Socks5(Proxy):
self.set_state("auth_done", length=2) self.set_state("auth_done", length=2)
elif ret[1] == 2: elif ret[1] == 2:
# username/password # username/password
self.append_write_buf(struct.pack('BB', 1, len(self._auth[0])) + self.append_write_buf(
self._auth[0] + struct.pack('B', len(self._auth[1])) + struct.pack('BB', 1, len(self._auth[0])) +
self._auth[1]) self._auth[0] + struct.pack('B', len(self._auth[1])) +
self._auth[1]
)
self.set_state("auth_needed", length=2, expectBytes=2) self.set_state("auth_needed", length=2, expectBytes=2)
else: else:
if ret[1] == 0xff: if ret[1] == 0xff:
@ -118,17 +124,19 @@ class Socks5(Proxy):
def state_proxy_addr_2_1(self): def state_proxy_addr_2_1(self):
""" """
Handle other addresses than IPv4 returned for peer (e.g. IPv6, onion, ...). This is part 1 which retrieves the Handle other addresses than IPv4 returned for peer
(e.g. IPv6, onion, ...). This is part 1 which retrieves the
length of the data. length of the data.
""" """
self.address_length = ord(self.read_buf[0:1]) self.address_length = ord(self.read_buf[0:1])
self.set_state("proxy_addr_2_2", length=1, expectBytes=self.address_length) self.set_state(
"proxy_addr_2_2", length=1, expectBytes=self.address_length)
return True return True
def state_proxy_addr_2_2(self): def state_proxy_addr_2_2(self):
""" """
Handle other addresses than IPv4 returned for peer (e.g. IPv6, onion, ...). This is part 2 which retrieves the Handle other addresses than IPv4 returned for peer
data. (e.g. IPv6, onion, ...). This is part 2 which retrieves the data.
""" """
self.boundaddr = self.read_buf[0:self.address_length] self.boundaddr = self.read_buf[0:self.address_length]
self.set_state("proxy_port", length=self.address_length, expectBytes=2) self.set_state("proxy_port", length=self.address_length, expectBytes=2)
@ -139,7 +147,8 @@ class Socks5(Proxy):
self.boundport = struct.unpack(">H", self.read_buf[0:2])[0] self.boundport = struct.unpack(">H", self.read_buf[0:2])[0]
self.__proxysockname = (self.boundaddr, self.boundport) self.__proxysockname = (self.boundaddr, self.boundport)
if self.ipaddr is not None: if self.ipaddr is not None:
self.__proxypeername = (socket.inet_ntoa(self.ipaddr), self.destination[1]) self.__proxypeername = (
socket.inet_ntoa(self.ipaddr), self.destination[1])
else: else:
self.__proxypeername = (self.destination[0], self.destport) self.__proxypeername = (self.destination[0], self.destport)
self.set_state("proxy_handshake_done", length=2) self.set_state("proxy_handshake_done", length=2)
@ -169,12 +178,15 @@ class Socks5Connection(Socks5):
if Proxy._remote_dns: # pylint: disable=protected-access if Proxy._remote_dns: # pylint: disable=protected-access
# Resolve remotely # Resolve remotely
self.ipaddr = None self.ipaddr = None
self.append_write_buf(chr(0x03).encode() + self.append_write_buf(
chr(len(self.destination[0])).encode() + chr(0x03).encode() +
self.destination[0]) chr(len(self.destination[0])).encode() +
self.destination[0]
)
else: else:
# Resolve locally # Resolve locally
self.ipaddr = socket.inet_aton(socket.gethostbyname(self.destination[0])) self.ipaddr = socket.inet_aton(
socket.gethostbyname(self.destination[0]))
self.append_write_buf(chr(0x01).encode() + self.ipaddr) self.append_write_buf(chr(0x01).encode() + self.ipaddr)
self.append_write_buf(struct.pack(">H", self.destination[1])) self.append_write_buf(struct.pack(">H", self.destination[1]))
self.set_state("pre_connect", length=0, expectBytes=4) self.set_state("pre_connect", length=0, expectBytes=4)
@ -200,14 +212,18 @@ class Socks5Resolver(Socks5):
"""Perform resolving""" """Perform resolving"""
# Now we can request the actual connection # Now we can request the actual connection
self.append_write_buf(struct.pack('BBB', 0x05, 0xF0, 0x00)) self.append_write_buf(struct.pack('BBB', 0x05, 0xF0, 0x00))
self.append_write_buf(chr(0x03).encode() + chr(len(self.host)).encode() + str(self.host)) self.append_write_buf(
chr(0x03).encode() + chr(len(self.host)).encode() +
str(self.host)
)
self.append_write_buf(struct.pack(">H", self.port)) self.append_write_buf(struct.pack(">H", self.port))
self.set_state("pre_connect", length=0, expectBytes=4) self.set_state("pre_connect", length=0, expectBytes=4)
return True return True
def resolved(self): def resolved(self):
""" """
Resolving is done, process the return value. To use this within PyBitmessage, a callback needs to be Resolving is done, process the return value.
To use this within PyBitmessage, a callback needs to be
implemented which hasn't been done yet. implemented which hasn't been done yet.
""" """
print "Resolved %s as %s" % (self.host, self.proxy_sock_name()) print "Resolved %s as %s" % (self.host, self.proxy_sock_name())

View File

@ -10,10 +10,10 @@ import socket
import time import time
import addresses import addresses
import asyncore_pollchoose as asyncore
import connectionpool
import helper_random import helper_random
import knownnodes import knownnodes
import network.asyncore_pollchoose as asyncore
import network.connectionpool
import protocol import protocol
import shared import shared
import state import state
@ -31,7 +31,8 @@ from network.tls import TLSDispatcher
from queues import UISignalQueue, invQueue, receiveDataQueue from queues import UISignalQueue, invQueue, receiveDataQueue
class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instance-attributes class TCPConnection(BMProto, TLSDispatcher):
# pylint: disable=too-many-instance-attributes
""" """
.. todo:: Look to understand and/or fix the non-parent-init-called .. todo:: Look to understand and/or fix the non-parent-init-called
@ -46,27 +47,32 @@ class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instanc
self.connectedAt = 0 self.connectedAt = 0
self.skipUntil = 0 self.skipUntil = 0
if address is None and sock is not None: if address is None and sock is not None:
self.destination = state.Peer(sock.getpeername()[0], sock.getpeername()[1]) self.destination = state.Peer(*sock.getpeername())
self.isOutbound = False self.isOutbound = False
TLSDispatcher.__init__(self, sock, server_side=True) TLSDispatcher.__init__(self, sock, server_side=True)
self.connectedAt = time.time() self.connectedAt = time.time()
logger.debug("Received connection from %s:%i", self.destination.host, self.destination.port) logger.debug(
'Received connection from %s:%i',
self.destination.host, self.destination.port)
self.nodeid = randomBytes(8) self.nodeid = randomBytes(8)
elif address is not None and sock is not None: elif address is not None and sock is not None:
TLSDispatcher.__init__(self, sock, server_side=False) TLSDispatcher.__init__(self, sock, server_side=False)
self.isOutbound = True self.isOutbound = True
logger.debug("Outbound proxy connection to %s:%i", self.destination.host, self.destination.port) logger.debug(
'Outbound proxy connection to %s:%i',
self.destination.host, self.destination.port)
else: else:
self.destination = address self.destination = address
self.isOutbound = True self.isOutbound = True
if ":" in address.host: self.create_socket(
self.create_socket(socket.AF_INET6, socket.SOCK_STREAM) socket.AF_INET6 if ":" in address.host else socket.AF_INET,
else: socket.SOCK_STREAM)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
TLSDispatcher.__init__(self, sock, server_side=False) TLSDispatcher.__init__(self, sock, server_side=False)
self.connect(self.destination) self.connect(self.destination)
logger.debug("Connecting to %s:%i", self.destination.host, self.destination.port) logger.debug(
'Connecting to %s:%i',
self.destination.host, self.destination.port)
encodedAddr = protocol.encodeHost(self.destination.host) encodedAddr = protocol.encodeHost(self.destination.host)
self.local = all([ self.local = all([
protocol.checkIPAddress(encodedAddr, True), protocol.checkIPAddress(encodedAddr, True),
@ -80,13 +86,18 @@ class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instanc
""" """
This is a defense against the so called intersection attacks. This is a defense against the so called intersection attacks.
It is called when you notice peer is requesting non-existing objects, or right after the connection is It is called when you notice peer is requesting non-existing
established. It will estimate how long an object will take to propagate across the network, and skip processing objects, or right after the connection is established. It will
"getdata" requests until then. This means an attacker only has one shot per IP to perform the attack. estimate how long an object will take to propagate across the
network, and skip processing "getdata" requests until then. This
means an attacker only has one shot per IP to perform the attack.
""" """
# estimated time for a small object to propagate across the whole network # estimated time for a small object to propagate across the
max_known_nodes = max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) # whole network
delay = math.ceil(math.log(max_known_nodes + 2, 20)) * (0.2 + invQueue.queueCount / 2.0) max_known_nodes = max(
len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes)
delay = math.ceil(math.log(max_known_nodes + 2, 20)) * (
0.2 + invQueue.queueCount / 2.0)
# take the stream with maximum amount of nodes # take the stream with maximum amount of nodes
# +2 is to avoid problems with log(0) and log(1) # +2 is to avoid problems with log(0) and log(1)
# 20 is avg connected nodes count # 20 is avg connected nodes count
@ -95,15 +106,20 @@ class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instanc
if initial: if initial:
self.skipUntil = self.connectedAt + delay self.skipUntil = self.connectedAt + delay
if self.skipUntil > time.time(): if self.skipUntil > time.time():
logger.debug("Initial skipping processing getdata for %.2fs", self.skipUntil - time.time()) logger.debug(
'Initial skipping processing getdata for %.2fs',
self.skipUntil - time.time())
else: else:
logger.debug("Skipping processing getdata due to missing object for %.2fs", delay) logger.debug(
'Skipping processing getdata due to missing object'
' for %.2fs', delay)
self.skipUntil = time.time() + delay self.skipUntil = time.time() + delay
def state_connection_fully_established(self): def state_connection_fully_established(self):
""" """
State after the bitmessage protocol handshake is completed (version/verack exchange, and if both side support State after the bitmessage protocol handshake is completed
TLS, the TLS handshake as well). (version/verack exchange, and if both side support TLS,
the TLS handshake as well).
""" """
self.set_connection_fully_established() self.set_connection_fully_established()
self.set_state("bm_header") self.set_state("bm_header")
@ -115,12 +131,14 @@ class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instanc
if not self.isOutbound and not self.local: if not self.isOutbound and not self.local:
shared.clientHasReceivedIncomingConnections = True shared.clientHasReceivedIncomingConnections = True
UISignalQueue.put(('setStatusIcon', 'green')) UISignalQueue.put(('setStatusIcon', 'green'))
UISignalQueue.put(('updateNetworkStatusTab', (self.isOutbound, True, self.destination))) UISignalQueue.put((
'updateNetworkStatusTab',
(self.isOutbound, True, self.destination)
))
self.antiIntersectionDelay(True) self.antiIntersectionDelay(True)
self.fullyEstablished = True self.fullyEstablished = True
if self.isOutbound: if self.isOutbound:
knownnodes.increaseRating(self.destination) knownnodes.increaseRating(self.destination)
if self.isOutbound:
Dandelion().maybeAddStem(self) Dandelion().maybeAddStem(self)
self.sendAddr() self.sendAddr()
self.sendBigInv() self.sendBigInv()
@ -161,18 +179,25 @@ class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instanc
self.append_write_buf(BMProto.assembleAddr(templist)) self.append_write_buf(BMProto.assembleAddr(templist))
def sendBigInv(self): def sendBigInv(self):
"""Send hashes of all inventory objects, chunked as the protocol has a per-command limit.""" """
Send hashes of all inventory objects, chunked as the protocol has
a per-command limit.
"""
def sendChunk(): def sendChunk():
"""Send one chunk of inv entries in one command""" """Send one chunk of inv entries in one command"""
if objectCount == 0: if objectCount == 0:
return return
logger.debug('Sending huge inv message with %i objects to just this one peer', objectCount) logger.debug(
self.append_write_buf(protocol.CreatePacket('inv', addresses.encodeVarint(objectCount) + payload)) 'Sending huge inv message with %i objects to just this'
' one peer', objectCount)
self.append_write_buf(protocol.CreatePacket(
'inv', addresses.encodeVarint(objectCount) + payload))
# Select all hashes for objects in this stream. # Select all hashes for objects in this stream.
bigInvList = {} bigInvList = {}
for stream in self.streams: for stream in self.streams:
# may lock for a long time, but I think it's better than thousands of small locks # may lock for a long time, but I think it's better than
# thousands of small locks
with self.objectsNewToThemLock: with self.objectsNewToThemLock:
for objHash in Inventory().unexpired_hashes_by_stream(stream): for objHash in Inventory().unexpired_hashes_by_stream(stream):
# don't advertise stem objects on bigInv # don't advertise stem objects on bigInv
@ -203,17 +228,18 @@ class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instanc
try: try:
AdvancedDispatcher.handle_connect(self) AdvancedDispatcher.handle_connect(self)
except socket.error as e: except socket.error as e:
if e.errno in asyncore._DISCONNECTED: # pylint: disable=protected-access # pylint: disable=protected-access
logger.debug("%s:%i: Connection failed: %s", self.destination.host, self.destination.port, str(e)) if e.errno in asyncore._DISCONNECTED:
logger.debug(
'%s:%i: Connection failed: %s',
self.destination.host, self.destination.port, e)
return return
self.nodeid = randomBytes(8) self.nodeid = randomBytes(8)
self.append_write_buf( self.append_write_buf(
protocol.assembleVersionMessage( protocol.assembleVersionMessage(
self.destination.host, self.destination.host, self.destination.port,
self.destination.port, connectionpool.BMConnectionPool().streams,
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
False,
nodeid=self.nodeid))
self.connectedAt = time.time() self.connectedAt = time.time()
receiveDataQueue.put(self.destination) receiveDataQueue.put(self.destination)
@ -224,7 +250,8 @@ class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instanc
for s in self.streams: for s in self.streams:
try: try:
with knownnodes.knownNodesLock: with knownnodes.knownNodesLock:
knownnodes.knownNodes[s][self.destination]["lastseen"] = time.time() knownnodes.knownNodes[s][self.destination][
"lastseen"] = time.time()
except KeyError: except KeyError:
pass pass
receiveDataQueue.put(self.destination) receiveDataQueue.put(self.destination)
@ -238,7 +265,10 @@ class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instanc
if self.isOutbound and not self.fullyEstablished: if self.isOutbound and not self.fullyEstablished:
knownnodes.decreaseRating(self.destination) knownnodes.decreaseRating(self.destination)
if self.fullyEstablished: if self.fullyEstablished:
UISignalQueue.put(('updateNetworkStatusTab', (self.isOutbound, False, self.destination))) UISignalQueue.put((
'updateNetworkStatusTab',
(self.isOutbound, False, self.destination)
))
if self.isOutbound: if self.isOutbound:
Dandelion().maybeRemoveStem(self) Dandelion().maybeRemoveStem(self)
BMProto.handle_close(self) BMProto.handle_close(self)
@ -253,16 +283,17 @@ class Socks5BMConnection(Socks5Connection, TCPConnection):
self.set_state("init") self.set_state("init")
def state_proxy_handshake_done(self): def state_proxy_handshake_done(self):
"""State when SOCKS5 connection succeeds, we need to send a Bitmessage handshake to peer.""" """
State when SOCKS5 connection succeeds, we need to send a
Bitmessage handshake to peer.
"""
Socks5Connection.state_proxy_handshake_done(self) Socks5Connection.state_proxy_handshake_done(self)
self.nodeid = randomBytes(8) self.nodeid = randomBytes(8)
self.append_write_buf( self.append_write_buf(
protocol.assembleVersionMessage( protocol.assembleVersionMessage(
self.destination.host, self.destination.host, self.destination.port,
self.destination.port, connectionpool.BMConnectionPool().streams,
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
False,
nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size) self.set_state("bm_header", expectBytes=protocol.Header.size)
return True return True
@ -276,16 +307,17 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection):
self.set_state("init") self.set_state("init")
def state_proxy_handshake_done(self): def state_proxy_handshake_done(self):
"""State when SOCKS4a connection succeeds, we need to send a Bitmessage handshake to peer.""" """
State when SOCKS4a connection succeeds, we need to send a
Bitmessage handshake to peer.
"""
Socks4aConnection.state_proxy_handshake_done(self) Socks4aConnection.state_proxy_handshake_done(self)
self.nodeid = randomBytes(8) self.nodeid = randomBytes(8)
self.append_write_buf( self.append_write_buf(
protocol.assembleVersionMessage( protocol.assembleVersionMessage(
self.destination.host, self.destination.host, self.destination.port,
self.destination.port, connectionpool.BMConnectionPool().streams,
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
False,
nodeid=self.nodeid))
self.set_state("bm_header", expectBytes=protocol.Header.size) self.set_state("bm_header", expectBytes=protocol.Header.size)
return True return True
@ -293,7 +325,7 @@ class Socks4aBMConnection(Socks4aConnection, TCPConnection):
class TCPServer(AdvancedDispatcher): class TCPServer(AdvancedDispatcher):
"""TCP connection server for Bitmessage protocol""" """TCP connection server for Bitmessage protocol"""
def __init__(self, host='127.0.0.1', port=8444): # pylint: disable=redefined-outer-name def __init__(self, host='127.0.0.1', port=8444):
if not hasattr(self, '_map'): if not hasattr(self, '_map'):
AdvancedDispatcher.__init__(self) AdvancedDispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
@ -308,7 +340,8 @@ class TCPServer(AdvancedDispatcher):
continue continue
else: else:
if attempt > 0: if attempt > 0:
BMConfigParser().set("bitmessagesettings", "port", str(port)) BMConfigParser().set(
'bitmessagesettings', 'port', str(port))
BMConfigParser().save() BMConfigParser().save()
break break
self.destination = state.Peer(host, port) self.destination = state.Peer(host, port)
@ -324,23 +357,30 @@ class TCPServer(AdvancedDispatcher):
def handle_accept(self): def handle_accept(self):
"""Incoming connection callback""" """Incoming connection callback"""
pair = self.accept() try:
if pair is not None: sock = self.accept()[0]
sock, _ = pair except (TypeError, IndexError):
state.ownAddresses[state.Peer(sock.getsockname()[0], sock.getsockname()[1])] = True return
if len(network.connectionpool.BMConnectionPool().inboundConnections) + \
len(network.connectionpool.BMConnectionPool().outboundConnections) > \ state.ownAddresses[state.Peer(*sock.getsockname())] = True
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \ if (
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10: len(connectionpool.BMConnectionPool().inboundConnections) +
# 10 is a sort of buffer, in between it will go through the version handshake len(connectionpool.BMConnectionPool().outboundConnections) >
# and return an error to the peer BMConfigParser().safeGetInt(
logger.warning("Server full, dropping connection") 'bitmessagesettings', 'maxtotalconnections') +
sock.close() BMConfigParser().safeGetInt(
return 'bitmessagesettings', 'maxbootstrapconnections') + 10
try: ):
network.connectionpool.BMConnectionPool().addConnection(TCPConnection(sock=sock)) # 10 is a sort of buffer, in between it will go through
except socket.error: # the version handshake and return an error to the peer
pass logger.warning("Server full, dropping connection")
sock.close()
return
try:
connectionpool.BMConnectionPool().addConnection(
TCPConnection(sock=sock))
except socket.error:
pass
if __name__ == "__main__": if __name__ == "__main__":