1
0
mirror of https://github.com/danog/dns.git synced 2025-01-22 21:41:11 +01:00

Ignore malformed UDP packets

Relates to #102.
This commit is contained in:
Niklas Keller 2023-01-10 23:29:50 +01:00
parent 70b29f2bfe
commit a8c9cee1c3
No known key found for this signature in database
GPG Key ID: AFA536ABA90C76A6
3 changed files with 51 additions and 10 deletions

View File

@ -28,6 +28,8 @@ abstract class Socket
private const MAX_CONCURRENT_REQUESTS = 500; private const MAX_CONCURRENT_REQUESTS = 500;
protected int $invalidPacketsReceived = 0;
abstract public static function connect(string $uri): self; abstract public static function connect(string $uri): self;
private readonly ReadableResourceStream $input; private readonly ReadableResourceStream $input;
@ -159,8 +161,10 @@ abstract class Socket
/** @var DeferredFuture<\Closure():Message> $deferred */ /** @var DeferredFuture<\Closure():Message> $deferred */
$deferred = new DeferredFuture; $deferred = new DeferredFuture;
$invalidPacketsReceived = &$this->invalidPacketsReceived;
/** @psalm-suppress InaccessibleProperty $this->pending is an ArrayObject */ /** @psalm-suppress InaccessibleProperty $this->pending is an ArrayObject */
$this->pending[$id] = new class($this->pending, $id, $deferred, $question, $timeout) { $this->pending[$id] = new class($this->pending, $id, $deferred, $question, $timeout, $invalidPacketsReceived) {
private readonly string $callbackId; private readonly string $callbackId;
public ?DeferredFuture $deferred; public ?DeferredFuture $deferred;
@ -171,15 +175,23 @@ abstract class Socket
DeferredFuture $deferred, DeferredFuture $deferred,
public readonly Question $question, public readonly Question $question,
float $timeout, float $timeout,
int &$invalidPacketsReceived
) { ) {
$this->deferred = $deferred; $this->deferred = $deferred;
$this->callbackId = EventLoop::unreference(EventLoop::delay( $this->callbackId = EventLoop::unreference(EventLoop::delay(
$timeout, $timeout,
weakClosure(function () use ($id, $pending, $timeout): void { weakClosure(function () use ($id, $pending, $timeout, &$invalidPacketsReceived): void {
$this->deferred?->complete(static fn () => throw new DnsTimeoutException( if ($invalidPacketsReceived > 0) {
"Didn't receive a response within {$timeout} seconds." $this->deferred?->complete(static fn () => throw new DnsTimeoutException(
)); "Didn't receive a response within {$timeout} seconds, but received {$invalidPacketsReceived} invalid packets on this socket"
));
} else {
$this->deferred?->complete(static fn () => throw new DnsTimeoutException(
"Didn't receive a response within {$timeout} seconds."
));
}
$this->deferred = null; $this->deferred = null;
unset($pending[$id]); unset($pending[$id]);

View File

@ -47,12 +47,20 @@ final class UdpSocket extends Socket
protected function receive(): Message protected function receive(): Message
{ {
$data = $this->read(); while (true) {
$data = $this->read();
if ($data === null) { if ($data === null) {
throw new DnsException("Reading from the server failed"); throw new DnsException("Reading from the server failed");
}
try {
return $this->decoder->decode($data);
} catch (\Exception) {
$this->invalidPacketsReceived++;
continue;
}
} }
return $this->decoder->decode($data);
} }
} }

View File

@ -3,6 +3,8 @@
namespace Amp\Dns\Test; namespace Amp\Dns\Test;
use Amp\Dns; use Amp\Dns;
use LibDNS\Records\QuestionFactory;
use Revolt\EventLoop;
class UdpSocketTest extends SocketTest class UdpSocketTest extends SocketTest
{ {
@ -12,6 +14,25 @@ class UdpSocketTest extends SocketTest
Dns\Internal\UdpSocket::connect("udp://8.8.8.8"); Dns\Internal\UdpSocket::connect("udp://8.8.8.8");
} }
public function testMalformedResponse(): void
{
$server = \stream_socket_server('udp://127.0.0.1:0', $errno, $errstr, \STREAM_SERVER_BIND);
EventLoop::onReadable($server, function () use ($server) {
\stream_socket_recvfrom($server, 512, 0, $addr);
\stream_socket_sendto($server, 'invalid', 0, $addr);
});
$question = (new QuestionFactory)->create(Dns\DnsRecord::A);
$question->setName("google.com");
$socket = Dns\Internal\UdpSocket::connect('udp://' . \stream_socket_get_name($server, false));
$this->expectException(Dns\DnsTimeoutException::class);
$this->expectErrorMessage("Didn't receive a response within 1 seconds, but received 1 invalid packets on this socket");
$socket->ask($question, 1);
}
protected function connect(): Dns\Internal\Socket protected function connect(): Dns\Internal\Socket
{ {
return Dns\Internal\UdpSocket::connect("udp://8.8.8.8:53"); return Dns\Internal\UdpSocket::connect("udp://8.8.8.8:53");