diff --git a/examples/11-query-any.php b/examples/11-query-any.php index 63fcbe87..fb6c3d73 100644 --- a/examples/11-query-any.php +++ b/examples/11-query-any.php @@ -43,6 +43,11 @@ $type = 'TXT'; $data = implode('', $data); break; + case Message::TYPE_MX: + // MX records contain "priority" and "target", only dump its values here + $type = 'MX'; + $data = implode(' ', $data); + break; default: // unknown type uses HEX format $type = 'Type ' . $answer->type; diff --git a/src/Model/Record.php b/src/Model/Record.php index f62ad539..c4f75dd5 100644 --- a/src/Model/Record.php +++ b/src/Model/Record.php @@ -48,6 +48,14 @@ class Record * suggests using key-value pairs such as `["name=test","version=1"]`, but * interpretation of this is not enforced and left up to consumers of this * library (used for DNS-SD/Zeroconf and others). + * - MX: + * Mail server priority (UINT16) and target hostname without trailing dot, + * for example `{"priority":10,"target":"mx.example.com"}`. + * The payload data uses an associative array with fixed keys "priority" + * (also commonly referred to as weight or preference) and "target" (also + * referred to as exchange). If a response message contains multiple + * records of this type, targets should be sorted by priority (lowest + * first) - this is left up to consumers of this library (used for SMTP). * - Any other unknown type: * An opaque binary string containing the RDATA as transported in the DNS * record. For forwards compatibility, you should not rely on this format @@ -56,7 +64,7 @@ class Record * considered a BC break. See the format definition of known types above * for more details. * - * @var string|string[] + * @var string|string[]|array */ public $data; diff --git a/src/Protocol/Parser.php b/src/Protocol/Parser.php index 49479db4..ed590ba6 100644 --- a/src/Protocol/Parser.php +++ b/src/Protocol/Parser.php @@ -177,6 +177,14 @@ public function parseAnswer(Message $message) $consumed += $len + 1; $remaining -= $len + 1; } + } elseif (Message::TYPE_MX === $type) { + list($priority) = array_values(unpack('n', substr($message->data, $consumed, 2))); + list($bodyLabels, $consumed) = $this->readLabels($message->data, $consumed + 2); + + $rdata = array( + 'priority' => $priority, + 'target' => implode('.', $bodyLabels) + ); } else { // unknown types simply parse rdata as an opaque binary string $rdata = substr($message->data, $consumed, $rdLength); diff --git a/tests/Protocol/ParserTest.php b/tests/Protocol/ParserTest.php index cbbae2ac..9839ee6a 100644 --- a/tests/Protocol/ParserTest.php +++ b/tests/Protocol/ParserTest.php @@ -306,6 +306,31 @@ public function testParseTXTResponseMultiple() $this->assertSame(array('hello', 'world'), $response->answers[0]->data); } + public function testParseMXResponse() + { + $data = ""; + $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io + $data .= "00 0f 00 01"; // answer: type MX, class IN + $data .= "00 01 51 80"; // answer: ttl 86400 + $data .= "00 09"; // answer: rdlength 9 + $data .= "00 0a 05 68 65 6c 6c 6f 00"; // answer: rdata priority 10: hello + + $data = $this->convertTcpDumpToBinary($data); + + $response = new Message(); + $response->header->set('anCount', 1); + $response->data = $data; + + $this->parser->parseAnswer($response); + + $this->assertCount(1, $response->answers); + $this->assertSame('igor.io', $response->answers[0]->name); + $this->assertSame(Message::TYPE_MX, $response->answers[0]->type); + $this->assertSame(Message::CLASS_IN, $response->answers[0]->class); + $this->assertSame(86400, $response->answers[0]->ttl); + $this->assertSame(array('priority' => 10, 'target' => 'hello'), $response->answers[0]->data); + } + public function testParseResponseWithTwoAnswers() { $data = "";