Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Fix network.py Intermittent Threaded Usage Failures due to Python Built-In Bug #56

Merged
merged 3 commits into from
Sep 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pythonping/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ def __init__(self, target, payload_provider, timeout, socket_options=(), seed_id
self.timeout = timeout
self.responses = ResponseList(verbose=verbose, output=output)
self.seed_id = seed_id
# note that to make Communicator instances thread safe, the seed ID must be unique per thread
if self.seed_id is None:
self.seed_id = os.getpid() & 0xFFFF

Expand Down
19 changes: 18 additions & 1 deletion pythonping/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

class Socket:
DONT_FRAGMENT = (socket.SOL_IP, 10, 1) # Option value for raw socket
PROTO_LOOKUP = {"icmp": socket.IPPROTO_ICMP, "tcp": socket.IPPROTO_TCP, "udp": socket.IPPROTO_UDP,
"ip": socket.IPPROTO_IP, "raw": socket.IPPROTO_RAW}

def __init__(self, destination, protocol, source=None, options=(), buffer_size=2048):
"""Creates a network socket to exchange messages
Expand All @@ -23,14 +25,29 @@ def __init__(self, destination, protocol, source=None, options=(), buffer_size=2
self.destination = socket.gethostbyname(destination)
except socket.gaierror as e:
raise RuntimeError('Cannot resolve address "' + destination + '", try verify your DNS or host file')
self.protocol = socket.getprotobyname(protocol)

self.protocol = Socket.getprotobyname(protocol)
self.buffer_size = buffer_size
if source is not None:
raise NotImplementedError('PythonPing currently does not support specification of source IP')
self.socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, self.protocol)
if options:
self.socket.setsockopt(*options)

# Implementing a version of socket.getprotobyname for this library since built-in is not thread safe
# for python 3.5, 3.6, and 3.7:
# https://bugs.python.org/issue30482
# This bug was causing failures as it would occasionally return a 0 (incorrect) instead of a 1 (correct)
# for the 'icmp' string, causing a OSError for "Protocol not supported" in multi-threaded usage:
# https://github.com/alessandromaggio/pythonping/issues/40
@staticmethod
def getprotobyname(name):
try:
return Socket.PROTO_LOOKUP[name.lower()]
except KeyError:
raise KeyError("'" + str(name) + "' is not in the list of supported proto types: "
+ str(list(Socket.PROTO_LOOKUP.keys())))

def send(self, packet):
"""Sends a raw packet on the stream

Expand Down