Passed
Push — master ( d7fd0c...0cd2cd )
by Sam
04:33
created

ServerTest   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 111
dl 0
loc 221
rs 10
c 0
b 0
f 0
wmc 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A mockQueryAndResponse() 0 15 1
A testNsAdditionalRecords() 0 23 1
A testStatusQueryWithNoQuestionsResolves() 0 13 1
A testOnMessage() 0 6 1
A setUp() 0 17 1
A testSrvAdditionalRecords() 0 24 1
A testMxAdditionalRecords() 0 23 1
A testHandleQueryFromStream() 0 5 1
A testOptType() 0 27 1
A encodeQuery() 0 8 1
1
<?php
2
3
/*
4
 * This file is part of PHP DNS Server.
5
 *
6
 * (c) Yif Swery <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace yswery\DNS\Tests;
13
14
use Symfony\Component\EventDispatcher\EventDispatcher;
15
use yswery\DNS\ClassEnum;
16
use yswery\DNS\Decoder;
17
use yswery\DNS\Header;
18
use yswery\DNS\Message;
19
use yswery\DNS\RecordTypeEnum;
20
use yswery\DNS\Encoder;
21
use yswery\DNS\Resolver\StackableResolver;
22
use yswery\DNS\Resolver\XmlResolver;
23
use yswery\DNS\ResourceRecord;
24
use yswery\DNS\Server;
25
use yswery\DNS\Resolver\JsonResolver;
26
use PHPUnit\Framework\TestCase;
27
28
class ServerTest extends TestCase
29
{
30
    /**
31
     * @var Server
32
     */
33
    private $server;
34
35
    /**
36
     * @throws \Exception
37
     */
38
    public function setUp()
39
    {
40
        $xmlResolver = new XmlResolver([
41
            __DIR__.'/Resources/test.com.xml',
42
        ]);
43
44
        $jsonResolver = new JsonResolver([
45
            __DIR__.'/Resources/test_records.json',
46
            __DIR__.'/Resources/example.com.json',
47
        ]);
48
49
        $resolver = new StackableResolver([
50
            $jsonResolver,
51
            $xmlResolver,
52
        ]);
53
54
        $this->server = new Server($resolver, new EventDispatcher());
55
    }
56
57
    /**
58
     * @param $name
59
     * @param $type
60
     * @param $id
61
     *
62
     * @return array
63
     */
64
    private function encodeQuery($name, $type, $id)
65
    {
66
        $qname = Encoder::encodeDomainName($name);
67
        $flags = 0b0000000000000000;
68
        $header = pack('nnnnnn', $id, $flags, 1, 0, 0, 0);
69
        $question = $qname.pack('nn', $type, 1);
70
71
        return [$header, $question];
72
    }
73
74
    /**
75
     * Create a mock query and response pair.
76
     *
77
     * @return array
78
     */
79
    private function mockQueryAndResponse(): array
80
    {
81
        list($queryHeader, $question) = $this->encodeQuery($name = 'test.com.', RecordTypeEnum::TYPE_A, $id = 1337);
82
        $query = $queryHeader.$question;
83
84
        $flags = 0b1000010000000000;
85
        $qname = Encoder::encodeDomainName($name);
86
        $header = pack('nnnnnn', $id, $flags, 1, 1, 0, 0);
87
88
        $rdata = inet_pton('111.111.111.111');
89
        $answer = $qname.pack('nnNn', 1, 1, 300, strlen($rdata)).$rdata;
90
91
        $response = $header.$question.$answer;
92
93
        return [$query, $response];
94
    }
95
96
    /**
97
     * @throws \yswery\DNS\UnsupportedTypeException
98
     */
99
    public function testHandleQueryFromStream()
100
    {
101
        list($query, $response) = $this->mockQueryAndResponse();
102
103
        $this->assertEquals($response, $this->server->handleQueryFromStream($query));
104
    }
105
106
    public function testStatusQueryWithNoQuestionsResolves()
107
    {
108
        $message = new Message();
109
        $message->getHeader()
110
            ->setOpcode(Header::OPCODE_STATUS_REQUEST)
111
            ->setId(1234);
112
113
        $encodedMessage = Encoder::encodeMessage($message);
114
115
        $message->getHeader()->setResponse(true);
116
        $expectation = Encoder::encodeMessage($message);
117
118
        $this->assertEquals($expectation, $this->server->handleQueryFromStream($encodedMessage));
119
    }
120
121
    /**
122
     * Tests that the server sends back a "Not implemented" RCODE for a type that has not been implemented, namely "OPT".
123
     *
124
     * @throws \yswery\DNS\UnsupportedTypeException | \Exception
125
     */
126
    public function testOptType()
127
    {
128
        $q_RR = (new ResourceRecord())
129
            ->setName('test.com.')
130
            ->setType(RecordTypeEnum::TYPE_OPT)
131
            ->setClass(ClassEnum::INTERNET)
132
            ->setQuestion(true);
133
134
        $query = new Message();
135
        $query->setQuestions([$q_RR])
136
            ->getHeader()
137
                ->setQuery(true)
138
                ->setId($id = 1337);
139
140
        $response = new Message();
141
        $response->setQuestions([$q_RR])
142
            ->getHeader()
143
                ->setId($id)
144
                ->setResponse(true)
145
                ->setRcode(Header::RCODE_NOT_IMPLEMENTED)
146
                ->setAuthoritative(true);
147
148
        $queryEncoded = Encoder::encodeMessage($query);
149
        $responseEncoded = Encoder::encodeMessage($response);
150
151
        $server = new Server(new DummyResolver(), new EventDispatcher());
152
        $this->assertEquals($responseEncoded, $server->handleQueryFromStream($queryEncoded));
153
    }
154
155
    public function testOnMessage()
156
    {
157
        list($query, $response) = $this->mockQueryAndResponse();
158
        $this->server->onMessage($query, '127.0.0.1', $socket = new MockSocket());
159
160
        $this->assertEquals($response, $socket->getLastTransmission());
161
    }
162
163
    /**
164
     * Certain queries such as SRV, SOA, and NS records SHOULD return additional records in order to prevent
165
     * unnecessary additional requests.
166
     *
167
     * @throws \yswery\DNS\UnsupportedTypeException
168
     */
169
    public function testSrvAdditionalRecords()
170
    {
171
        $queryHeader = (new Header())
172
            ->setQuery(true)
173
            ->setOpcode(Header::OPCODE_STANDARD_QUERY)
174
            ->setId(1234);
175
176
        $queryRecord = (new ResourceRecord())
177
            ->setQuestion(true)
178
            ->setName('_ldap._tcp.example.com.')
179
            ->setType(RecordTypeEnum::TYPE_SRV);
180
181
        $message = (new Message())
182
            ->setHeader($queryHeader)
183
            ->addQuestion($queryRecord);
184
185
        $query = Encoder::encodeMessage($message);
186
        $this->server->onMessage($query, '127.0.0.1', $socket = new MockSocket());
187
        $encodedResponse = $socket->getLastTransmission();
188
        $response = Decoder::decodeMessage($encodedResponse);
189
190
        $this->assertEquals(1, $response->getHeader()->getAnswerCount());
191
        $this->assertEquals(1, $response->getHeader()->getAdditionalRecordsCount());
192
        $this->assertEquals('192.168.3.89', $response->getAdditionals()[0]->getRdata());
193
    }
194
195
    /**
196
     * @throws \yswery\DNS\UnsupportedTypeException
197
     */
198
    public function testMxAdditionalRecords()
199
    {
200
        $queryHeader = (new Header())
201
            ->setQuery(true)
202
            ->setOpcode(Header::OPCODE_STANDARD_QUERY)
203
            ->setId(1234);
204
205
        $mxQuestion = (new ResourceRecord())
206
            ->setQuestion(true)
207
            ->setType(RecordTypeEnum::TYPE_MX)
208
            ->setName('example.com.');
209
210
        $message = (new Message())
211
            ->setHeader($queryHeader)
212
            ->addQuestion($mxQuestion);
213
214
        $query = Encoder::encodeMessage($message);
215
        $this->server->onMessage($query, '127.0.0.1', $socket = new MockSocket());
216
        $encodedResponse = $socket->getLastTransmission();
217
        $response = Decoder::decodeMessage($encodedResponse);
218
219
        $this->assertEquals(2, $response->getHeader()->getAnswerCount());
220
        $this->assertEquals(2, $response->getHeader()->getAdditionalRecordsCount());
221
    }
222
223
    /**
224
     * @throws \yswery\DNS\UnsupportedTypeException
225
     */
226
    public function testNsAdditionalRecords()
227
    {
228
        $queryHeader = (new Header())
229
            ->setQuery(true)
230
            ->setOpcode(Header::OPCODE_STANDARD_QUERY)
231
            ->setId(1234);
232
233
        $nsQuestion = (new ResourceRecord())
234
            ->setQuestion(true)
235
            ->setType(RecordTypeEnum::TYPE_NS)
236
            ->setName('example.com.');
237
238
        $message = (new Message())
239
            ->setHeader($queryHeader)
240
            ->addQuestion($nsQuestion);
241
242
        $query = Encoder::encodeMessage($message);
243
        $this->server->onMessage($query, '127.0.0.1', $socket = new MockSocket());
244
        $encodedResponse = $socket->getLastTransmission();
245
        $response = Decoder::decodeMessage($encodedResponse);
246
247
        $this->assertEquals(2, $response->getHeader()->getAnswerCount());
248
        $this->assertEquals(2, $response->getHeader()->getAdditionalRecordsCount());
249
    }
250
}
251