Skip to content

Commit

Permalink
Docstrings in network from #1401
Browse files Browse the repository at this point in the history
  • Loading branch information
g1itch committed Jul 26, 2019
1 parent 19ab56a commit 2998599
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/network/dandelion.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

@Singleton
class Dandelion():
"""Dandelion class for tracking stem/fluff stages."""
def __init__(self):
# currently assignable child stems
self.stem = []
Expand All @@ -35,13 +36,15 @@ def __init__(self):
self.lock = RLock()

def poissonTimeout(self, start=None, average=0):
"""Generate deadline using Poisson distribution"""
if start is None:
start = time()
if average == 0:
average = FLUFF_TRIGGER_MEAN_DELAY
return start + expovariate(1.0 / average) + FLUFF_TRIGGER_FIXED_DELAY

def addHash(self, hashId, source=None, stream=1):
"""Add inventory vector to dandelion stem"""
if not state.dandelion:
return
with self.lock:
Expand All @@ -51,6 +54,10 @@ def addHash(self, hashId, source=None, stream=1):
self.poissonTimeout())

def setHashStream(self, hashId, stream=1):
"""
Update stream for inventory vector (as inv/dinv commands don't
include streams, we only learn this after receiving the object)
"""
with self.lock:
if hashId in self.hashMap:
self.hashMap[hashId] = Stem(
Expand All @@ -59,6 +66,7 @@ def setHashStream(self, hashId, stream=1):
self.poissonTimeout())

def removeHash(self, hashId, reason="no reason specified"):
"""Switch inventory vector from stem to fluff mode"""
logging.debug(
"%s entering fluff mode due to %s.",
''.join('%02x' % ord(i) for i in hashId), reason)
Expand All @@ -69,12 +77,19 @@ def removeHash(self, hashId, reason="no reason specified"):
pass

def hasHash(self, hashId):
"""Is inventory vector in stem mode?"""
return hashId in self.hashMap

def objectChildStem(self, hashId):
"""Child (i.e. next) node for an inventory vector during stem mode"""
return self.hashMap[hashId].child

def maybeAddStem(self, connection):
"""
If we had too few outbound connections, add the current one to the
current stem list. Dandelion as designed by the authors should
always have two active stem child connections.
"""
# fewer than MAX_STEMS outbound connections at last reshuffle?
with self.lock:
if len(self.stem) < MAX_STEMS:
Expand All @@ -90,6 +105,10 @@ def maybeAddStem(self, connection):
invQueue.put((v.stream, k, v.child))

def maybeRemoveStem(self, connection):
"""
Remove current connection from the stem list (called e.g. when
a connection is closed).
"""
# is the stem active?
with self.lock:
if connection in self.stem:
Expand All @@ -107,6 +126,10 @@ def maybeRemoveStem(self, connection):
None, v.stream, self.poissonTimeout())

def pickStem(self, parent=None):
"""
Pick a random active stem, but not the parent one
(the one where an object came from)
"""
try:
# pick a random from available stems
stem = choice(range(len(self.stem)))
Expand All @@ -123,6 +146,10 @@ def pickStem(self, parent=None):
return None

def getNodeStem(self, node=None):
"""
Return child stem node for a given parent stem node
(the mapping is static for about 10 minutes, then it reshuffles)
"""
with self.lock:
try:
return self.nodeMap[node]
Expand All @@ -131,6 +158,7 @@ def getNodeStem(self, node=None):
return self.nodeMap[node]

def expire(self):
"""Switch expired objects from stem to fluff mode"""
with self.lock:
deadline = time()
toDelete = [
Expand All @@ -144,6 +172,7 @@ def expire(self):
return toDelete

def reRandomiseStems(self):
"""Re-shuffle stem mapping (parent <-> child pairs)"""
with self.lock:
try:
# random two connections
Expand Down
17 changes: 17 additions & 0 deletions src/network/socks4a.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@


class Socks4aError(ProxyError):
"""SOCKS4a error base class"""
errorCodes = (
"Request granted",
"Request rejected or failed",
Expand All @@ -17,16 +18,19 @@ class Socks4aError(ProxyError):


class Socks4a(Proxy):
"""SOCKS4a proxy class"""
def __init__(self, address=None):
Proxy.__init__(self, address)
self.ipaddr = None
self.destport = address[1]

def state_init(self):
"""Protocol initialisation (before connection is established)"""
self.set_state("auth_done", 0)
return True

def state_pre_connect(self):
"""Handle feedback from SOCKS4a while it is connecting on our behalf"""
# Get the response
if self.read_buf[0:1] != chr(0x00).encode():
# bad data
Expand All @@ -53,14 +57,20 @@ def state_pre_connect(self):
return True

def proxy_sock_name(self):
"""
Handle return value when using SOCKS4a for DNS resolving
instead of connecting.
"""
return socket.inet_ntoa(self.__proxysockname[0])


class Socks4aConnection(Socks4a):
"""Child SOCKS4a class used for making outbound connections."""
def __init__(self, address):
Socks4a.__init__(self, address=address)

def state_auth_done(self):
"""Request connection to be made"""
# Now we can request the actual connection
rmtrslv = False
self.append_write_buf(
Expand Down Expand Up @@ -92,6 +102,7 @@ def state_auth_done(self):
return True

def state_pre_connect(self):
"""Tell SOCKS4a to initiate a connection"""
try:
return Socks4a.state_pre_connect(self)
except Socks4aError as e:
Expand All @@ -100,6 +111,7 @@ def state_pre_connect(self):


class Socks4aResolver(Socks4a):
"""DNS resolver class using SOCKS4a"""
def __init__(self, host):
self.host = host
self.port = 8444
Expand All @@ -118,4 +130,9 @@ def state_auth_done(self):
return True

def resolved(self):
"""
Resolving is done, process the return value. To use this within
PyBitmessage, a callback needs to be implemented which hasn't
been done yet.
"""
print "Resolved %s as %s" % (self.host, self.proxy_sock_name())

0 comments on commit 2998599

Please sign in to comment.