Runnable with both Python3 and Python2, with both PyQt5 and PyQt4 by using Qt.py #2250
|
@ -476,7 +476,7 @@ class MessageList_TimeWidget(BMTableWidgetItem):
|
||||||
msgid is available by QtCore.Qt.UserRole
|
msgid is available by QtCore.Qt.UserRole
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, label=None, unread=False, timestamp=None, msgid=''):
|
def __init__(self, label=None, unread=False, timestamp=None, msgid=b''):
|
||||||
super(MessageList_TimeWidget, self).__init__(label, unread)
|
super(MessageList_TimeWidget, self).__init__(label, unread)
|
||||||
self.setData(QtCore.Qt.UserRole, QtCore.QByteArray(msgid))
|
self.setData(QtCore.Qt.UserRole, QtCore.QByteArray(msgid))
|
||||||
self.setData(TimestampRole, int(timestamp))
|
self.setData(TimestampRole, int(timestamp))
|
||||||
|
|
|
@ -28,8 +28,6 @@ class Inventory:
|
||||||
|
|
||||||
# cheap inheritance copied from asyncore
|
# cheap inheritance copied from asyncore
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr == "__contains__":
|
|
||||||
self.numberOfInventoryLookupsPerformed += 1
|
|
||||||
try:
|
try:
|
||||||
realRet = getattr(self._realInventory, attr)
|
realRet = getattr(self._realInventory, attr)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -40,6 +38,10 @@ class Inventory:
|
||||||
else:
|
else:
|
||||||
return realRet
|
return realRet
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
self.numberOfInventoryLookupsPerformed += 1
|
||||||
|
return key in self._realInventory
|
||||||
|
|
||||||
# hint for pylint: this is dictionary like object
|
# hint for pylint: this is dictionary like object
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return self._realInventory[key]
|
return self._realInventory[key]
|
||||||
|
|
|
@ -724,21 +724,6 @@ class dispatcher(object):
|
||||||
if why.args[0] not in (ENOTCONN, EBADF):
|
if why.args[0] not in (ENOTCONN, EBADF):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# cheap inheritance, used to pass all other attribute
|
|
||||||
# references to the underlying socket object.
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
try:
|
|
||||||
retattr = getattr(self.socket, attr)
|
|
||||||
except AttributeError:
|
|
||||||
raise AttributeError(
|
|
||||||
"%s instance has no attribute '%s'"
|
|
||||||
% (self.__class__.__name__, attr))
|
|
||||||
else:
|
|
||||||
msg = "%(me)s.%(attr)s is deprecated; use %(me)s.socket.%(attr)s"\
|
|
||||||
" instead" % {'me': self.__class__.__name__, 'attr': attr}
|
|
||||||
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
|
||||||
return retattr
|
|
||||||
|
|
||||||
# log and log_info may be overridden to provide more sophisticated
|
# log and log_info may be overridden to provide more sophisticated
|
||||||
# logging and warning methods. In general, log is for 'hit' logging
|
# logging and warning methods. In general, log is for 'hit' logging
|
||||||
# and 'log_info' is for informational, warning and error logging.
|
# and 'log_info' is for informational, warning and error logging.
|
||||||
|
|
|
@ -9,6 +9,7 @@ import re
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
|
import six
|
||||||
|
|
||||||
# magic imports!
|
# magic imports!
|
||||||
import addresses
|
import addresses
|
||||||
|
@ -34,6 +35,18 @@ from .objectracker import ObjectTracker, missingObjects
|
||||||
logger = logging.getLogger('default')
|
logger = logging.getLogger('default')
|
||||||
|
|
||||||
|
|
||||||
|
def _hoststr(v):
|
||||||
|
if six.PY3:
|
||||||
|
return v
|
||||||
|
else: # assume six.PY2
|
||||||
|
return str(v)
|
||||||
|
|
||||||
|
def _restr(v):
|
||||||
|
if six.PY3:
|
||||||
|
return v.decode("utf-8", "replace")
|
||||||
|
else: # assume six.PY2
|
||||||
|
return v
|
||||||
|
|
||||||
class BMProtoError(ProxyError):
|
class BMProtoError(ProxyError):
|
||||||
"""A Bitmessage Protocol Base Error"""
|
"""A Bitmessage Protocol Base Error"""
|
||||||
errorCodes = ("Protocol error")
|
errorCodes = ("Protocol error")
|
||||||
|
@ -115,7 +128,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
|
||||||
if not self.invalid:
|
if not self.invalid:
|
||||||
try:
|
try:
|
||||||
retval = getattr(
|
retval = getattr(
|
||||||
self, "bm_command_" + str(self.command).lower())()
|
self, "bm_command_" + self.command.decode("utf-8", "replace").lower())()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# unimplemented command
|
# unimplemented command
|
||||||
logger.debug('unimplemented command %s', self.command)
|
logger.debug('unimplemented command %s', self.command)
|
||||||
|
@ -169,16 +182,16 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
|
||||||
# protocol.checkIPAddress()
|
# protocol.checkIPAddress()
|
||||||
services, host, port = self.decode_payload_content("Q16sH")
|
services, host, port = self.decode_payload_content("Q16sH")
|
||||||
if host[0:12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
|
if host[0:12] == b'\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, _hoststr(host[12:16]))
|
||||||
elif host[0:6] == b'\xfd\x87\xd8\x7e\xeb\x43':
|
elif host[0:6] == b'\xfd\x87\xd8\x7e\xeb\x43':
|
||||||
# Onion, based on BMD/bitcoind
|
# Onion, based on BMD/bitcoind
|
||||||
host = base64.b32encode(host[6:]).lower() + b".onion"
|
host = base64.b32encode(host[6:]).lower() + b".onion"
|
||||||
else:
|
else:
|
||||||
host = socket.inet_ntop(socket.AF_INET6, str(host))
|
host = socket.inet_ntop(socket.AF_INET6, _hoststr(host))
|
||||||
if host == b"":
|
if host == b"":
|
||||||
# This can happen on Windows systems which are not 64-bit
|
# This can happen on Windows systems which are not 64-bit
|
||||||
# compatible 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, _hoststr(host[12:16]))
|
||||||
|
|
||||||
return Node(services, host, port)
|
return Node(services, host, port)
|
||||||
|
|
||||||
|
@ -534,7 +547,7 @@ class BMProto(AdvancedDispatcher, ObjectTracker):
|
||||||
self.append_write_buf(protocol.CreatePacket(b'verack'))
|
self.append_write_buf(protocol.CreatePacket(b'verack'))
|
||||||
self.verackSent = True
|
self.verackSent = True
|
||||||
ua_valid = re.match(
|
ua_valid = re.match(
|
||||||
r'^/[a-zA-Z]+:[0-9]+\.?[\w\s\(\)\./:;-]*/$', self.userAgent)
|
r'^/[a-zA-Z]+:[0-9]+\.?[\w\s\(\)\./:;-]*/$', _restr(self.userAgent))
|
||||||
if not ua_valid:
|
if not ua_valid:
|
||||||
self.userAgent = b'/INVALID:0/'
|
self.userAgent = b'/INVALID:0/'
|
||||||
if not self.isOutbound:
|
if not self.isOutbound:
|
||||||
|
|
|
@ -14,10 +14,16 @@ from queues import queue, portCheckerQueue
|
||||||
logger = logging.getLogger('default')
|
logger = logging.getLogger('default')
|
||||||
|
|
||||||
|
|
||||||
|
def _ends_with(s, tail):
|
||||||
|
try:
|
||||||
|
return s.endswith(tail)
|
||||||
|
except:
|
||||||
|
return s.decode("utf-8", "replace").endswith(tail)
|
||||||
|
|
||||||
def getDiscoveredPeer():
|
def getDiscoveredPeer():
|
||||||
"""Get a peer from the local peer discovery list"""
|
"""Get a peer from the local peer discovery list"""
|
||||||
try:
|
try:
|
||||||
peer = random.choice(state.discoveredPeers.keys()) # nosec B311
|
peer = random.choice(list(state.discoveredPeers.keys())) # nosec B311
|
||||||
except (IndexError, KeyError):
|
except (IndexError, KeyError):
|
||||||
raise ValueError
|
raise ValueError
|
||||||
try:
|
try:
|
||||||
|
@ -45,7 +51,7 @@ def chooseConnection(stream):
|
||||||
return getDiscoveredPeer()
|
return getDiscoveredPeer()
|
||||||
for _ in range(50):
|
for _ in range(50):
|
||||||
peer = random.choice( # nosec B311
|
peer = random.choice( # nosec B311
|
||||||
knownnodes.knownNodes[stream].keys())
|
list(knownnodes.knownNodes[stream].keys()))
|
||||||
try:
|
try:
|
||||||
peer_info = knownnodes.knownNodes[stream][peer]
|
peer_info = knownnodes.knownNodes[stream][peer]
|
||||||
if peer_info.get('self'):
|
if peer_info.get('self'):
|
||||||
|
@ -57,10 +63,10 @@ def chooseConnection(stream):
|
||||||
if haveOnion:
|
if haveOnion:
|
||||||
# do not connect to raw IP addresses
|
# do not connect to raw IP addresses
|
||||||
# --keep all traffic within Tor overlay
|
# --keep all traffic within Tor overlay
|
||||||
if onionOnly and not peer.host.endswith('.onion'):
|
if onionOnly and not _ends_with(peer.host, '.onion'):
|
||||||
continue
|
continue
|
||||||
# onion addresses have a higher priority when SOCKS
|
# onion addresses have a higher priority when SOCKS
|
||||||
if peer.host.endswith('.onion') and rating > 0:
|
if _ends_with(peer.host, '.onion') and rating > 0:
|
||||||
rating = 1
|
rating = 1
|
||||||
# TODO: need better check
|
# TODO: need better check
|
||||||
elif not peer.host.startswith('bootstrap'):
|
elif not peer.host.startswith('bootstrap'):
|
||||||
|
|
|
@ -25,6 +25,12 @@ from .udp import UDPSocket
|
||||||
logger = logging.getLogger('default')
|
logger = logging.getLogger('default')
|
||||||
|
|
||||||
|
|
||||||
|
def _ends_with(s, tail):
|
||||||
|
try:
|
||||||
|
return s.endswith(tail)
|
||||||
|
except:
|
||||||
|
return s.decode("utf-8", "replace").endswith(tail)
|
||||||
|
|
||||||
class BMConnectionPool(object):
|
class BMConnectionPool(object):
|
||||||
"""Pool of all existing connections"""
|
"""Pool of all existing connections"""
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
@ -160,8 +166,8 @@ class BMConnectionPool(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getListeningIP():
|
def getListeningIP():
|
||||||
"""What IP are we supposed to be listening on?"""
|
"""What IP are we supposed to be listening on?"""
|
||||||
if config.safeGet(
|
if _ends_with(config.safeGet(
|
||||||
"bitmessagesettings", "onionhostname", "").endswith(".onion"):
|
"bitmessagesettings", "onionhostname", ""), ".onion"):
|
||||||
host = config.safeGet(
|
host = config.safeGet(
|
||||||
"bitmessagesettings", "onionbindip")
|
"bitmessagesettings", "onionbindip")
|
||||||
else:
|
else:
|
||||||
|
@ -314,7 +320,7 @@ class BMConnectionPool(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if chosen.host.endswith(".onion") and Proxy.onion_proxy:
|
if _ends_with(chosen.host, ".onion") and Proxy.onion_proxy:
|
||||||
if onionsocksproxytype == "SOCKS5":
|
if onionsocksproxytype == "SOCKS5":
|
||||||
self.addConnection(Socks5BMConnection(chosen))
|
self.addConnection(Socks5BMConnection(chosen))
|
||||||
elif onionsocksproxytype == "SOCKS4a":
|
elif onionsocksproxytype == "SOCKS4a":
|
||||||
|
|
|
@ -185,8 +185,8 @@ class Dandelion: # pylint: disable=old-style-class
|
||||||
try:
|
try:
|
||||||
# random two connections
|
# random two connections
|
||||||
self.stem = sample(
|
self.stem = sample(
|
||||||
connectionpool.BMConnectionPool(
|
sorted(connectionpool.BMConnectionPool(
|
||||||
).outboundConnections.values(), MAX_STEMS)
|
).outboundConnections.values()), MAX_STEMS)
|
||||||
# not enough stems available
|
# not enough stems available
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.stem = connectionpool.BMConnectionPool(
|
self.stem = connectionpool.BMConnectionPool(
|
||||||
|
|
|
@ -106,6 +106,12 @@ def addKnownNode(stream, peer, lastseen=None, is_self=False):
|
||||||
Returns True if added a new node.
|
Returns True if added a new node.
|
||||||
"""
|
"""
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
|
if not isinstance(peer.host, str):
|
||||||
|
try:
|
||||||
|
peer = Peer(peer.host.decode("ascii"), peer.port)
|
||||||
|
except UnicodeDecodeError as err:
|
||||||
|
logger.warning("Invalid host: {}".format(peer.host.decode("ascii", "backslashreplace")))
|
||||||
|
return
|
||||||
if isinstance(stream, Iterable):
|
if isinstance(stream, Iterable):
|
||||||
with knownNodesLock:
|
with knownNodesLock:
|
||||||
for s in stream:
|
for s in stream:
|
||||||
|
@ -151,7 +157,7 @@ def createDefaultKnownNodes():
|
||||||
def readKnownNodes():
|
def readKnownNodes():
|
||||||
"""Load knownnodes from filesystem"""
|
"""Load knownnodes from filesystem"""
|
||||||
try:
|
try:
|
||||||
with open(state.appdata + 'knownnodes.dat', 'rb') as source:
|
with open(state.appdata + 'knownnodes.dat', 'r') as source:
|
||||||
with knownNodesLock:
|
with knownNodesLock:
|
||||||
try:
|
try:
|
||||||
json_deserialize_knownnodes(source)
|
json_deserialize_knownnodes(source)
|
||||||
|
|
|
@ -14,6 +14,12 @@ from .node import Peer
|
||||||
logger = logging.getLogger('default')
|
logger = logging.getLogger('default')
|
||||||
|
|
||||||
|
|
||||||
|
def _ends_with(s, tail):
|
||||||
|
try:
|
||||||
|
return s.endswith(tail)
|
||||||
|
except:
|
||||||
|
return s.decode("utf-8", "replace").endswith(tail)
|
||||||
|
|
||||||
class ProxyError(Exception):
|
class ProxyError(Exception):
|
||||||
"""Base proxy exception class"""
|
"""Base proxy exception class"""
|
||||||
errorCodes = ("Unknown error",)
|
errorCodes = ("Unknown error",)
|
||||||
|
@ -125,7 +131,7 @@ class Proxy(AdvancedDispatcher):
|
||||||
self.auth = None
|
self.auth = None
|
||||||
self.connect(
|
self.connect(
|
||||||
self.onion_proxy
|
self.onion_proxy
|
||||||
if address.host.endswith(".onion") and self.onion_proxy else
|
if _ends_with(address.host, ".onion") and self.onion_proxy else
|
||||||
self.proxy
|
self.proxy
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,12 @@ maximumAgeOfNodesThatIAdvertiseToOthers = 10800 #: Equals three hours
|
||||||
maximumTimeOffsetWrongCount = 3 #: Connections with wrong time offset
|
maximumTimeOffsetWrongCount = 3 #: Connections with wrong time offset
|
||||||
|
|
||||||
|
|
||||||
|
def _ends_with(s, tail):
|
||||||
|
try:
|
||||||
|
return s.endswith(tail)
|
||||||
|
except:
|
||||||
|
return s.decode("utf-8", "replace").endswith(tail)
|
||||||
|
|
||||||
class TCPConnection(BMProto, TLSDispatcher):
|
class TCPConnection(BMProto, TLSDispatcher):
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
"""
|
"""
|
||||||
|
@ -195,7 +201,7 @@ class TCPConnection(BMProto, TLSDispatcher):
|
||||||
(k, v) for k, v in six.iteritems(nodes)
|
(k, v) for k, v in six.iteritems(nodes)
|
||||||
if v["lastseen"] > int(time.time())
|
if v["lastseen"] > int(time.time())
|
||||||
- maximumAgeOfNodesThatIAdvertiseToOthers
|
- maximumAgeOfNodesThatIAdvertiseToOthers
|
||||||
and v["rating"] >= 0 and not k.host.endswith('.onion')
|
and v["rating"] >= 0 and not _ends_with(k.host, '.onion')
|
||||||
]
|
]
|
||||||
# sent 250 only if the remote isn't interested in it
|
# sent 250 only if the remote isn't interested in it
|
||||||
elemCount = min(
|
elemCount = min(
|
||||||
|
|
|
@ -81,7 +81,7 @@ class UDPSocket(BMProto): # pylint: disable=too-many-instance-attributes
|
||||||
return True
|
return True
|
||||||
remoteport = False
|
remoteport = False
|
||||||
for seenTime, stream, _, ip, port in addresses:
|
for seenTime, stream, _, ip, port in addresses:
|
||||||
decodedIP = protocol.checkIPAddress(str(ip))
|
decodedIP = protocol.checkIPAddress(ip)
|
||||||
if stream not in network.connectionpool.pool.streams:
|
if stream not in network.connectionpool.pool.streams:
|
||||||
continue
|
continue
|
||||||
if (seenTime < time.time() - protocol.MAX_TIME_OFFSET
|
if (seenTime < time.time() - protocol.MAX_TIME_OFFSET
|
||||||
|
|
|
@ -170,7 +170,7 @@ def checkIPAddress(host, private=False):
|
||||||
otherwise returns False
|
otherwise returns False
|
||||||
"""
|
"""
|
||||||
if host[0:12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
|
if host[0:12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF':
|
||||||
hostStandardFormat = socket.inet_ntop(socket.AF_INET, host[12:])
|
hostStandardFormat = socket.inet_ntop(socket.AF_INET, bytes(host[12:]))
|
||||||
return checkIPv4Address(host[12:], hostStandardFormat, private)
|
return checkIPv4Address(host[12:], hostStandardFormat, private)
|
||||||
elif host[0:6] == b'\xfd\x87\xd8\x7e\xeb\x43':
|
elif host[0:6] == b'\xfd\x87\xd8\x7e\xeb\x43':
|
||||||
# Onion, based on BMD/bitcoind
|
# Onion, based on BMD/bitcoind
|
||||||
|
@ -419,7 +419,7 @@ def assembleVersionMessage(
|
||||||
return CreatePacket(b'version', payload)
|
return CreatePacket(b'version', payload)
|
||||||
|
|
||||||
|
|
||||||
def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
|
def assembleErrorMessage(fatal=0, banTime=0, inventoryVector=b'', errorText=''):
|
||||||
"""
|
"""
|
||||||
Construct the payload of an error message,
|
Construct the payload of an error message,
|
||||||
return the resulting bytes of running `CreatePacket` on it
|
return the resulting bytes of running `CreatePacket` on it
|
||||||
|
@ -428,6 +428,8 @@ def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''):
|
||||||
payload += encodeVarint(banTime)
|
payload += encodeVarint(banTime)
|
||||||
payload += encodeVarint(len(inventoryVector))
|
payload += encodeVarint(len(inventoryVector))
|
||||||
payload += inventoryVector
|
payload += inventoryVector
|
||||||
|
if isinstance(errorText, str):
|
||||||
|
errorText = errorText.encode("utf-8", "replace")
|
||||||
payload += encodeVarint(len(errorText))
|
payload += encodeVarint(len(errorText))
|
||||||
payload += errorText
|
payload += errorText
|
||||||
return CreatePacket(b'error', payload)
|
return CreatePacket(b'error', payload)
|
||||||
|
|
Reference in New Issue
Block a user