Skip to content
Merged
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ Note that if you defined `size`, these two fields will be ignored
* `verbose` enables the verbose mode, printing output to a stream (see `out`)
* `out` is the target stream of verbose mode. If you enable the verbose mode and do not provide
`out`, verbose output will be send to the `sys.stdout` stream. You may want to use a file here.
* `match` is a flag that, if set to True, will enable payload matching between a ping request
and reply (default behaviour follows that of Windows which counts a successful reply by a
matched packet identifier only; Linux behaviour counts a non equivalent payload with a matched
packet identifier in reply as fail, such as when pinging 8.8.8.8 with 1000 bytes and the reply
is truncated to only the first 74 of request payload with a matching packet identifier)

## FAQ
### Do I need privileged mode or root?
Expand All @@ -59,8 +64,10 @@ to create custom IP packets. Unfortunately, there is simply no other way to crea
If you wish to extend PythonPing, or integrate it in your application, we recommend to use the
classes that are part of Python Ping instead of the `ping` function. `executor.Communicator`
handles the communication with the target device, it takes care of sending ICMP requests and
processing responses. It ultimately produces the `executor.ResponseList` object. The `Communicator`
needs to know a target and which payloads to send to the remote device. For that, we have several
classes in the `payload_provider` module. You may want to create your own provider by extending
processing responses (note that for it to be thread safe you must then handle making a unique
seed ID for each thread instance, see ping.__init\__ for an example of this). It ultimately
produces the `executor.ResponseList` object. The `Communicator` needs to know a target and
which payloads to send to the remote device. For that, we have several classes in the
`payload_provider` module. You may want to create your own provider by extending
`payload_provider.PayloadProvider`. If you are interested in that, you should check the
documentation of both `executor` and `payload_provider` module.
5 changes: 2 additions & 3 deletions pythonping/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
from . import network, executor, payload_provider
from .utils import random_text
from random import randint
from typing import List


# this needs to be available across all thread usages
SEED_IDs: List[int] = []
# this needs to be available across all thread usages and will hold ints
SEED_IDs = []


def ping(target,
Expand Down
32 changes: 19 additions & 13 deletions pythonping/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,14 +252,17 @@ def send_ping(self, packet_id, sequence_number, payload):

:param packet_id: The ID to use for the packet
:type packet_id: int
:param sequence_number: The seuqnce number to use for the packet
:param sequence_number: The sequence number to use for the packet
:type sequence_number: int
:param payload: The payload of the ICMP message
:type payload: bytes"""
self.socket.send(icmp.ICMP(
:type payload: Union[str, bytes]
:rtype: bytes"""
i = icmp.ICMP(
icmp.Types.EchoRequest,
payload=payload,
identifier=packet_id, sequence_number=sequence_number).packet)
identifier=packet_id, sequence_number=sequence_number)
self.socket.send(i.packet)
return i.payload

def listen_for(self, packet_id, timeout, payload_pattern=None):
"""Listens for a packet of a given id for a given timeout
Expand All @@ -280,15 +283,18 @@ def listen_for(self, packet_id, timeout, payload_pattern=None):
# If we actually received something
if raw_packet != b'':
response.unpack(raw_packet)
# To allow Windows-like behaviour (no payload inspection, but only match packet identifiers),
# simply allow for it to be an always true in the legacy usage case
if payload_pattern is None:
payload_pattern = response.payload

# Ensure we have not unpacked the packet we sent (RHEL will also listen to outgoing packets)
if (response.id == packet_id and response.message_type != icmp.Types.EchoRequest.type_id
and response.payload == payload_pattern):
return Response(Message('', response, source_socket[0]), timeout - time_left)
if response.id == packet_id and response.message_type != icmp.Types.EchoRequest.type_id:
if payload_pattern is None:
# To allow Windows-like behaviour (no payload inspection, but only match packet identifiers),
# simply allow for it to be an always true in the legacy usage case
payload_matched = True
else:
payload_matched = (payload_pattern == response.payload)

if payload_matched:
return Response(Message('', response, source_socket[0]), timeout - time_left)
return Response(None, timeout)

@staticmethod
Expand All @@ -313,10 +319,10 @@ def run(self, match_payloads=False):
identifier = self.seed_id
seq = 1
for payload in self.provider:
self.send_ping(identifier, seq, payload)
payload_bytes_sent = self.send_ping(identifier, seq, payload)
if not match_payloads:
self.responses.append(self.listen_for(identifier, self.timeout))
else:
self.responses.append(self.listen_for(identifier, self.timeout, payload))
self.responses.append(self.listen_for(identifier, self.timeout, payload_bytes_sent))

seq = self.increase_seq(seq)
4 changes: 2 additions & 2 deletions pythonping/payload_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def __init__(self, pattern, count):
"""Creates a provider of many identical payloads

:param pattern: The existing payload
:type pattern: bytes
:type pattern: Union[str, bytes]
:param count: How many payloads to generate
:type count: int"""
self.pattern = pattern
Expand All @@ -61,7 +61,7 @@ def __init__(self, pattern, start_size, end_size):
"""Creates a provider of payloads of increasing size

:param pattern: The existing payload, may be cut or replicated to fit the size
:type pattern: bytes
:type pattern: Union[str, bytes]
:param start_size: The first payload size to start with, included
:type start_size: int
:param end_size: The payload size to end with, included
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
long_description = file.read()

setup(name='pythonping',
version='1.0.10',
version='1.0.12',
description='A simple way to ping in Python',
url='https://github.com/alessandromaggio/pythonping',
author='Alessandro Maggio',
Expand Down