angelcamposm / ping

A Ping package for Laravel
MIT License
55 stars 10 forks source link

Parser fails if host unreachable #10

Closed hendrikbl closed 3 years ago

hendrikbl commented 3 years ago

Describe the bug If the host is unreachable, the round trip times are missing from the output. This results in invalid array keys when parsing the output.

To Reproduce Steps to reproduce the behavior:

  1. Run a PingCommand to an unreachable host.

Expected behavior Ping object with host_status unreachable.

Possible Solution It's kinda messy, but here's my one minute attempt to fix it:

    public function __construct(array $ping)
    {
        parent::__construct($ping);

        $stats = array_slice($ping, (array_search( "", array_slice($ping, 1)) + 2) );

        $this->setRoundTripTime(array_key_exists(4, $stats) ? $stats[4] : "");
        $this->setSequence();
        $this->setStatistics($stats[1]);
        $this->setHostStatus();
    }
private function parseRoundTripTime(string $row): array
    {
        if ($row === "") {
            return [];
        };

       ...
   }

Desktop (please complete the following information):

angelcamposm commented 3 years ago

i will check this week, thanks 👏

hendrikbl commented 3 years ago

Just noticed the sequence is also broken in that case. My quickfix is as follows:

private function getSequence(): array
{
   $ping = $this->raw;
   $sequence = array_slice($ping, 2, (array_search("", array_slice($ping, 1))-1));
   return $sequence;
 }

Another thing i noticed is, if the hosts is unreachable, windows says all packages were sent and received wich results in a status Ok. To prevent this from happening, I check if round_trip_time is set. With the changes i made early in this issue, this leads to an accurate result as far as I can tell:

private function getHostStatus(): string
{
    return (($this->statistics['packet_loss'] < 100) && count($this->round_trip_time) != 0) ? 'Ok' : 'Unreachable';
}
angelcamposm commented 3 years ago

Hi @hendrikbl, today I have reviewed the code, I have implemented a function that verifies the last element of the $ping array and searches for the word "100% perdidos" in spanish or "100% lost" in english.

    /**
     * Check if the last element of the array has 100% value string.
     *
     * @param array $ping
     * @return bool
     */
    private function isUnreachable(array $ping): bool
    {
        $needles = 'perdidos|lost';

        $result = $ping[count($ping) - 1];

        $unreachable = false;

        foreach (explode('|', $needles) as $needle) {

            $search = strpos($result, '100% '.$needle);

            if ($search !== false) {
                $unreachable = true;
                break;
            }
        }

        return $unreachable;
    }

Then I have modified the constructor of the class, because if the host is unreachable, there are no round-trip time statistics or packet sequence.

Also, the packet statistics when the host is unreachable are not in the same position either so I have also corrected that.

    /**
     * PingParserForWindows constructor.
     *
     * @param array $ping
     */
    public function __construct(array $ping)
    {
        parent::__construct($ping);

        $this->unreachable = self::isUnreachable($ping);

        if ($this->unreachable) {
            $this->setStatistics($ping[count($ping) - 2]);
        } else {
            $this->setRoundTripTime($ping[count($ping) - 1]);
            $this->setSequence();
            $this->setStatistics($ping[count($ping) - 4]);
        }

        $this->setHostStatus();
    }

In the future, if you want to add a language other than Spanish or English to the parser, you simply add the value of the word "lost" in the language to be added.

hendrikbl commented 3 years ago

Nice, I will test this once it's available. Even though I think relying on language specific words for parsing isn't the best way to go.

Another thing i noticed is, if the hosts is unreachable, windows says all packages were sent and received wich results in a status Ok. To prevent this from happening, I check if round_trip_time is set. With the changes i made early in this issue, this leads to an accurate result as far as I can tell:

What I meant with the above is the following: ping

In both cases the host is unreachable but the second one doesn't say 100% loss. To me that's kinda stupid but it's because the pinging device itself is replying.

Two solutions come to my mind:

  1. parsing the actual replies (way too difficult and language dependent)
  2. check if round-trip times exists since thats the only difference in the statistics block compared to a successfull ping
angelcamposm commented 3 years ago

Hi @hendrikbl , if you download the PingParserForWindows.php file located in Parsers directory, you can test if it works correctly before preparing the release.

it's kinda strange the second response, i've tested in my computer with Windows 10 version. 20H2 and i receive always the same response

Note: The IP address, unless it is a public IP address, does not need to be masked since these types of IP addresses are not routable to the internet. ;)

hendrikbl commented 3 years ago

Hi @hendrikbl , if you download the PingParserForWindows.php file located in Parsers directory, you can test if it works correctly before preparing the release.

I will test it this week and see if it works 👍

it's kinda strange the second response, i've tested in my computer with Windows 10 version. 20H2 and i receive always the same response

Here's a nice answer from Stackexchange:

So what happens when you power cycle a machine and ping it?

At first you likely already have a valid ARP table entry. Your machine puts the packets on the wire but they never get delivered because the network stack on the destination is down. You get "Request timed out" (with microsoft ping, linux ping doesn't display the lack of response at all).

Later the ARP cache entry times out. The hop before the box you power cycled (may be your client box or a router) tries to do a new ARP lookup but fails, so it bounces the packets with ICMP errors. Often you see multiple packets get queued up and bounced at the same time.

Finally the network stack on the target box comes back up. ARP succeeds and the packets are sent to the target which replies to them.