Merge pull request #1363 from coffeedogs/final_code_quality_5
Changes based on style and lint checks. (final_code_quality_5)
This commit is contained in:
commit
7a53da73ff
|
@ -1,3 +1,8 @@
|
||||||
|
"""
|
||||||
|
src/defaults.py
|
||||||
|
===============
|
||||||
|
"""
|
||||||
|
|
||||||
# sanity check, prevent doing ridiculous PoW
|
# sanity check, prevent doing ridiculous PoW
|
||||||
# 20 million PoWs equals approximately 2 days on dev's dual R9 290
|
# 20 million PoWs equals approximately 2 days on dev's dual R9 290
|
||||||
ridiculousDifficulty = 20000000
|
ridiculousDifficulty = 20000000
|
||||||
|
@ -7,7 +12,13 @@ ridiculousDifficulty = 20000000
|
||||||
# namecoin integration to "namecoind".
|
# namecoin integration to "namecoind".
|
||||||
namecoinDefaultRpcPort = "8336"
|
namecoinDefaultRpcPort = "8336"
|
||||||
|
|
||||||
#If changed, these values will cause particularly unexpected behavior: You won't be able to either send or receive messages because the proof of work you do (or demand) won't match that done or demanded by others. Don't change them!
|
# If changed, these values will cause particularly unexpected behavior:
|
||||||
networkDefaultProofOfWorkNonceTrialsPerByte = 1000 #The amount of work that should be performed (and demanded) per byte of the payload.
|
# You won't be able to either send or receive messages because the proof
|
||||||
networkDefaultPayloadLengthExtraBytes = 1000 #To make sending short messages a little more difficult, this value is added to the payload length for use in calculating the proof of work target.
|
# of work you do (or demand) won't match that done or demanded by others.
|
||||||
|
# Don't change them!
|
||||||
|
# The amount of work that should be performed (and demanded) per byte of the payload.
|
||||||
|
networkDefaultProofOfWorkNonceTrialsPerByte = 1000
|
||||||
|
# To make sending short messages a little more difficult, this value is
|
||||||
|
# added to the payload length for use in calculating the proof of work
|
||||||
|
# target.
|
||||||
|
networkDefaultPayloadLengthExtraBytes = 1000
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
# -*- Mode: Python -*-
|
# -*- Mode: Python -*-
|
||||||
# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
|
# Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp
|
||||||
# Author: Sam Rushing <rushing@nightmare.com>
|
# Author: Sam Rushing <rushing@nightmare.com>
|
||||||
|
# pylint: disable=too-many-statements,too-many-branches,no-self-use,too-many-lines,attribute-defined-outside-init
|
||||||
|
# pylint: disable=global-statement
|
||||||
|
"""
|
||||||
|
src/network/asyncore_pollchoose.py
|
||||||
|
==================================
|
||||||
|
|
||||||
# ======================================================================
|
# ======================================================================
|
||||||
# Copyright 1996 by Sam Rushing
|
# Copyright 1996 by Sam Rushing
|
||||||
|
@ -25,7 +30,7 @@
|
||||||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
# ======================================================================
|
# ======================================================================
|
||||||
|
|
||||||
"""Basic infrastructure for asynchronous socket service clients and servers.
|
Basic infrastructure for asynchronous socket service clients and servers.
|
||||||
|
|
||||||
There are only two ways to have a program on a single processor do "more
|
There are only two ways to have a program on a single processor do "more
|
||||||
than one thing at a time". Multi-threaded programming is the simplest and
|
than one thing at a time". Multi-threaded programming is the simplest and
|
||||||
|
@ -46,22 +51,20 @@ many of the difficult problems for you, making the task of building
|
||||||
sophisticated high-performance network servers and clients a snap.
|
sophisticated high-performance network servers and clients a snap.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# randomise object order for bandwidth balancing
|
import os
|
||||||
import random
|
|
||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from threading import current_thread
|
|
||||||
import warnings
|
import warnings
|
||||||
|
from errno import (
|
||||||
|
EADDRINUSE, EAGAIN, EALREADY, EBADF, ECONNABORTED, ECONNREFUSED, ECONNRESET, EHOSTUNREACH, EINPROGRESS, EINTR,
|
||||||
|
EINVAL, EISCONN, ENETUNREACH, ENOTCONN, ENOTSOCK, EPIPE, ESHUTDOWN, ETIMEDOUT, EWOULDBLOCK, errorcode
|
||||||
|
)
|
||||||
|
from threading import current_thread
|
||||||
|
|
||||||
import os
|
|
||||||
import helper_random
|
import helper_random
|
||||||
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \
|
|
||||||
ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \
|
|
||||||
ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENOTSOCK, EINTR, ETIMEDOUT, \
|
|
||||||
EADDRINUSE, \
|
|
||||||
errorcode
|
|
||||||
try:
|
try:
|
||||||
from errno import WSAEWOULDBLOCK
|
from errno import WSAEWOULDBLOCK
|
||||||
except (ImportError, AttributeError):
|
except (ImportError, AttributeError):
|
||||||
|
@ -75,13 +78,15 @@ try:
|
||||||
except (ImportError, AttributeError):
|
except (ImportError, AttributeError):
|
||||||
WSAECONNRESET = ECONNRESET
|
WSAECONNRESET = ECONNRESET
|
||||||
try:
|
try:
|
||||||
from errno import WSAEADDRINUSE
|
# Desirable side-effects on Windows; imports winsock error numbers
|
||||||
|
from errno import WSAEADDRINUSE # pylint: disable=unused-import
|
||||||
except (ImportError, AttributeError):
|
except (ImportError, AttributeError):
|
||||||
WSAEADDRINUSE = EADDRINUSE
|
WSAEADDRINUSE = EADDRINUSE
|
||||||
|
|
||||||
_DISCONNECTED = frozenset((ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE,
|
|
||||||
EBADF, ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ETIMEDOUT,
|
_DISCONNECTED = frozenset((
|
||||||
WSAECONNRESET))
|
ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF, ECONNREFUSED,
|
||||||
|
EHOSTUNREACH, ENETUNREACH, ETIMEDOUT, WSAECONNRESET))
|
||||||
|
|
||||||
OP_READ = 1
|
OP_READ = 1
|
||||||
OP_WRITE = 2
|
OP_WRITE = 2
|
||||||
|
@ -91,17 +96,21 @@ try:
|
||||||
except NameError:
|
except NameError:
|
||||||
socket_map = {}
|
socket_map = {}
|
||||||
|
|
||||||
|
|
||||||
def _strerror(err):
|
def _strerror(err):
|
||||||
try:
|
try:
|
||||||
return os.strerror(err)
|
return os.strerror(err)
|
||||||
except (ValueError, OverflowError, NameError):
|
except (ValueError, OverflowError, NameError):
|
||||||
if err in errorcode:
|
if err in errorcode:
|
||||||
return errorcode[err]
|
return errorcode[err]
|
||||||
return "Unknown error %s" %err
|
return "Unknown error %s" % err
|
||||||
|
|
||||||
|
|
||||||
class ExitNow(Exception):
|
class ExitNow(Exception):
|
||||||
|
"""We don't use directly but may be necessary as we replace asyncore due to some library raising or expecting it"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
_reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit)
|
_reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit)
|
||||||
|
|
||||||
maxDownloadRate = 0
|
maxDownloadRate = 0
|
||||||
|
@ -113,28 +122,38 @@ uploadTimestamp = 0
|
||||||
uploadBucket = 0
|
uploadBucket = 0
|
||||||
sentBytes = 0
|
sentBytes = 0
|
||||||
|
|
||||||
|
|
||||||
def read(obj):
|
def read(obj):
|
||||||
|
"""Event to read from the object, i.e. its network socket."""
|
||||||
|
|
||||||
if not can_receive():
|
if not can_receive():
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
obj.handle_read_event()
|
obj.handle_read_event()
|
||||||
except _reraised_exceptions:
|
except _reraised_exceptions:
|
||||||
raise
|
raise
|
||||||
except:
|
except BaseException:
|
||||||
obj.handle_error()
|
obj.handle_error()
|
||||||
|
|
||||||
|
|
||||||
def write(obj):
|
def write(obj):
|
||||||
|
"""Event to write to the object, i.e. its network socket."""
|
||||||
|
|
||||||
if not can_send():
|
if not can_send():
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
obj.handle_write_event()
|
obj.handle_write_event()
|
||||||
except _reraised_exceptions:
|
except _reraised_exceptions:
|
||||||
raise
|
raise
|
||||||
except:
|
except BaseException:
|
||||||
obj.handle_error()
|
obj.handle_error()
|
||||||
|
|
||||||
|
|
||||||
def set_rates(download, upload):
|
def set_rates(download, upload):
|
||||||
|
"""Set throttling rates"""
|
||||||
|
|
||||||
global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp
|
global maxDownloadRate, maxUploadRate, downloadBucket, uploadBucket, downloadTimestamp, uploadTimestamp
|
||||||
|
|
||||||
maxDownloadRate = float(download) * 1024
|
maxDownloadRate = float(download) * 1024
|
||||||
maxUploadRate = float(upload) * 1024
|
maxUploadRate = float(upload) * 1024
|
||||||
downloadBucket = maxDownloadRate
|
downloadBucket = maxDownloadRate
|
||||||
|
@ -142,14 +161,24 @@ def set_rates(download, upload):
|
||||||
downloadTimestamp = time.time()
|
downloadTimestamp = time.time()
|
||||||
uploadTimestamp = time.time()
|
uploadTimestamp = time.time()
|
||||||
|
|
||||||
|
|
||||||
def can_receive():
|
def can_receive():
|
||||||
|
"""Predicate indicating whether the download throttle is in effect"""
|
||||||
|
|
||||||
return maxDownloadRate == 0 or downloadBucket > 0
|
return maxDownloadRate == 0 or downloadBucket > 0
|
||||||
|
|
||||||
|
|
||||||
def can_send():
|
def can_send():
|
||||||
|
"""Predicate indicating whether the upload throttle is in effect"""
|
||||||
|
|
||||||
return maxUploadRate == 0 or uploadBucket > 0
|
return maxUploadRate == 0 or uploadBucket > 0
|
||||||
|
|
||||||
|
|
||||||
def update_received(download=0):
|
def update_received(download=0):
|
||||||
|
"""Update the receiving throttle"""
|
||||||
|
|
||||||
global receivedBytes, downloadBucket, downloadTimestamp
|
global receivedBytes, downloadBucket, downloadTimestamp
|
||||||
|
|
||||||
currentTimestamp = time.time()
|
currentTimestamp = time.time()
|
||||||
receivedBytes += download
|
receivedBytes += download
|
||||||
if maxDownloadRate > 0:
|
if maxDownloadRate > 0:
|
||||||
|
@ -160,8 +189,12 @@ def update_received(download=0):
|
||||||
downloadBucket -= download
|
downloadBucket -= download
|
||||||
downloadTimestamp = currentTimestamp
|
downloadTimestamp = currentTimestamp
|
||||||
|
|
||||||
|
|
||||||
def update_sent(upload=0):
|
def update_sent(upload=0):
|
||||||
|
"""Update the sending throttle"""
|
||||||
|
|
||||||
global sentBytes, uploadBucket, uploadTimestamp
|
global sentBytes, uploadBucket, uploadTimestamp
|
||||||
|
|
||||||
currentTimestamp = time.time()
|
currentTimestamp = time.time()
|
||||||
sentBytes += upload
|
sentBytes += upload
|
||||||
if maxUploadRate > 0:
|
if maxUploadRate > 0:
|
||||||
|
@ -172,15 +205,21 @@ def update_sent(upload=0):
|
||||||
uploadBucket -= upload
|
uploadBucket -= upload
|
||||||
uploadTimestamp = currentTimestamp
|
uploadTimestamp = currentTimestamp
|
||||||
|
|
||||||
|
|
||||||
def _exception(obj):
|
def _exception(obj):
|
||||||
|
"""Handle exceptions as appropriate"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
obj.handle_expt_event()
|
obj.handle_expt_event()
|
||||||
except _reraised_exceptions:
|
except _reraised_exceptions:
|
||||||
raise
|
raise
|
||||||
except:
|
except BaseException:
|
||||||
obj.handle_error()
|
obj.handle_error()
|
||||||
|
|
||||||
|
|
||||||
def readwrite(obj, flags):
|
def readwrite(obj, flags):
|
||||||
|
"""Read and write any pending data to/from the object"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if flags & select.POLLIN and can_receive():
|
if flags & select.POLLIN and can_receive():
|
||||||
obj.handle_read_event()
|
obj.handle_read_event()
|
||||||
|
@ -197,15 +236,20 @@ def readwrite(obj, flags):
|
||||||
obj.handle_close()
|
obj.handle_close()
|
||||||
except _reraised_exceptions:
|
except _reraised_exceptions:
|
||||||
raise
|
raise
|
||||||
except:
|
except BaseException:
|
||||||
obj.handle_error()
|
obj.handle_error()
|
||||||
|
|
||||||
|
|
||||||
def select_poller(timeout=0.0, map=None):
|
def select_poller(timeout=0.0, map=None):
|
||||||
"""A poller which uses select(), available on most platforms."""
|
"""A poller which uses select(), available on most platforms."""
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
if map is None:
|
if map is None:
|
||||||
map = socket_map
|
map = socket_map
|
||||||
if map:
|
if map:
|
||||||
r = []; w = []; e = []
|
r = []
|
||||||
|
w = []
|
||||||
|
e = []
|
||||||
for fd, obj in list(map.items()):
|
for fd, obj in list(map.items()):
|
||||||
is_r = obj.readable()
|
is_r = obj.readable()
|
||||||
is_w = obj.writable()
|
is_w = obj.writable()
|
||||||
|
@ -251,13 +295,16 @@ def select_poller(timeout=0.0, map=None):
|
||||||
else:
|
else:
|
||||||
current_thread().stop.wait(timeout)
|
current_thread().stop.wait(timeout)
|
||||||
|
|
||||||
|
|
||||||
def poll_poller(timeout=0.0, map=None):
|
def poll_poller(timeout=0.0, map=None):
|
||||||
"""A poller which uses poll(), available on most UNIXen."""
|
"""A poller which uses poll(), available on most UNIXen."""
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
if map is None:
|
if map is None:
|
||||||
map = socket_map
|
map = socket_map
|
||||||
if timeout is not None:
|
if timeout is not None:
|
||||||
# timeout is in milliseconds
|
# timeout is in milliseconds
|
||||||
timeout = int(timeout*1000)
|
timeout = int(timeout * 1000)
|
||||||
try:
|
try:
|
||||||
poll_poller.pollster
|
poll_poller.pollster
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -301,12 +348,16 @@ def poll_poller(timeout=0.0, map=None):
|
||||||
else:
|
else:
|
||||||
current_thread().stop.wait(timeout)
|
current_thread().stop.wait(timeout)
|
||||||
|
|
||||||
|
|
||||||
# Aliases for backward compatibility
|
# Aliases for backward compatibility
|
||||||
poll = select_poller
|
poll = select_poller
|
||||||
poll2 = poll3 = poll_poller
|
poll2 = poll3 = poll_poller
|
||||||
|
|
||||||
|
|
||||||
def epoll_poller(timeout=0.0, map=None):
|
def epoll_poller(timeout=0.0, map=None):
|
||||||
"""A poller which uses epoll(), supported on Linux 2.5.44 and newer."""
|
"""A poller which uses epoll(), supported on Linux 2.5.44 and newer."""
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
if map is None:
|
if map is None:
|
||||||
map = socket_map
|
map = socket_map
|
||||||
try:
|
try:
|
||||||
|
@ -346,7 +397,7 @@ def epoll_poller(timeout=0.0, map=None):
|
||||||
if e.errno != EINTR:
|
if e.errno != EINTR:
|
||||||
raise
|
raise
|
||||||
r = []
|
r = []
|
||||||
except select.error, err:
|
except select.error as err:
|
||||||
if err.args[0] != EINTR:
|
if err.args[0] != EINTR:
|
||||||
raise
|
raise
|
||||||
r = []
|
r = []
|
||||||
|
@ -358,8 +409,11 @@ def epoll_poller(timeout=0.0, map=None):
|
||||||
else:
|
else:
|
||||||
current_thread().stop.wait(timeout)
|
current_thread().stop.wait(timeout)
|
||||||
|
|
||||||
|
|
||||||
def kqueue_poller(timeout=0.0, map=None):
|
def kqueue_poller(timeout=0.0, map=None):
|
||||||
"""A poller which uses kqueue(), BSD specific."""
|
"""A poller which uses kqueue(), BSD specific."""
|
||||||
|
# pylint: disable=redefined-builtin,no-member
|
||||||
|
|
||||||
if map is None:
|
if map is None:
|
||||||
map = socket_map
|
map = socket_map
|
||||||
try:
|
try:
|
||||||
|
@ -425,12 +479,14 @@ def kqueue_poller(timeout=0.0, map=None):
|
||||||
current_thread().stop.wait(timeout)
|
current_thread().stop.wait(timeout)
|
||||||
|
|
||||||
|
|
||||||
def loop(timeout=30.0, use_poll=False, map=None, count=None,
|
def loop(timeout=30.0, use_poll=False, map=None, count=None, poller=None):
|
||||||
poller=None):
|
"""Poll in a loop, until count or timeout is reached"""
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
if map is None:
|
if map is None:
|
||||||
map = socket_map
|
map = socket_map
|
||||||
if count is None:
|
if count is None:
|
||||||
count = True
|
count = True
|
||||||
# code which grants backward compatibility with "use_poll"
|
# code which grants backward compatibility with "use_poll"
|
||||||
# argument which should no longer be used in favor of
|
# argument which should no longer be used in favor of
|
||||||
# "poller"
|
# "poller"
|
||||||
|
@ -460,10 +516,13 @@ def loop(timeout=30.0, use_poll=False, map=None, count=None,
|
||||||
break
|
break
|
||||||
# then poll
|
# then poll
|
||||||
poller(subtimeout, map)
|
poller(subtimeout, map)
|
||||||
if type(count) is int:
|
if isinstance(count, int):
|
||||||
count = count - 1
|
count = count - 1
|
||||||
|
|
||||||
|
|
||||||
class dispatcher:
|
class dispatcher:
|
||||||
|
"""Dispatcher for socket objects"""
|
||||||
|
# pylint: disable=too-many-public-methods,too-many-instance-attributes,old-style-class
|
||||||
|
|
||||||
debug = False
|
debug = False
|
||||||
connected = False
|
connected = False
|
||||||
|
@ -478,6 +537,7 @@ class dispatcher:
|
||||||
minTx = 1500
|
minTx = 1500
|
||||||
|
|
||||||
def __init__(self, sock=None, map=None):
|
def __init__(self, sock=None, map=None):
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
if map is None:
|
if map is None:
|
||||||
self._map = socket_map
|
self._map = socket_map
|
||||||
else:
|
else:
|
||||||
|
@ -510,7 +570,7 @@ class dispatcher:
|
||||||
self.socket = None
|
self.socket = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
status = [self.__class__.__module__+"."+self.__class__.__name__]
|
status = [self.__class__.__module__ + "." + self.__class__.__name__]
|
||||||
if self.accepting and self.addr:
|
if self.accepting and self.addr:
|
||||||
status.append('listening')
|
status.append('listening')
|
||||||
elif self.connected:
|
elif self.connected:
|
||||||
|
@ -525,7 +585,9 @@ class dispatcher:
|
||||||
__str__ = __repr__
|
__str__ = __repr__
|
||||||
|
|
||||||
def add_channel(self, map=None):
|
def add_channel(self, map=None):
|
||||||
#self.log_info('adding channel %s' % self)
|
"""Add a channel"""
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
if map is None:
|
if map is None:
|
||||||
map = self._map
|
map = self._map
|
||||||
map[self._fileno] = self
|
map[self._fileno] = self
|
||||||
|
@ -533,11 +595,13 @@ class dispatcher:
|
||||||
self.poller_filter = 0
|
self.poller_filter = 0
|
||||||
|
|
||||||
def del_channel(self, map=None):
|
def del_channel(self, map=None):
|
||||||
|
"""Delete a channel"""
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
fd = self._fileno
|
fd = self._fileno
|
||||||
if map is None:
|
if map is None:
|
||||||
map = self._map
|
map = self._map
|
||||||
if fd in map:
|
if fd in map:
|
||||||
#self.log_info('closing channel %d:%s' % (fd, self))
|
|
||||||
del map[fd]
|
del map[fd]
|
||||||
if self._fileno:
|
if self._fileno:
|
||||||
try:
|
try:
|
||||||
|
@ -564,25 +628,29 @@ class dispatcher:
|
||||||
self.poller_registered = False
|
self.poller_registered = False
|
||||||
|
|
||||||
def create_socket(self, family=socket.AF_INET, socket_type=socket.SOCK_STREAM):
|
def create_socket(self, family=socket.AF_INET, socket_type=socket.SOCK_STREAM):
|
||||||
|
"""Create a socket"""
|
||||||
self.family_and_type = family, socket_type
|
self.family_and_type = family, socket_type
|
||||||
sock = socket.socket(family, socket_type)
|
sock = socket.socket(family, socket_type)
|
||||||
sock.setblocking(0)
|
sock.setblocking(0)
|
||||||
self.set_socket(sock)
|
self.set_socket(sock)
|
||||||
|
|
||||||
def set_socket(self, sock, map=None):
|
def set_socket(self, sock, map=None):
|
||||||
|
"""Set socket"""
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
self.socket = sock
|
self.socket = sock
|
||||||
## self.__dict__['socket'] = sock
|
|
||||||
self._fileno = sock.fileno()
|
self._fileno = sock.fileno()
|
||||||
self.add_channel(map)
|
self.add_channel(map)
|
||||||
|
|
||||||
def set_reuse_addr(self):
|
def set_reuse_addr(self):
|
||||||
# try to re-use a server port if possible
|
"""try to re-use a server port if possible"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.socket.setsockopt(
|
self.socket.setsockopt(
|
||||||
socket.SOL_SOCKET, socket.SO_REUSEADDR,
|
socket.SOL_SOCKET, socket.SO_REUSEADDR,
|
||||||
self.socket.getsockopt(socket.SOL_SOCKET,
|
self.socket.getsockopt(socket.SOL_SOCKET,
|
||||||
socket.SO_REUSEADDR) | 1
|
socket.SO_REUSEADDR) | 1
|
||||||
)
|
)
|
||||||
except socket.error:
|
except socket.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -593,11 +661,13 @@ class dispatcher:
|
||||||
# ==================================================
|
# ==================================================
|
||||||
|
|
||||||
def readable(self):
|
def readable(self):
|
||||||
|
"""Predicate to indicate download throttle status"""
|
||||||
if maxDownloadRate > 0:
|
if maxDownloadRate > 0:
|
||||||
return downloadBucket > dispatcher.minTx
|
return downloadBucket > dispatcher.minTx
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def writable(self):
|
def writable(self):
|
||||||
|
"""Predicate to indicate upload throttle status"""
|
||||||
if maxUploadRate > 0:
|
if maxUploadRate > 0:
|
||||||
return uploadBucket > dispatcher.minTx
|
return uploadBucket > dispatcher.minTx
|
||||||
return True
|
return True
|
||||||
|
@ -607,21 +677,24 @@ class dispatcher:
|
||||||
# ==================================================
|
# ==================================================
|
||||||
|
|
||||||
def listen(self, num):
|
def listen(self, num):
|
||||||
|
"""Listen on a port"""
|
||||||
self.accepting = True
|
self.accepting = True
|
||||||
if os.name == 'nt' and num > 5:
|
if os.name == 'nt' and num > 5:
|
||||||
num = 5
|
num = 5
|
||||||
return self.socket.listen(num)
|
return self.socket.listen(num)
|
||||||
|
|
||||||
def bind(self, addr):
|
def bind(self, addr):
|
||||||
|
"""Bind to an address"""
|
||||||
self.addr = addr
|
self.addr = addr
|
||||||
return self.socket.bind(addr)
|
return self.socket.bind(addr)
|
||||||
|
|
||||||
def connect(self, address):
|
def connect(self, address):
|
||||||
|
"""Connect to an address"""
|
||||||
self.connected = False
|
self.connected = False
|
||||||
self.connecting = True
|
self.connecting = True
|
||||||
err = self.socket.connect_ex(address)
|
err = self.socket.connect_ex(address)
|
||||||
if err in (EINPROGRESS, EALREADY, EWOULDBLOCK, WSAEWOULDBLOCK) \
|
if err in (EINPROGRESS, EALREADY, EWOULDBLOCK, WSAEWOULDBLOCK) \
|
||||||
or err == EINVAL and os.name in ('nt', 'ce'):
|
or err == EINVAL and os.name in ('nt', 'ce'):
|
||||||
self.addr = address
|
self.addr = address
|
||||||
return
|
return
|
||||||
if err in (0, EISCONN):
|
if err in (0, EISCONN):
|
||||||
|
@ -631,7 +704,7 @@ class dispatcher:
|
||||||
raise socket.error(err, errorcode[err])
|
raise socket.error(err, errorcode[err])
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
# XXX can return either an address pair or None
|
"""Accept incoming connections. Returns either an address pair or None."""
|
||||||
try:
|
try:
|
||||||
conn, addr = self.socket.accept()
|
conn, addr = self.socket.accept()
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -645,6 +718,7 @@ class dispatcher:
|
||||||
return conn, addr
|
return conn, addr
|
||||||
|
|
||||||
def send(self, data):
|
def send(self, data):
|
||||||
|
"""Send data"""
|
||||||
try:
|
try:
|
||||||
result = self.socket.send(data)
|
result = self.socket.send(data)
|
||||||
return result
|
return result
|
||||||
|
@ -658,6 +732,7 @@ class dispatcher:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def recv(self, buffer_size):
|
def recv(self, buffer_size):
|
||||||
|
"""Receive data"""
|
||||||
try:
|
try:
|
||||||
data = self.socket.recv(buffer_size)
|
data = self.socket.recv(buffer_size)
|
||||||
if not data:
|
if not data:
|
||||||
|
@ -665,8 +740,7 @@ class dispatcher:
|
||||||
# a read condition, and having recv() return 0.
|
# a read condition, and having recv() return 0.
|
||||||
self.handle_close()
|
self.handle_close()
|
||||||
return b''
|
return b''
|
||||||
else:
|
return data
|
||||||
return data
|
|
||||||
except socket.error as why:
|
except socket.error as why:
|
||||||
# winsock sometimes raises ENOTCONN
|
# winsock sometimes raises ENOTCONN
|
||||||
if why.args[0] in (EAGAIN, EWOULDBLOCK, WSAEWOULDBLOCK):
|
if why.args[0] in (EAGAIN, EWOULDBLOCK, WSAEWOULDBLOCK):
|
||||||
|
@ -678,6 +752,7 @@ class dispatcher:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""Close connection"""
|
||||||
self.connected = False
|
self.connected = False
|
||||||
self.accepting = False
|
self.accepting = False
|
||||||
self.connecting = False
|
self.connecting = False
|
||||||
|
@ -695,10 +770,10 @@ class dispatcher:
|
||||||
retattr = getattr(self.socket, attr)
|
retattr = getattr(self.socket, attr)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise AttributeError("%s instance has no attribute '%s'"
|
raise AttributeError("%s instance has no attribute '%s'"
|
||||||
%(self.__class__.__name__, attr))
|
% (self.__class__.__name__, attr))
|
||||||
else:
|
else:
|
||||||
msg = "%(me)s.%(attr)s is deprecated; use %(me)s.socket.%(attr)s " \
|
msg = "%(me)s.%(attr)s is deprecated; use %(me)s.socket.%(attr)s " \
|
||||||
"instead" % {'me' : self.__class__.__name__, 'attr' : attr}
|
"instead" % {'me': self.__class__.__name__, 'attr': attr}
|
||||||
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
warnings.warn(msg, DeprecationWarning, stacklevel=2)
|
||||||
return retattr
|
return retattr
|
||||||
|
|
||||||
|
@ -707,13 +782,16 @@ class dispatcher:
|
||||||
# and 'log_info' is for informational, warning and error logging.
|
# and 'log_info' is for informational, warning and error logging.
|
||||||
|
|
||||||
def log(self, message):
|
def log(self, message):
|
||||||
|
"""Log a message to stderr"""
|
||||||
sys.stderr.write('log: %s\n' % str(message))
|
sys.stderr.write('log: %s\n' % str(message))
|
||||||
|
|
||||||
def log_info(self, message, log_type='info'):
|
def log_info(self, message, log_type='info'):
|
||||||
|
"""Conditionally print a message"""
|
||||||
if log_type not in self.ignore_log_types:
|
if log_type not in self.ignore_log_types:
|
||||||
print('%s: %s' % (log_type, message))
|
print '%s: %s' % (log_type, message)
|
||||||
|
|
||||||
def handle_read_event(self):
|
def handle_read_event(self):
|
||||||
|
"""Handle a read event"""
|
||||||
if self.accepting:
|
if self.accepting:
|
||||||
# accepting sockets are never connected, they "spawn" new
|
# accepting sockets are never connected, they "spawn" new
|
||||||
# sockets that are connected
|
# sockets that are connected
|
||||||
|
@ -726,6 +804,7 @@ class dispatcher:
|
||||||
self.handle_read()
|
self.handle_read()
|
||||||
|
|
||||||
def handle_connect_event(self):
|
def handle_connect_event(self):
|
||||||
|
"""Handle a connection event"""
|
||||||
err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
|
err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
|
||||||
if err != 0:
|
if err != 0:
|
||||||
raise socket.error(err, _strerror(err))
|
raise socket.error(err, _strerror(err))
|
||||||
|
@ -734,6 +813,7 @@ class dispatcher:
|
||||||
self.connecting = False
|
self.connecting = False
|
||||||
|
|
||||||
def handle_write_event(self):
|
def handle_write_event(self):
|
||||||
|
"""Handle a write event"""
|
||||||
if self.accepting:
|
if self.accepting:
|
||||||
# Accepting sockets shouldn't get a write event.
|
# Accepting sockets shouldn't get a write event.
|
||||||
# We will pretend it didn't happen.
|
# We will pretend it didn't happen.
|
||||||
|
@ -745,6 +825,7 @@ class dispatcher:
|
||||||
self.handle_write()
|
self.handle_write()
|
||||||
|
|
||||||
def handle_expt_event(self):
|
def handle_expt_event(self):
|
||||||
|
"""Handle expected exceptions"""
|
||||||
# handle_expt_event() is called if there might be an error on the
|
# handle_expt_event() is called if there might be an error on the
|
||||||
# socket, or if there is OOB data
|
# socket, or if there is OOB data
|
||||||
# check for the error condition first
|
# check for the error condition first
|
||||||
|
@ -763,12 +844,13 @@ class dispatcher:
|
||||||
self.handle_expt()
|
self.handle_expt()
|
||||||
|
|
||||||
def handle_error(self):
|
def handle_error(self):
|
||||||
nil, t, v, tbinfo = compact_traceback()
|
"""Handle unexpected exceptions"""
|
||||||
|
_, t, v, tbinfo = compact_traceback()
|
||||||
|
|
||||||
# sometimes a user repr method will crash.
|
# sometimes a user repr method will crash.
|
||||||
try:
|
try:
|
||||||
self_repr = repr(self)
|
self_repr = repr(self)
|
||||||
except:
|
except BaseException:
|
||||||
self_repr = '<__repr__(self) failed for object at %0x>' % id(self)
|
self_repr = '<__repr__(self) failed for object at %0x>' % id(self)
|
||||||
|
|
||||||
self.log_info(
|
self.log_info(
|
||||||
|
@ -777,89 +859,110 @@ class dispatcher:
|
||||||
t,
|
t,
|
||||||
v,
|
v,
|
||||||
tbinfo
|
tbinfo
|
||||||
),
|
),
|
||||||
'error'
|
'error'
|
||||||
)
|
)
|
||||||
self.handle_close()
|
self.handle_close()
|
||||||
|
|
||||||
def handle_expt(self):
|
|
||||||
self.log_info('unhandled incoming priority event', 'warning')
|
|
||||||
|
|
||||||
def handle_read(self):
|
|
||||||
self.log_info('unhandled read event', 'warning')
|
|
||||||
|
|
||||||
def handle_write(self):
|
|
||||||
self.log_info('unhandled write event', 'warning')
|
|
||||||
|
|
||||||
def handle_connect(self):
|
|
||||||
self.log_info('unhandled connect event', 'warning')
|
|
||||||
|
|
||||||
def handle_accept(self):
|
def handle_accept(self):
|
||||||
|
"""Handle an accept event"""
|
||||||
pair = self.accept()
|
pair = self.accept()
|
||||||
if pair is not None:
|
if pair is not None:
|
||||||
self.handle_accepted(*pair)
|
self.handle_accepted(*pair)
|
||||||
|
|
||||||
|
def handle_expt(self):
|
||||||
|
"""Log that the subclass does not implement handle_expt"""
|
||||||
|
self.log_info('unhandled incoming priority event', 'warning')
|
||||||
|
|
||||||
|
def handle_read(self):
|
||||||
|
"""Log that the subclass does not implement handle_read"""
|
||||||
|
self.log_info('unhandled read event', 'warning')
|
||||||
|
|
||||||
|
def handle_write(self):
|
||||||
|
"""Log that the subclass does not implement handle_write"""
|
||||||
|
self.log_info('unhandled write event', 'warning')
|
||||||
|
|
||||||
|
def handle_connect(self):
|
||||||
|
"""Log that the subclass does not implement handle_connect"""
|
||||||
|
self.log_info('unhandled connect event', 'warning')
|
||||||
|
|
||||||
def handle_accepted(self, sock, addr):
|
def handle_accepted(self, sock, addr):
|
||||||
|
"""Log that the subclass does not implement handle_accepted"""
|
||||||
sock.close()
|
sock.close()
|
||||||
self.log_info('unhandled accepted event on %s' % (addr), 'warning')
|
self.log_info('unhandled accepted event on %s' % (addr), 'warning')
|
||||||
|
|
||||||
def handle_close(self):
|
def handle_close(self):
|
||||||
|
"""Log that the subclass does not implement handle_close"""
|
||||||
self.log_info('unhandled close event', 'warning')
|
self.log_info('unhandled close event', 'warning')
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
# adds simple buffered output capability, useful for simple clients.
|
|
||||||
# [for more sophisticated usage use asynchat.async_chat]
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class dispatcher_with_send(dispatcher):
|
class dispatcher_with_send(dispatcher):
|
||||||
|
"""
|
||||||
|
adds simple buffered output capability, useful for simple clients.
|
||||||
|
[for more sophisticated usage use asynchat.async_chat]
|
||||||
|
"""
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
def __init__(self, sock=None, map=None):
|
def __init__(self, sock=None, map=None):
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
dispatcher.__init__(self, sock, map)
|
dispatcher.__init__(self, sock, map)
|
||||||
self.out_buffer = b''
|
self.out_buffer = b''
|
||||||
|
|
||||||
def initiate_send(self):
|
def initiate_send(self):
|
||||||
|
"""Initiate a send"""
|
||||||
num_sent = 0
|
num_sent = 0
|
||||||
num_sent = dispatcher.send(self, self.out_buffer[:512])
|
num_sent = dispatcher.send(self, self.out_buffer[:512])
|
||||||
self.out_buffer = self.out_buffer[num_sent:]
|
self.out_buffer = self.out_buffer[num_sent:]
|
||||||
|
|
||||||
def handle_write(self):
|
def handle_write(self):
|
||||||
|
"""Handle a write event"""
|
||||||
self.initiate_send()
|
self.initiate_send()
|
||||||
|
|
||||||
def writable(self):
|
def writable(self):
|
||||||
return (not self.connected) or len(self.out_buffer)
|
"""Predicate to indicate if the object is writable"""
|
||||||
|
return not self.connected or len(self.out_buffer)
|
||||||
|
|
||||||
def send(self, data):
|
def send(self, data):
|
||||||
|
"""Send data"""
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.log_info('sending %s' % repr(data))
|
self.log_info('sending %s' % repr(data))
|
||||||
self.out_buffer = self.out_buffer + data
|
self.out_buffer = self.out_buffer + data
|
||||||
self.initiate_send()
|
self.initiate_send()
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# used for debugging.
|
# used for debugging.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def compact_traceback():
|
def compact_traceback():
|
||||||
|
"""Return a compact traceback"""
|
||||||
t, v, tb = sys.exc_info()
|
t, v, tb = sys.exc_info()
|
||||||
tbinfo = []
|
tbinfo = []
|
||||||
if not tb: # Must have a traceback
|
if not tb: # Must have a traceback
|
||||||
raise AssertionError("traceback does not exist")
|
raise AssertionError("traceback does not exist")
|
||||||
while tb:
|
while tb:
|
||||||
tbinfo.append((
|
tbinfo.append((
|
||||||
tb.tb_frame.f_code.co_filename,
|
tb.tb_frame.f_code.co_filename,
|
||||||
tb.tb_frame.f_code.co_name,
|
tb.tb_frame.f_code.co_name,
|
||||||
str(tb.tb_lineno)
|
str(tb.tb_lineno)
|
||||||
))
|
))
|
||||||
tb = tb.tb_next
|
tb = tb.tb_next
|
||||||
|
|
||||||
# just to be safe
|
# just to be safe
|
||||||
del tb
|
del tb
|
||||||
|
|
||||||
file, function, line = tbinfo[-1]
|
filename, function, line = tbinfo[-1]
|
||||||
info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo])
|
info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo])
|
||||||
return (file, function, line), t, v, info
|
return (filename, function, line), t, v, info
|
||||||
|
|
||||||
|
|
||||||
def close_all(map=None, ignore_all=False):
|
def close_all(map=None, ignore_all=False):
|
||||||
|
"""Close all connections"""
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
if map is None:
|
if map is None:
|
||||||
map = socket_map
|
map = socket_map
|
||||||
for x in list(map.values()):
|
for x in list(map.values()):
|
||||||
|
@ -872,11 +975,12 @@ def close_all(map=None, ignore_all=False):
|
||||||
raise
|
raise
|
||||||
except _reraised_exceptions:
|
except _reraised_exceptions:
|
||||||
raise
|
raise
|
||||||
except:
|
except BaseException:
|
||||||
if not ignore_all:
|
if not ignore_all:
|
||||||
raise
|
raise
|
||||||
map.clear()
|
map.clear()
|
||||||
|
|
||||||
|
|
||||||
# Asynchronous File I/O:
|
# Asynchronous File I/O:
|
||||||
#
|
#
|
||||||
# After a little research (reading man pages on various unixen, and
|
# After a little research (reading man pages on various unixen, and
|
||||||
|
@ -890,27 +994,34 @@ def close_all(map=None, ignore_all=False):
|
||||||
#
|
#
|
||||||
# Regardless, this is useful for pipes, and stdin/stdout...
|
# Regardless, this is useful for pipes, and stdin/stdout...
|
||||||
|
|
||||||
|
|
||||||
if os.name == 'posix':
|
if os.name == 'posix':
|
||||||
import fcntl
|
import fcntl
|
||||||
|
|
||||||
class file_wrapper:
|
class file_wrapper:
|
||||||
# Here we override just enough to make a file
|
"""
|
||||||
# look like a socket for the purposes of asyncore.
|
Here we override just enough to make a file look like a socket for the purposes of asyncore.
|
||||||
# The passed fd is automatically os.dup()'d
|
|
||||||
|
The passed fd is automatically os.dup()'d
|
||||||
|
"""
|
||||||
|
# pylint: disable=old-style-class
|
||||||
|
|
||||||
def __init__(self, fd):
|
def __init__(self, fd):
|
||||||
self.fd = os.dup(fd)
|
self.fd = os.dup(fd)
|
||||||
|
|
||||||
def recv(self, *args):
|
def recv(self, *args):
|
||||||
|
"""Fake recv()"""
|
||||||
return os.read(self.fd, *args)
|
return os.read(self.fd, *args)
|
||||||
|
|
||||||
def send(self, *args):
|
def send(self, *args):
|
||||||
|
"""Fake send()"""
|
||||||
return os.write(self.fd, *args)
|
return os.write(self.fd, *args)
|
||||||
|
|
||||||
def getsockopt(self, level, optname, buflen=None):
|
def getsockopt(self, level, optname, buflen=None):
|
||||||
|
"""Fake getsockopt()"""
|
||||||
if (level == socket.SOL_SOCKET and
|
if (level == socket.SOL_SOCKET and
|
||||||
optname == socket.SO_ERROR and
|
optname == socket.SO_ERROR and
|
||||||
not buflen):
|
not buflen):
|
||||||
return 0
|
return 0
|
||||||
raise NotImplementedError("Only asyncore specific behaviour "
|
raise NotImplementedError("Only asyncore specific behaviour "
|
||||||
"implemented.")
|
"implemented.")
|
||||||
|
@ -919,14 +1030,19 @@ if os.name == 'posix':
|
||||||
write = send
|
write = send
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""Fake close()"""
|
||||||
os.close(self.fd)
|
os.close(self.fd)
|
||||||
|
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
|
"""Fake fileno()"""
|
||||||
return self.fd
|
return self.fd
|
||||||
|
|
||||||
class file_dispatcher(dispatcher):
|
class file_dispatcher(dispatcher):
|
||||||
|
"""A dispatcher for file_wrapper objects"""
|
||||||
|
|
||||||
def __init__(self, fd, map=None):
|
def __init__(self, fd, map=None):
|
||||||
|
# pylint: disable=redefined-builtin
|
||||||
|
|
||||||
dispatcher.__init__(self, None, map)
|
dispatcher.__init__(self, None, map)
|
||||||
self.connected = True
|
self.connected = True
|
||||||
try:
|
try:
|
||||||
|
@ -940,6 +1056,7 @@ if os.name == 'posix':
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
|
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
|
||||||
|
|
||||||
def set_file(self, fd):
|
def set_file(self, fd):
|
||||||
|
"""Set file"""
|
||||||
self.socket = file_wrapper(fd)
|
self.socket = file_wrapper(fd)
|
||||||
self._fileno = self.socket.fileno()
|
self._fileno = self.socket.fileno()
|
||||||
self.add_channel()
|
self.add_channel()
|
||||||
|
|
|
@ -1,41 +1,42 @@
|
||||||
import base64
|
# pylint: disable=too-many-ancestors
|
||||||
from binascii import hexlify
|
"""
|
||||||
import hashlib
|
src/network/tcp.py
|
||||||
import math
|
==================
|
||||||
import time
|
"""
|
||||||
from pprint import pprint
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
import random
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
from addresses import calculateInventoryHash
|
import math
|
||||||
from debug import logger
|
import random
|
||||||
from helper_random import randomBytes
|
import socket
|
||||||
import helper_random
|
import time
|
||||||
from inventory import Inventory
|
|
||||||
import knownnodes
|
|
||||||
from network.advanceddispatcher import AdvancedDispatcher
|
|
||||||
from network.bmproto import BMProtoError, BMProtoInsufficientDataError, BMProtoExcessiveDataError, BMProto
|
|
||||||
from network.bmobject import BMObject, BMObjectInsufficientPOWError, BMObjectInvalidDataError, BMObjectExpiredError, BMObjectUnwantedStreamError, BMObjectInvalidError, BMObjectAlreadyHaveError
|
|
||||||
import network.connectionpool
|
|
||||||
from network.dandelion import Dandelion
|
|
||||||
from network.node import Node
|
|
||||||
import network.asyncore_pollchoose as asyncore
|
|
||||||
from network.proxy import Proxy, ProxyError, GeneralProxyError
|
|
||||||
from network.objectracker import ObjectTracker
|
|
||||||
from network.socks5 import Socks5Connection, Socks5Resolver, Socks5AuthError, Socks5Error
|
|
||||||
from network.socks4a import Socks4aConnection, Socks4aResolver, Socks4aError
|
|
||||||
from network.tls import TLSDispatcher
|
|
||||||
|
|
||||||
import addresses
|
import addresses
|
||||||
from bmconfigparser import BMConfigParser
|
import helper_random
|
||||||
from queues import invQueue, objectProcessorQueue, portCheckerQueue, UISignalQueue, receiveDataQueue
|
import knownnodes
|
||||||
|
import network.asyncore_pollchoose as asyncore
|
||||||
|
import network.connectionpool
|
||||||
|
import protocol
|
||||||
import shared
|
import shared
|
||||||
import state
|
import state
|
||||||
import protocol
|
from bmconfigparser import BMConfigParser
|
||||||
|
from debug import logger
|
||||||
|
from helper_random import randomBytes
|
||||||
|
from inventory import Inventory
|
||||||
|
from network.advanceddispatcher import AdvancedDispatcher
|
||||||
|
from network.bmproto import BMProto
|
||||||
|
from network.dandelion import Dandelion
|
||||||
|
from network.objectracker import ObjectTracker
|
||||||
|
from network.socks4a import Socks4aConnection
|
||||||
|
from network.socks5 import Socks5Connection
|
||||||
|
from network.tls import TLSDispatcher
|
||||||
|
from queues import UISignalQueue, invQueue, receiveDataQueue
|
||||||
|
|
||||||
|
|
||||||
|
class TCPConnection(BMProto, TLSDispatcher): # pylint: disable=too-many-instance-attributes
|
||||||
|
"""
|
||||||
|
|
||||||
|
.. todo:: Look to understand and/or fix the non-parent-init-called
|
||||||
|
"""
|
||||||
|
|
||||||
class TCPConnection(BMProto, TLSDispatcher):
|
|
||||||
def __init__(self, address=None, sock=None):
|
def __init__(self, address=None, sock=None):
|
||||||
BMProto.__init__(self, address=address, sock=sock)
|
BMProto.__init__(self, address=address, sock=sock)
|
||||||
self.verackReceived = False
|
self.verackReceived = False
|
||||||
|
@ -67,18 +68,25 @@ class TCPConnection(BMProto, TLSDispatcher):
|
||||||
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)
|
||||||
if protocol.checkIPAddress(encodedAddr, True) and not protocol.checkSocksIP(self.destination.host):
|
self.local = all([
|
||||||
self.local = True
|
protocol.checkIPAddress(encodedAddr, True),
|
||||||
else:
|
not protocol.checkSocksIP(self.destination.host)
|
||||||
self.local = False
|
])
|
||||||
#shared.connectedHostsList[self.destination] = 0
|
ObjectTracker.__init__(self) # pylint: disable=non-parent-init-called
|
||||||
ObjectTracker.__init__(self)
|
|
||||||
self.bm_proto_reset()
|
self.bm_proto_reset()
|
||||||
self.set_state("bm_header", expectBytes=protocol.Header.size)
|
self.set_state("bm_header", expectBytes=protocol.Header.size)
|
||||||
|
|
||||||
def antiIntersectionDelay(self, initial = False):
|
def antiIntersectionDelay(self, initial=False):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
established. It will 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 whole network
|
||||||
delay = math.ceil(math.log(max(len(knownnodes.knownNodes[x]) for x in knownnodes.knownNodes) + 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
|
||||||
|
@ -93,12 +101,17 @@ class TCPConnection(BMProto, TLSDispatcher):
|
||||||
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
|
||||||
|
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")
|
||||||
self.bm_proto_reset()
|
self.bm_proto_reset()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_connection_fully_established(self):
|
def set_connection_fully_established(self):
|
||||||
|
"""Initiate inventory synchronisation."""
|
||||||
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'))
|
||||||
|
@ -113,50 +126,50 @@ class TCPConnection(BMProto, TLSDispatcher):
|
||||||
self.sendBigInv()
|
self.sendBigInv()
|
||||||
|
|
||||||
def sendAddr(self):
|
def sendAddr(self):
|
||||||
|
"""Send a partial list of known addresses to peer."""
|
||||||
# We are going to share a maximum number of 1000 addrs (per overlapping
|
# We are going to share a maximum number of 1000 addrs (per overlapping
|
||||||
# stream) with our peer. 500 from overlapping streams, 250 from the
|
# stream) with our peer. 500 from overlapping streams, 250 from the
|
||||||
# left child stream, and 250 from the right child stream.
|
# left child stream, and 250 from the right child stream.
|
||||||
maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500)
|
maxAddrCount = BMConfigParser().safeGetInt("bitmessagesettings", "maxaddrperstreamsend", 500)
|
||||||
|
|
||||||
# init
|
# init
|
||||||
addressCount = 0
|
|
||||||
payload = b''
|
|
||||||
|
|
||||||
templist = []
|
templist = []
|
||||||
addrs = {}
|
addrs = {}
|
||||||
for stream in self.streams:
|
for stream in self.streams:
|
||||||
with knownnodes.knownNodesLock:
|
with knownnodes.knownNodesLock:
|
||||||
if len(knownnodes.knownNodes[stream]) > 0:
|
if knownnodes.knownNodes[stream]:
|
||||||
filtered = {k: v for k, v in knownnodes.knownNodes[stream].items()
|
filtered = {k: v for k, v in knownnodes.knownNodes[stream].items()
|
||||||
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
||||||
elemCount = len(filtered)
|
elemCount = len(filtered)
|
||||||
if elemCount > maxAddrCount:
|
if elemCount > maxAddrCount:
|
||||||
elemCount = maxAddrCount
|
elemCount = maxAddrCount
|
||||||
# only if more recent than 3 hours
|
# only if more recent than 3 hours
|
||||||
addrs[stream] = helper_random.randomsample(filtered.items(), elemCount)
|
addrs[stream] = helper_random.randomsample(filtered.items(), elemCount)
|
||||||
# sent 250 only if the remote isn't interested in it
|
# sent 250 only if the remote isn't interested in it
|
||||||
if len(knownnodes.knownNodes[stream * 2]) > 0 and stream not in self.streams:
|
if knownnodes.knownNodes[stream * 2] and stream not in self.streams:
|
||||||
filtered = {k: v for k, v in knownnodes.knownNodes[stream*2].items()
|
filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2].items()
|
||||||
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
||||||
elemCount = len(filtered)
|
elemCount = len(filtered)
|
||||||
if elemCount > maxAddrCount / 2:
|
if elemCount > maxAddrCount / 2:
|
||||||
elemCount = int(maxAddrCount / 2)
|
elemCount = int(maxAddrCount / 2)
|
||||||
addrs[stream * 2] = helper_random.randomsample(filtered.items(), elemCount)
|
addrs[stream * 2] = helper_random.randomsample(filtered.items(), elemCount)
|
||||||
if len(knownnodes.knownNodes[(stream * 2) + 1]) > 0 and stream not in self.streams:
|
if knownnodes.knownNodes[(stream * 2) + 1] and stream not in self.streams:
|
||||||
filtered = {k: v for k, v in knownnodes.knownNodes[stream*2+1].items()
|
filtered = {k: v for k, v in knownnodes.knownNodes[stream * 2 + 1].items()
|
||||||
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
if v["lastseen"] > (int(time.time()) - shared.maximumAgeOfNodesThatIAdvertiseToOthers)}
|
||||||
elemCount = len(filtered)
|
elemCount = len(filtered)
|
||||||
if elemCount > maxAddrCount / 2:
|
if elemCount > maxAddrCount / 2:
|
||||||
elemCount = int(maxAddrCount / 2)
|
elemCount = int(maxAddrCount / 2)
|
||||||
addrs[stream * 2 + 1] = helper_random.randomsample(filtered.items(), elemCount)
|
addrs[stream * 2 + 1] = helper_random.randomsample(filtered.items(), elemCount)
|
||||||
for substream in addrs.keys():
|
for substream in addrs:
|
||||||
for peer, params in addrs[substream]:
|
for peer, params in addrs[substream]:
|
||||||
templist.append((substream, peer, params["lastseen"]))
|
templist.append((substream, peer, params["lastseen"]))
|
||||||
if len(templist) > 0:
|
if templist:
|
||||||
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."""
|
||||||
def sendChunk():
|
def sendChunk():
|
||||||
|
"""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('Sending huge inv message with %i objects to just this one peer', objectCount)
|
||||||
|
@ -172,13 +185,12 @@ class TCPConnection(BMProto, TLSDispatcher):
|
||||||
if Dandelion().hasHash(objHash):
|
if Dandelion().hasHash(objHash):
|
||||||
continue
|
continue
|
||||||
bigInvList[objHash] = 0
|
bigInvList[objHash] = 0
|
||||||
#self.objectsNewToThem[objHash] = time.time()
|
|
||||||
objectCount = 0
|
objectCount = 0
|
||||||
payload = b''
|
payload = b''
|
||||||
# Now let us start appending all of these hashes together. They will be
|
# Now let us start appending all of these hashes together. They will be
|
||||||
# sent out in a big inv message to our new peer.
|
# sent out in a big inv message to our new peer.
|
||||||
for hash, storedValue in bigInvList.items():
|
for obj_hash, _ in bigInvList.items():
|
||||||
payload += hash
|
payload += obj_hash
|
||||||
objectCount += 1
|
objectCount += 1
|
||||||
|
|
||||||
# Remove -1 below when sufficient time has passed for users to
|
# Remove -1 below when sufficient time has passed for users to
|
||||||
|
@ -193,20 +205,26 @@ class TCPConnection(BMProto, TLSDispatcher):
|
||||||
sendChunk()
|
sendChunk()
|
||||||
|
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
|
"""Callback for TCP connection being established."""
|
||||||
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:
|
if e.errno in asyncore._DISCONNECTED: # pylint: disable=protected-access
|
||||||
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, str(e))
|
||||||
return
|
return
|
||||||
self.nodeid = randomBytes(8)
|
self.nodeid = randomBytes(8)
|
||||||
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
|
self.append_write_buf(
|
||||||
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
|
protocol.assembleVersionMessage(
|
||||||
#print "%s:%i: Sending version" % (self.destination.host, self.destination.port)
|
self.destination.host,
|
||||||
|
self.destination.port,
|
||||||
|
network.connectionpool.BMConnectionPool().streams,
|
||||||
|
False,
|
||||||
|
nodeid=self.nodeid))
|
||||||
self.connectedAt = time.time()
|
self.connectedAt = time.time()
|
||||||
receiveDataQueue.put(self.destination)
|
receiveDataQueue.put(self.destination)
|
||||||
|
|
||||||
def handle_read(self):
|
def handle_read(self):
|
||||||
|
"""Callback for reading from a socket"""
|
||||||
TLSDispatcher.handle_read(self)
|
TLSDispatcher.handle_read(self)
|
||||||
if self.isOutbound and self.fullyEstablished:
|
if self.isOutbound and self.fullyEstablished:
|
||||||
for s in self.streams:
|
for s in self.streams:
|
||||||
|
@ -218,9 +236,11 @@ class TCPConnection(BMProto, TLSDispatcher):
|
||||||
receiveDataQueue.put(self.destination)
|
receiveDataQueue.put(self.destination)
|
||||||
|
|
||||||
def handle_write(self):
|
def handle_write(self):
|
||||||
|
"""Callback for writing to a socket"""
|
||||||
TLSDispatcher.handle_write(self)
|
TLSDispatcher.handle_write(self)
|
||||||
|
|
||||||
def handle_close(self):
|
def handle_close(self):
|
||||||
|
"""Callback for connection being closed."""
|
||||||
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:
|
||||||
|
@ -231,37 +251,55 @@ class TCPConnection(BMProto, TLSDispatcher):
|
||||||
|
|
||||||
|
|
||||||
class Socks5BMConnection(Socks5Connection, TCPConnection):
|
class Socks5BMConnection(Socks5Connection, TCPConnection):
|
||||||
|
"""SOCKS5 wrapper for TCP connections"""
|
||||||
|
|
||||||
def __init__(self, address):
|
def __init__(self, address):
|
||||||
Socks5Connection.__init__(self, address=address)
|
Socks5Connection.__init__(self, address=address)
|
||||||
TCPConnection.__init__(self, address=address, sock=self.socket)
|
TCPConnection.__init__(self, address=address, sock=self.socket)
|
||||||
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."""
|
||||||
Socks5Connection.state_proxy_handshake_done(self)
|
Socks5Connection.state_proxy_handshake_done(self)
|
||||||
self.nodeid = randomBytes(8)
|
self.nodeid = randomBytes(8)
|
||||||
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
|
self.append_write_buf(
|
||||||
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
|
protocol.assembleVersionMessage(
|
||||||
|
self.destination.host,
|
||||||
|
self.destination.port,
|
||||||
|
network.connectionpool.BMConnectionPool().streams,
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class Socks4aBMConnection(Socks4aConnection, TCPConnection):
|
class Socks4aBMConnection(Socks4aConnection, TCPConnection):
|
||||||
|
"""SOCKS4a wrapper for TCP connections"""
|
||||||
|
|
||||||
def __init__(self, address):
|
def __init__(self, address):
|
||||||
Socks4aConnection.__init__(self, address=address)
|
Socks4aConnection.__init__(self, address=address)
|
||||||
TCPConnection.__init__(self, address=address, sock=self.socket)
|
TCPConnection.__init__(self, address=address, sock=self.socket)
|
||||||
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."""
|
||||||
Socks4aConnection.state_proxy_handshake_done(self)
|
Socks4aConnection.state_proxy_handshake_done(self)
|
||||||
self.nodeid = randomBytes(8)
|
self.nodeid = randomBytes(8)
|
||||||
self.append_write_buf(protocol.assembleVersionMessage(self.destination.host, self.destination.port, \
|
self.append_write_buf(
|
||||||
network.connectionpool.BMConnectionPool().streams, False, nodeid=self.nodeid))
|
protocol.assembleVersionMessage(
|
||||||
|
self.destination.host,
|
||||||
|
self.destination.port,
|
||||||
|
network.connectionpool.BMConnectionPool().streams,
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class TCPServer(AdvancedDispatcher):
|
class TCPServer(AdvancedDispatcher):
|
||||||
def __init__(self, host='127.0.0.1', port=8444):
|
"""TCP connection server for Bitmessage protocol"""
|
||||||
|
|
||||||
|
def __init__(self, host='127.0.0.1', port=8444): # pylint: disable=redefined-outer-name
|
||||||
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)
|
||||||
|
@ -284,20 +322,22 @@ class TCPServer(AdvancedDispatcher):
|
||||||
self.listen(5)
|
self.listen(5)
|
||||||
|
|
||||||
def is_bound(self):
|
def is_bound(self):
|
||||||
|
"""Is the socket bound?"""
|
||||||
try:
|
try:
|
||||||
return self.bound
|
return self.bound
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def handle_accept(self):
|
def handle_accept(self):
|
||||||
|
"""Incoming connection callback"""
|
||||||
pair = self.accept()
|
pair = self.accept()
|
||||||
if pair is not None:
|
if pair is not None:
|
||||||
sock, addr = pair
|
sock, _ = pair
|
||||||
state.ownAddresses[state.Peer(sock.getsockname()[0], sock.getsockname()[1])] = True
|
state.ownAddresses[state.Peer(sock.getsockname()[0], sock.getsockname()[1])] = True
|
||||||
if len(network.connectionpool.BMConnectionPool().inboundConnections) + \
|
if len(network.connectionpool.BMConnectionPool().inboundConnections) + \
|
||||||
len(network.connectionpool.BMConnectionPool().outboundConnections) > \
|
len(network.connectionpool.BMConnectionPool().outboundConnections) > \
|
||||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
|
BMConfigParser().safeGetInt("bitmessagesettings", "maxtotalconnections") + \
|
||||||
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10:
|
BMConfigParser().safeGetInt("bitmessagesettings", "maxbootstrapconnections") + 10:
|
||||||
# 10 is a sort of buffer, in between it will go through the version handshake
|
# 10 is a sort of buffer, in between it will go through the version handshake
|
||||||
# and return an error to the peer
|
# and return an error to the peer
|
||||||
logger.warning("Server full, dropping connection")
|
logger.warning("Server full, dropping connection")
|
||||||
|
@ -314,17 +354,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
for host in (("127.0.0.1", 8448),):
|
for host in (("127.0.0.1", 8448),):
|
||||||
direct = TCPConnection(host)
|
direct = TCPConnection(host)
|
||||||
while len(asyncore.socket_map) > 0:
|
while asyncore.socket_map:
|
||||||
print "loop, state = %s" % (direct.state)
|
print "loop, state = %s" % (direct.state)
|
||||||
asyncore.loop(timeout=10, count=1)
|
asyncore.loop(timeout=10, count=1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
proxy = Socks5BMConnection(host)
|
|
||||||
while len(asyncore.socket_map) > 0:
|
|
||||||
# print "loop, state = %s" % (proxy.state)
|
|
||||||
asyncore.loop(timeout=10, count=1)
|
|
||||||
|
|
||||||
proxy = Socks4aBMConnection(host)
|
|
||||||
while len(asyncore.socket_map) > 0:
|
|
||||||
# print "loop, state = %s" % (proxy.state)
|
|
||||||
asyncore.loop(timeout=10, count=1)
|
|
||||||
|
|
|
@ -1,60 +1,71 @@
|
||||||
#import shared
|
# pylint: disable=too-many-branches,too-many-statements,protected-access
|
||||||
#import time
|
"""
|
||||||
#from multiprocessing import Pool, cpu_count
|
src/proofofwork.py
|
||||||
|
==================
|
||||||
|
"""
|
||||||
|
|
||||||
|
import ctypes
|
||||||
import hashlib
|
import hashlib
|
||||||
from struct import unpack, pack
|
import os
|
||||||
from subprocess import call
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from struct import pack, unpack
|
||||||
|
from subprocess import call
|
||||||
|
|
||||||
|
import openclpow
|
||||||
|
import paths
|
||||||
|
import queues
|
||||||
|
import state
|
||||||
|
import tr
|
||||||
from bmconfigparser import BMConfigParser
|
from bmconfigparser import BMConfigParser
|
||||||
from debug import logger
|
from debug import logger
|
||||||
import paths
|
|
||||||
import openclpow
|
|
||||||
import queues
|
|
||||||
import tr
|
|
||||||
import os
|
|
||||||
import ctypes
|
|
||||||
|
|
||||||
import state
|
|
||||||
|
|
||||||
bitmsglib = 'bitmsghash.so'
|
bitmsglib = 'bitmsghash.so'
|
||||||
|
|
||||||
bmpow = None
|
bmpow = None
|
||||||
|
|
||||||
|
|
||||||
def _set_idle():
|
def _set_idle():
|
||||||
if 'linux' in sys.platform:
|
if 'linux' in sys.platform:
|
||||||
os.nice(20)
|
os.nice(20)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
# pylint: disable=no-member,import-error
|
||||||
sys.getwindowsversion()
|
sys.getwindowsversion()
|
||||||
import win32api,win32process,win32con # @UnresolvedImport
|
import win32api
|
||||||
|
import win32process
|
||||||
|
import win32con
|
||||||
pid = win32api.GetCurrentProcessId()
|
pid = win32api.GetCurrentProcessId()
|
||||||
handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
|
handle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, True, pid)
|
||||||
win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS)
|
win32process.SetPriorityClass(handle, win32process.IDLE_PRIORITY_CLASS)
|
||||||
except:
|
except:
|
||||||
#Windows 64-bit
|
# Windows 64-bit
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _pool_worker(nonce, initialHash, target, pool_size):
|
def _pool_worker(nonce, initialHash, target, pool_size):
|
||||||
_set_idle()
|
_set_idle()
|
||||||
trialValue = float('inf')
|
trialValue = float('inf')
|
||||||
while trialValue > target:
|
while trialValue > target:
|
||||||
nonce += pool_size
|
nonce += pool_size
|
||||||
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
|
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(
|
||||||
|
pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
|
||||||
return [trialValue, nonce]
|
return [trialValue, nonce]
|
||||||
|
|
||||||
|
|
||||||
def _doSafePoW(target, initialHash):
|
def _doSafePoW(target, initialHash):
|
||||||
logger.debug("Safe PoW start")
|
logger.debug("Safe PoW start")
|
||||||
nonce = 0
|
nonce = 0
|
||||||
trialValue = float('inf')
|
trialValue = float('inf')
|
||||||
while trialValue > target and state.shutdown == 0:
|
while trialValue > target and state.shutdown == 0:
|
||||||
nonce += 1
|
nonce += 1
|
||||||
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
|
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(
|
||||||
|
pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
|
||||||
if state.shutdown != 0:
|
if state.shutdown != 0:
|
||||||
raise StopIteration("Interrupted")
|
raise StopIteration("Interrupted") # pylint: misplaced-bare-raise
|
||||||
logger.debug("Safe PoW done")
|
logger.debug("Safe PoW done")
|
||||||
return [trialValue, nonce]
|
return [trialValue, nonce]
|
||||||
|
|
||||||
|
|
||||||
def _doFastPoW(target, initialHash):
|
def _doFastPoW(target, initialHash):
|
||||||
logger.debug("Fast PoW start")
|
logger.debug("Fast PoW start")
|
||||||
from multiprocessing import Pool, cpu_count
|
from multiprocessing import Pool, cpu_count
|
||||||
|
@ -97,6 +108,7 @@ def _doFastPoW(target, initialHash):
|
||||||
return result[0], result[1]
|
return result[0], result[1]
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
|
||||||
def _doCPoW(target, initialHash):
|
def _doCPoW(target, initialHash):
|
||||||
h = initialHash
|
h = initialHash
|
||||||
m = target
|
m = target
|
||||||
|
@ -104,21 +116,29 @@ def _doCPoW(target, initialHash):
|
||||||
out_m = ctypes.c_ulonglong(m)
|
out_m = ctypes.c_ulonglong(m)
|
||||||
logger.debug("C PoW start")
|
logger.debug("C PoW start")
|
||||||
nonce = bmpow(out_h, out_m)
|
nonce = bmpow(out_h, out_m)
|
||||||
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
|
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
|
||||||
if state.shutdown != 0:
|
if state.shutdown != 0:
|
||||||
raise StopIteration("Interrupted")
|
raise StopIteration("Interrupted")
|
||||||
logger.debug("C PoW done")
|
logger.debug("C PoW done")
|
||||||
return [trialValue, nonce]
|
return [trialValue, nonce]
|
||||||
|
|
||||||
|
|
||||||
def _doGPUPoW(target, initialHash):
|
def _doGPUPoW(target, initialHash):
|
||||||
logger.debug("GPU PoW start")
|
logger.debug("GPU PoW start")
|
||||||
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
|
nonce = openclpow.do_opencl_pow(initialHash.encode("hex"), target)
|
||||||
trialValue, = unpack('>Q',hashlib.sha512(hashlib.sha512(pack('>Q',nonce) + initialHash).digest()).digest()[0:8])
|
trialValue, = unpack('>Q', hashlib.sha512(hashlib.sha512(pack('>Q', nonce) + initialHash).digest()).digest()[0:8])
|
||||||
#print "{} - value {} < {}".format(nonce, trialValue, target)
|
|
||||||
if trialValue > target:
|
if trialValue > target:
|
||||||
deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus)
|
deviceNames = ", ".join(gpu.name for gpu in openclpow.enabledGpus)
|
||||||
queues.UISignalQueue.put(('updateStatusBar', (tr._translate("MainWindow",'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.'), 1)))
|
queues.UISignalQueue.put((
|
||||||
logger.error("Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.", deviceNames)
|
'updateStatusBar', (
|
||||||
|
tr._translate(
|
||||||
|
"MainWindow",
|
||||||
|
'Your GPU(s) did not calculate correctly, disabling OpenCL. Please report to the developers.'
|
||||||
|
),
|
||||||
|
1)))
|
||||||
|
logger.error(
|
||||||
|
"Your GPUs (%s) did not calculate correctly, disabling OpenCL. Please report to the developers.",
|
||||||
|
deviceNames)
|
||||||
openclpow.enabledGpus = []
|
openclpow.enabledGpus = []
|
||||||
raise Exception("GPU did not calculate correctly.")
|
raise Exception("GPU did not calculate correctly.")
|
||||||
if state.shutdown != 0:
|
if state.shutdown != 0:
|
||||||
|
@ -126,11 +146,17 @@ def _doGPUPoW(target, initialHash):
|
||||||
logger.debug("GPU PoW done")
|
logger.debug("GPU PoW done")
|
||||||
return [trialValue, nonce]
|
return [trialValue, nonce]
|
||||||
|
|
||||||
def estimate(difficulty, format = False):
|
|
||||||
|
def estimate(difficulty, format=False): # pylint: disable=redefined-builtin
|
||||||
|
"""
|
||||||
|
.. todo: fix unused variable
|
||||||
|
"""
|
||||||
ret = difficulty / 10
|
ret = difficulty / 10
|
||||||
if ret < 1:
|
if ret < 1:
|
||||||
ret = 1
|
ret = 1
|
||||||
|
|
||||||
if format:
|
if format:
|
||||||
|
# pylint: disable=unused-variable
|
||||||
out = str(int(ret)) + " seconds"
|
out = str(int(ret)) + " seconds"
|
||||||
if ret > 60:
|
if ret > 60:
|
||||||
ret /= 60
|
ret /= 60
|
||||||
|
@ -148,25 +174,46 @@ def estimate(difficulty, format = False):
|
||||||
if ret > 366:
|
if ret > 366:
|
||||||
ret /= 366
|
ret /= 366
|
||||||
out = str(int(ret)) + " years"
|
out = str(int(ret)) + " years"
|
||||||
else:
|
ret = None # Ensure legacy behaviour
|
||||||
return ret
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def getPowType():
|
def getPowType():
|
||||||
|
"""Get the proof of work implementation"""
|
||||||
|
|
||||||
if openclpow.openclEnabled():
|
if openclpow.openclEnabled():
|
||||||
return "OpenCL"
|
return "OpenCL"
|
||||||
if bmpow:
|
if bmpow:
|
||||||
return "C"
|
return "C"
|
||||||
return "python"
|
return "python"
|
||||||
|
|
||||||
|
|
||||||
def notifyBuild(tried=False):
|
def notifyBuild(tried=False):
|
||||||
|
"""Notify the user of the success or otherwise of building the PoW C module"""
|
||||||
|
|
||||||
if bmpow:
|
if bmpow:
|
||||||
queues.UISignalQueue.put(('updateStatusBar', (tr._translate("proofofwork", "C PoW module built successfully."), 1)))
|
queues.UISignalQueue.put(('updateStatusBar', (tr._translate(
|
||||||
|
"proofofwork", "C PoW module built successfully."), 1)))
|
||||||
elif tried:
|
elif tried:
|
||||||
queues.UISignalQueue.put(('updateStatusBar', (tr._translate("proofofwork", "Failed to build C PoW module. Please build it manually."), 1)))
|
queues.UISignalQueue.put(
|
||||||
|
(
|
||||||
|
'updateStatusBar', (
|
||||||
|
tr._translate(
|
||||||
|
"proofofwork",
|
||||||
|
"Failed to build C PoW module. Please build it manually."
|
||||||
|
),
|
||||||
|
1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
queues.UISignalQueue.put(('updateStatusBar', (tr._translate("proofofwork", "C PoW module unavailable. Please build it."), 1)))
|
queues.UISignalQueue.put(('updateStatusBar', (tr._translate(
|
||||||
|
"proofofwork", "C PoW module unavailable. Please build it."), 1)))
|
||||||
|
|
||||||
|
|
||||||
def buildCPoW():
|
def buildCPoW():
|
||||||
|
"""Attempt to build the PoW C module"""
|
||||||
if bmpow is not None:
|
if bmpow is not None:
|
||||||
return
|
return
|
||||||
if paths.frozen is not None:
|
if paths.frozen is not None:
|
||||||
|
@ -190,29 +237,27 @@ def buildCPoW():
|
||||||
except:
|
except:
|
||||||
notifyBuild(True)
|
notifyBuild(True)
|
||||||
|
|
||||||
|
|
||||||
def run(target, initialHash):
|
def run(target, initialHash):
|
||||||
|
"""Run the proof of work thread"""
|
||||||
|
|
||||||
if state.shutdown != 0:
|
if state.shutdown != 0:
|
||||||
raise
|
raise # pylint: disable=misplaced-bare-raise
|
||||||
target = int(target)
|
target = int(target)
|
||||||
if openclpow.openclEnabled():
|
if openclpow.openclEnabled():
|
||||||
# trialvalue1, nonce1 = _doGPUPoW(target, initialHash)
|
|
||||||
# trialvalue, nonce = _doFastPoW(target, initialHash)
|
|
||||||
# print "GPU: %s, %s" % (trialvalue1, nonce1)
|
|
||||||
# print "Fast: %s, %s" % (trialvalue, nonce)
|
|
||||||
# return [trialvalue, nonce]
|
|
||||||
try:
|
try:
|
||||||
return _doGPUPoW(target, initialHash)
|
return _doGPUPoW(target, initialHash)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
pass # fallback
|
pass # fallback
|
||||||
if bmpow:
|
if bmpow:
|
||||||
try:
|
try:
|
||||||
return _doCPoW(target, initialHash)
|
return _doCPoW(target, initialHash)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
pass # fallback
|
pass # fallback
|
||||||
if paths.frozen == "macosx_app" or not paths.frozen:
|
if paths.frozen == "macosx_app" or not paths.frozen:
|
||||||
# on my (Peter Surda) Windows 10, Windows Defender
|
# on my (Peter Surda) Windows 10, Windows Defender
|
||||||
# does not like this and fights with PyBitmessage
|
# does not like this and fights with PyBitmessage
|
||||||
|
@ -225,24 +270,30 @@ def run(target, initialHash):
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
logger.error("Fast PoW got exception:", exc_info=True)
|
logger.error("Fast PoW got exception:", exc_info=True)
|
||||||
pass #fallback
|
|
||||||
try:
|
try:
|
||||||
return _doSafePoW(target, initialHash)
|
return _doSafePoW(target, initialHash)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
pass #fallback
|
pass # fallback
|
||||||
|
|
||||||
|
|
||||||
def resetPoW():
|
def resetPoW():
|
||||||
|
"""Initialise the OpenCL PoW"""
|
||||||
openclpow.initCL()
|
openclpow.initCL()
|
||||||
|
|
||||||
|
|
||||||
# init
|
# init
|
||||||
|
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
global bitmsglib, bso, bmpow
|
"""Initialise PoW"""
|
||||||
|
# pylint: disable=global-statement
|
||||||
|
global bitmsglib, bmpow
|
||||||
|
|
||||||
openclpow.initCL()
|
openclpow.initCL()
|
||||||
|
|
||||||
if "win32" == sys.platform:
|
if sys.platform == "win32":
|
||||||
if ctypes.sizeof(ctypes.c_voidp) == 4:
|
if ctypes.sizeof(ctypes.c_voidp) == 4:
|
||||||
bitmsglib = 'bitmsghash32.dll'
|
bitmsglib = 'bitmsghash32.dll'
|
||||||
else:
|
else:
|
||||||
|
|
170
src/upnp.py
170
src/upnp.py
|
@ -1,21 +1,33 @@
|
||||||
# A simple upnp module to forward port for BitMessage
|
# pylint: disable=too-many-statements,too-many-branches,protected-access,no-self-use
|
||||||
# Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port
|
"""
|
||||||
|
src/upnp.py
|
||||||
|
===========
|
||||||
|
|
||||||
|
A simple upnp module to forward port for BitMessage
|
||||||
|
Reference: http://mattscodecave.com/posts/using-python-and-upnp-to-forward-a-port
|
||||||
|
"""
|
||||||
|
|
||||||
import httplib
|
import httplib
|
||||||
from random import randint
|
|
||||||
import socket
|
import socket
|
||||||
from struct import unpack, pack
|
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from bmconfigparser import BMConfigParser
|
import urllib2
|
||||||
from network.connectionpool import BMConnectionPool
|
from random import randint
|
||||||
from helper_threading import *
|
from urlparse import urlparse
|
||||||
|
from xml.dom.minidom import Document, parseString
|
||||||
|
|
||||||
import queues
|
import queues
|
||||||
import shared
|
import shared
|
||||||
import state
|
import state
|
||||||
import tr
|
import tr
|
||||||
|
from bmconfigparser import BMConfigParser
|
||||||
|
from debug import logger
|
||||||
|
from helper_threading import StoppableThread
|
||||||
|
from network.connectionpool import BMConnectionPool
|
||||||
|
|
||||||
|
|
||||||
def createRequestXML(service, action, arguments=None):
|
def createRequestXML(service, action, arguments=None):
|
||||||
from xml.dom.minidom import Document
|
"""Router UPnP requests are XML formatted"""
|
||||||
|
|
||||||
doc = Document()
|
doc = Document()
|
||||||
|
|
||||||
|
@ -63,11 +75,17 @@ def createRequestXML(service, action, arguments=None):
|
||||||
# our tree is ready, conver it to a string
|
# our tree is ready, conver it to a string
|
||||||
return doc.toxml()
|
return doc.toxml()
|
||||||
|
|
||||||
class UPnPError(Exception):
|
|
||||||
def __init__(self, message):
|
|
||||||
self.message
|
|
||||||
|
|
||||||
class Router:
|
class UPnPError(Exception):
|
||||||
|
"""Handle a UPnP error"""
|
||||||
|
|
||||||
|
def __init__(self, message):
|
||||||
|
super(UPnPError, self).__init__()
|
||||||
|
logger.error(message)
|
||||||
|
|
||||||
|
|
||||||
|
class Router: # pylint: disable=old-style-class
|
||||||
|
"""Encapulate routing"""
|
||||||
name = ""
|
name = ""
|
||||||
path = ""
|
path = ""
|
||||||
address = None
|
address = None
|
||||||
|
@ -75,10 +93,6 @@ class Router:
|
||||||
extPort = None
|
extPort = None
|
||||||
|
|
||||||
def __init__(self, ssdpResponse, address):
|
def __init__(self, ssdpResponse, address):
|
||||||
import urllib2
|
|
||||||
from xml.dom.minidom import parseString
|
|
||||||
from urlparse import urlparse
|
|
||||||
from debug import logger
|
|
||||||
|
|
||||||
self.address = address
|
self.address = address
|
||||||
|
|
||||||
|
@ -92,9 +106,9 @@ class Router:
|
||||||
try:
|
try:
|
||||||
self.routerPath = urlparse(header['location'])
|
self.routerPath = urlparse(header['location'])
|
||||||
if not self.routerPath or not hasattr(self.routerPath, "hostname"):
|
if not self.routerPath or not hasattr(self.routerPath, "hostname"):
|
||||||
logger.error ("UPnP: no hostname: %s", header['location'])
|
logger.error("UPnP: no hostname: %s", header['location'])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
logger.error ("UPnP: missing location header")
|
logger.error("UPnP: missing location header")
|
||||||
|
|
||||||
# get the profile xml file and read it into a variable
|
# get the profile xml file and read it into a variable
|
||||||
directory = urllib2.urlopen(header['location']).read()
|
directory = urllib2.urlopen(header['location']).read()
|
||||||
|
@ -108,45 +122,58 @@ class Router:
|
||||||
|
|
||||||
for service in service_types:
|
for service in service_types:
|
||||||
if service.childNodes[0].data.find('WANIPConnection') > 0 or \
|
if service.childNodes[0].data.find('WANIPConnection') > 0 or \
|
||||||
service.childNodes[0].data.find('WANPPPConnection') > 0:
|
service.childNodes[0].data.find('WANPPPConnection') > 0:
|
||||||
self.path = service.parentNode.getElementsByTagName('controlURL')[0].childNodes[0].data
|
self.path = service.parentNode.getElementsByTagName('controlURL')[0].childNodes[0].data
|
||||||
self.upnp_schema = service.childNodes[0].data.split(':')[-2]
|
self.upnp_schema = service.childNodes[0].data.split(':')[-2]
|
||||||
|
|
||||||
def AddPortMapping(self, externalPort, internalPort, internalClient, protocol, description, leaseDuration = 0, enabled = 1):
|
def AddPortMapping(
|
||||||
from debug import logger
|
self,
|
||||||
|
externalPort,
|
||||||
|
internalPort,
|
||||||
|
internalClient,
|
||||||
|
protocol,
|
||||||
|
description,
|
||||||
|
leaseDuration=0,
|
||||||
|
enabled=1,
|
||||||
|
): # pylint: disable=too-many-arguments
|
||||||
|
"""Add UPnP port mapping"""
|
||||||
|
|
||||||
resp = self.soapRequest(self.upnp_schema + ':1', 'AddPortMapping', [
|
resp = self.soapRequest(self.upnp_schema + ':1', 'AddPortMapping', [
|
||||||
('NewRemoteHost', ''),
|
('NewRemoteHost', ''),
|
||||||
('NewExternalPort', str(externalPort)),
|
('NewExternalPort', str(externalPort)),
|
||||||
('NewProtocol', protocol),
|
('NewProtocol', protocol),
|
||||||
('NewInternalPort', str(internalPort)),
|
('NewInternalPort', str(internalPort)),
|
||||||
('NewInternalClient', internalClient),
|
('NewInternalClient', internalClient),
|
||||||
('NewEnabled', str(enabled)),
|
('NewEnabled', str(enabled)),
|
||||||
('NewPortMappingDescription', str(description)),
|
('NewPortMappingDescription', str(description)),
|
||||||
('NewLeaseDuration', str(leaseDuration))
|
('NewLeaseDuration', str(leaseDuration))
|
||||||
])
|
])
|
||||||
self.extPort = externalPort
|
self.extPort = externalPort
|
||||||
logger.info("Successfully established UPnP mapping for %s:%i on external port %i", internalClient, internalPort, externalPort)
|
logger.info("Successfully established UPnP mapping for %s:%i on external port %i",
|
||||||
|
internalClient, internalPort, externalPort)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def DeletePortMapping(self, externalPort, protocol):
|
def DeletePortMapping(self, externalPort, protocol):
|
||||||
from debug import logger
|
"""Delete UPnP port mapping"""
|
||||||
|
|
||||||
resp = self.soapRequest(self.upnp_schema + ':1', 'DeletePortMapping', [
|
resp = self.soapRequest(self.upnp_schema + ':1', 'DeletePortMapping', [
|
||||||
('NewRemoteHost', ''),
|
('NewRemoteHost', ''),
|
||||||
('NewExternalPort', str(externalPort)),
|
('NewExternalPort', str(externalPort)),
|
||||||
('NewProtocol', protocol),
|
('NewProtocol', protocol),
|
||||||
])
|
])
|
||||||
logger.info("Removed UPnP mapping on external port %i", externalPort)
|
logger.info("Removed UPnP mapping on external port %i", externalPort)
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
def GetExternalIPAddress(self):
|
def GetExternalIPAddress(self):
|
||||||
from xml.dom.minidom import parseString
|
"""Get the external address"""
|
||||||
|
|
||||||
resp = self.soapRequest(self.upnp_schema + ':1', 'GetExternalIPAddress')
|
resp = self.soapRequest(self.upnp_schema + ':1', 'GetExternalIPAddress')
|
||||||
dom = parseString(resp)
|
dom = parseString(resp)
|
||||||
return dom.getElementsByTagName('NewExternalIPAddress')[0].childNodes[0].data
|
return dom.getElementsByTagName('NewExternalIPAddress')[0].childNodes[0].data
|
||||||
|
|
||||||
def soapRequest(self, service, action, arguments=None):
|
def soapRequest(self, service, action, arguments=None):
|
||||||
from xml.dom.minidom import parseString
|
"""Make a request to a router"""
|
||||||
from debug import logger
|
|
||||||
conn = httplib.HTTPConnection(self.routerPath.hostname, self.routerPath.port)
|
conn = httplib.HTTPConnection(self.routerPath.hostname, self.routerPath.port)
|
||||||
conn.request(
|
conn.request(
|
||||||
'POST',
|
'POST',
|
||||||
|
@ -155,8 +182,8 @@ class Router:
|
||||||
{
|
{
|
||||||
'SOAPAction': '"urn:schemas-upnp-org:service:%s#%s"' % (service, action),
|
'SOAPAction': '"urn:schemas-upnp-org:service:%s#%s"' % (service, action),
|
||||||
'Content-Type': 'text/xml'
|
'Content-Type': 'text/xml'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
resp = conn.getresponse()
|
resp = conn.getresponse()
|
||||||
conn.close()
|
conn.close()
|
||||||
if resp.status == 500:
|
if resp.status == 500:
|
||||||
|
@ -164,21 +191,24 @@ class Router:
|
||||||
try:
|
try:
|
||||||
dom = parseString(respData)
|
dom = parseString(respData)
|
||||||
errinfo = dom.getElementsByTagName('errorDescription')
|
errinfo = dom.getElementsByTagName('errorDescription')
|
||||||
if len(errinfo) > 0:
|
if errinfo:
|
||||||
logger.error("UPnP error: %s", respData)
|
logger.error("UPnP error: %s", respData)
|
||||||
raise UPnPError(errinfo[0].childNodes[0].data)
|
raise UPnPError(errinfo[0].childNodes[0].data)
|
||||||
except:
|
except:
|
||||||
raise UPnPError("Unable to parse SOAP error: %s" %(respData))
|
raise UPnPError("Unable to parse SOAP error: %s" % (respData))
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
class uPnPThread(threading.Thread, StoppableThread):
|
class uPnPThread(threading.Thread, StoppableThread):
|
||||||
|
"""Start a thread to handle UPnP activity"""
|
||||||
|
|
||||||
SSDP_ADDR = "239.255.255.250"
|
SSDP_ADDR = "239.255.255.250"
|
||||||
GOOGLE_DNS = "8.8.8.8"
|
GOOGLE_DNS = "8.8.8.8"
|
||||||
SSDP_PORT = 1900
|
SSDP_PORT = 1900
|
||||||
SSDP_MX = 2
|
SSDP_MX = 2
|
||||||
SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
|
SSDP_ST = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
|
||||||
|
|
||||||
def __init__ (self):
|
def __init__(self):
|
||||||
threading.Thread.__init__(self, name="uPnPThread")
|
threading.Thread.__init__(self, name="uPnPThread")
|
||||||
try:
|
try:
|
||||||
self.extPort = BMConfigParser().getint('bitmessagesettings', 'extport')
|
self.extPort = BMConfigParser().getint('bitmessagesettings', 'extport')
|
||||||
|
@ -194,7 +224,7 @@ class uPnPThread(threading.Thread, StoppableThread):
|
||||||
self.initStop()
|
self.initStop()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
from debug import logger
|
"""Start the thread to manage UPnP activity"""
|
||||||
|
|
||||||
logger.debug("Starting UPnP thread")
|
logger.debug("Starting UPnP thread")
|
||||||
logger.debug("Local IP: %s", self.localIP)
|
logger.debug("Local IP: %s", self.localIP)
|
||||||
|
@ -209,9 +239,11 @@ class uPnPThread(threading.Thread, StoppableThread):
|
||||||
if not bound:
|
if not bound:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
# pylint: disable=attribute-defined-outside-init
|
||||||
self.localPort = BMConfigParser().getint('bitmessagesettings', 'port')
|
self.localPort = BMConfigParser().getint('bitmessagesettings', 'port')
|
||||||
|
|
||||||
while state.shutdown == 0 and BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'):
|
while state.shutdown == 0 and BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'):
|
||||||
if time.time() - lastSent > self.sendSleep and len(self.routers) == 0:
|
if time.time() - lastSent > self.sendSleep and not self.routers:
|
||||||
try:
|
try:
|
||||||
self.sendSearchRouter()
|
self.sendSearchRouter()
|
||||||
except:
|
except:
|
||||||
|
@ -219,7 +251,7 @@ class uPnPThread(threading.Thread, StoppableThread):
|
||||||
lastSent = time.time()
|
lastSent = time.time()
|
||||||
try:
|
try:
|
||||||
while state.shutdown == 0 and BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'):
|
while state.shutdown == 0 and BMConfigParser().safeGetBoolean('bitmessagesettings', 'upnp'):
|
||||||
resp,(ip,port) = self.sock.recvfrom(1000)
|
resp, (ip, _) = self.sock.recvfrom(1000)
|
||||||
if resp is None:
|
if resp is None:
|
||||||
continue
|
continue
|
||||||
newRouter = Router(resp, ip)
|
newRouter = Router(resp, ip)
|
||||||
|
@ -230,14 +262,11 @@ class uPnPThread(threading.Thread, StoppableThread):
|
||||||
logger.debug("Found UPnP router at %s", ip)
|
logger.debug("Found UPnP router at %s", ip)
|
||||||
self.routers.append(newRouter)
|
self.routers.append(newRouter)
|
||||||
self.createPortMapping(newRouter)
|
self.createPortMapping(newRouter)
|
||||||
queues.UISignalQueue.put(('updateStatusBar', tr._translate("MainWindow",'UPnP port mapping established on port %1').arg(str(self.extPort))))
|
queues.UISignalQueue.put(('updateStatusBar', tr._translate(
|
||||||
# retry connections so that the submitted port is refreshed
|
"MainWindow", 'UPnP port mapping established on port %1'
|
||||||
with shared.alreadyAttemptedConnectionsListLock:
|
).arg(str(self.extPort))))
|
||||||
shared.alreadyAttemptedConnectionsList.clear()
|
|
||||||
shared.alreadyAttemptedConnectionsListResetTime = int(
|
|
||||||
time.time())
|
|
||||||
break
|
break
|
||||||
except socket.timeout as e:
|
except socket.timeout:
|
||||||
pass
|
pass
|
||||||
except:
|
except:
|
||||||
logger.error("Failure running UPnP router search.", exc_info=True)
|
logger.error("Failure running UPnP router search.", exc_info=True)
|
||||||
|
@ -259,22 +288,25 @@ class uPnPThread(threading.Thread, StoppableThread):
|
||||||
self.deletePortMapping(router)
|
self.deletePortMapping(router)
|
||||||
shared.extPort = None
|
shared.extPort = None
|
||||||
if deleted:
|
if deleted:
|
||||||
queues.UISignalQueue.put(('updateStatusBar', tr._translate("MainWindow",'UPnP port mapping removed')))
|
queues.UISignalQueue.put(('updateStatusBar', tr._translate("MainWindow", 'UPnP port mapping removed')))
|
||||||
logger.debug("UPnP thread done")
|
logger.debug("UPnP thread done")
|
||||||
|
|
||||||
def getLocalIP(self):
|
def getLocalIP(self):
|
||||||
|
"""Get the local IP of the node"""
|
||||||
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
s.connect((uPnPThread.GOOGLE_DNS, 1))
|
s.connect((uPnPThread.GOOGLE_DNS, 1))
|
||||||
return s.getsockname()[0]
|
return s.getsockname()[0]
|
||||||
|
|
||||||
def sendSearchRouter(self):
|
def sendSearchRouter(self):
|
||||||
from debug import logger
|
"""Querying for UPnP services"""
|
||||||
|
|
||||||
ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \
|
ssdpRequest = "M-SEARCH * HTTP/1.1\r\n" + \
|
||||||
"HOST: %s:%d\r\n" % (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT) + \
|
"HOST: %s:%d\r\n" % (uPnPThread.SSDP_ADDR, uPnPThread.SSDP_PORT) + \
|
||||||
"MAN: \"ssdp:discover\"\r\n" + \
|
"MAN: \"ssdp:discover\"\r\n" + \
|
||||||
"MX: %d\r\n" % (uPnPThread.SSDP_MX, ) + \
|
"MX: %d\r\n" % (uPnPThread.SSDP_MX, ) + \
|
||||||
"ST: %s\r\n" % (uPnPThread.SSDP_ST, ) + "\r\n"
|
"ST: %s\r\n" % (uPnPThread.SSDP_ST, ) + "\r\n"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.debug("Sending UPnP query")
|
logger.debug("Sending UPnP query")
|
||||||
|
@ -283,19 +315,23 @@ class uPnPThread(threading.Thread, StoppableThread):
|
||||||
logger.exception("UPnP send query failed")
|
logger.exception("UPnP send query failed")
|
||||||
|
|
||||||
def createPortMapping(self, router):
|
def createPortMapping(self, router):
|
||||||
from debug import logger
|
"""Add a port mapping"""
|
||||||
|
|
||||||
for i in range(50):
|
for i in range(50):
|
||||||
try:
|
try:
|
||||||
routerIP, = unpack('>I', socket.inet_aton(router.address))
|
|
||||||
localIP = self.localIP
|
localIP = self.localIP
|
||||||
if i == 0:
|
if i == 0:
|
||||||
extPort = self.localPort # try same port first
|
extPort = self.localPort # try same port first
|
||||||
elif i == 1 and self.extPort:
|
elif i == 1 and self.extPort:
|
||||||
extPort = self.extPort # try external port from last time next
|
extPort = self.extPort # try external port from last time next
|
||||||
else:
|
else:
|
||||||
extPort = randint(32767, 65535)
|
extPort = randint(32767, 65535)
|
||||||
logger.debug("Attempt %i, requesting UPnP mapping for %s:%i on external port %i", i, localIP, self.localPort, extPort)
|
logger.debug(
|
||||||
|
"Attempt %i, requesting UPnP mapping for %s:%i on external port %i",
|
||||||
|
i,
|
||||||
|
localIP,
|
||||||
|
self.localPort,
|
||||||
|
extPort)
|
||||||
router.AddPortMapping(extPort, self.localPort, localIP, 'TCP', 'BitMessage')
|
router.AddPortMapping(extPort, self.localPort, localIP, 'TCP', 'BitMessage')
|
||||||
shared.extPort = extPort
|
shared.extPort = extPort
|
||||||
self.extPort = extPort
|
self.extPort = extPort
|
||||||
|
@ -306,7 +342,5 @@ class uPnPThread(threading.Thread, StoppableThread):
|
||||||
logger.debug("UPnP error: ", exc_info=True)
|
logger.debug("UPnP error: ", exc_info=True)
|
||||||
|
|
||||||
def deletePortMapping(self, router):
|
def deletePortMapping(self, router):
|
||||||
|
"""Delete a port mapping"""
|
||||||
router.DeletePortMapping(router.extPort, 'TCP')
|
router.DeletePortMapping(router.extPort, 'TCP')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user