ValentinBELYN / icmplib

Easily forge ICMP packets and make your own ping and traceroute.
GNU Lesser General Public License v3.0
266 stars 46 forks source link

callback for multiping, #42

Closed gato-gto closed 2 months ago

gato-gto commented 2 years ago

I propose to implement a callback for the 'multiping' functions, to perform some functions after receiving a response from the host

Using your library as an example

import asyncio

from datetime import datetime
from icmplib import Host, ICMPLibError, ICMPRequest, AsyncSocket, ICMPv4Socket, ICMPv6Socket
from icmplib.utils import unique_identifier, is_ipv6_address, async_resolve, is_hostname

from test_funcs import getHosts

async def async_ping(address, count=4, interval=1, timeout=2, id=None,
                     source=None, family=None, privileged=True, callback_func=None, **kwargs):
    if is_hostname(address):
        address = (await async_resolve(address, family))[0]

    if is_ipv6_address(address):
        _Socket = ICMPv6Socket
    else:
        _Socket = ICMPv4Socket

    id = id or unique_identifier()
    packets_sent = 0
    rtts = []

    with AsyncSocket(_Socket(source, privileged)) as sock:
        for sequence in range(count):
            if sequence > 0:
                await asyncio.sleep(interval)

            request = ICMPRequest(
                destination=address,
                id=id,
                sequence=sequence,
                **kwargs)

            try:
                sock.send(request)
                packets_sent += 1

                reply = await sock.receive(request, timeout)
                reply.raise_for_status()

                rtt = (reply.time - request.time) * 1000
                rtts.append(rtt)

            except ICMPLibError:
                pass
    if callback_func:
        return await callback_func(Host(address, packets_sent, rtts))

    return Host(address, packets_sent, rtts)

async def async_multiping(addresses, count=2, interval=0.5, timeout=2,
                          concurrent_tasks=50, source=None, family=None, privileged=True,
                          callback_func=None, **kwargs):
    loop = asyncio.get_running_loop()
    tasks = []
    tasks_pending = set()

    for address in addresses:
        if len(tasks_pending) >= concurrent_tasks:
            _, tasks_pending = await asyncio.wait(
                tasks_pending,
                return_when=asyncio.FIRST_COMPLETED)

        task = loop.create_task(
            async_ping(
                address=address,
                count=count,
                interval=interval,
                timeout=timeout,
                source=source,
                family=family,
                privileged=privileged,
                callback_func=callback_func,
                **kwargs)
        )

        tasks.append(task)
        tasks_pending.add(task)

    await asyncio.wait(tasks_pending)

    return [task.result() for task in tasks]

async def callback(host):
    print(host.address)
    # send_me(host) example func
    return host

async def test():
    startTime = datetime.now()

    hosts = await getHosts()

    hostsDown = []
    hostsUp = []

    for result in await async_multiping(
            hosts, count=1, timeout=1, interval=0.4, privileged=False, payload_size=8, callback_func=callback
    ):
        if result.is_alive:
            hostsUp.append(result)
        else:
            hostsDown.append(result)

    exTime = (datetime.now() - startTime).total_seconds()

    print(' -', len(hostsUp), 'hosts up out of', len(hosts))

    print(' -', 'execution speed', exTime, 'secs')

asyncio.run(test())

Output

172.20.3.12
172.20.3.198
172.20.3.97
172.20.4.136
172.20.4.184
 - 1160 hosts up out of 1173
 - execution speed 1.371824 secs
ValentinBELYN commented 2 years ago

Hi @gato-gto,

Thank you for the suggestion. The multiping function was not designed for this purpose but the idea is interesting.

I will add it in a next update.

ValentinBELYN commented 2 months ago

Sorry, ultimately this won't be implemented in icmplib.

I think it's cleaner to use the sockets provided by icmplib directly to achieve this goal.