Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
namespace React\Dns\Protocol;
4
 
5
use React\Dns\Model\Message;
6
use React\Dns\Model\Record;
7
use React\Dns\Query\Query;
8
 
9
final class BinaryDumper
10
{
11
    /**
12
     * @param Message $message
13
     * @return string
14
     */
15
    public function toBinary(Message $message)
16
    {
17
        $data = '';
18
 
19
        $data .= $this->headerToBinary($message);
20
        $data .= $this->questionToBinary($message->questions);
21
        $data .= $this->recordsToBinary($message->answers);
22
        $data .= $this->recordsToBinary($message->authority);
23
        $data .= $this->recordsToBinary($message->additional);
24
 
25
        return $data;
26
    }
27
 
28
    /**
29
     * @param Message $message
30
     * @return string
31
     */
32
    private function headerToBinary(Message $message)
33
    {
34
        $data = '';
35
 
36
        $data .= pack('n', $message->id);
37
 
38
        $flags = 0x00;
39
        $flags = ($flags << 1) | ($message->qr ? 1 : 0);
40
        $flags = ($flags << 4) | $message->opcode;
41
        $flags = ($flags << 1) | ($message->aa ? 1 : 0);
42
        $flags = ($flags << 1) | ($message->tc ? 1 : 0);
43
        $flags = ($flags << 1) | ($message->rd ? 1 : 0);
44
        $flags = ($flags << 1) | ($message->ra ? 1 : 0);
45
        $flags = ($flags << 3) | 0; // skip unused zero bit
46
        $flags = ($flags << 4) | $message->rcode;
47
 
48
        $data .= pack('n', $flags);
49
 
50
        $data .= pack('n', count($message->questions));
51
        $data .= pack('n', count($message->answers));
52
        $data .= pack('n', count($message->authority));
53
        $data .= pack('n', count($message->additional));
54
 
55
        return $data;
56
    }
57
 
58
    /**
59
     * @param Query[] $questions
60
     * @return string
61
     */
62
    private function questionToBinary(array $questions)
63
    {
64
        $data = '';
65
 
66
        foreach ($questions as $question) {
67
            $data .= $this->domainNameToBinary($question->name);
68
            $data .= pack('n*', $question->type, $question->class);
69
        }
70
 
71
        return $data;
72
    }
73
 
74
    /**
75
     * @param Record[] $records
76
     * @return string
77
     */
78
    private function recordsToBinary(array $records)
79
    {
80
        $data = '';
81
 
82
        foreach ($records as $record) {
83
            /* @var $record Record */
84
            switch ($record->type) {
85
                case Message::TYPE_A:
86
                case Message::TYPE_AAAA:
87
                    $binary = \inet_pton($record->data);
88
                    break;
89
                case Message::TYPE_CNAME:
90
                case Message::TYPE_NS:
91
                case Message::TYPE_PTR:
92
                    $binary = $this->domainNameToBinary($record->data);
93
                    break;
94
                case Message::TYPE_TXT:
95
                    $binary = $this->textsToBinary($record->data);
96
                    break;
97
                case Message::TYPE_MX:
98
                    $binary = \pack(
99
                        'n',
100
                        $record->data['priority']
101
                    );
102
                    $binary .= $this->domainNameToBinary($record->data['target']);
103
                    break;
104
                case Message::TYPE_SRV:
105
                    $binary = \pack(
106
                        'n*',
107
                        $record->data['priority'],
108
                        $record->data['weight'],
109
                        $record->data['port']
110
                    );
111
                    $binary .= $this->domainNameToBinary($record->data['target']);
112
                    break;
113
                case Message::TYPE_SOA:
114
                    $binary  = $this->domainNameToBinary($record->data['mname']);
115
                    $binary .= $this->domainNameToBinary($record->data['rname']);
116
                    $binary .= \pack(
117
                        'N*',
118
                        $record->data['serial'],
119
                        $record->data['refresh'],
120
                        $record->data['retry'],
121
                        $record->data['expire'],
122
                        $record->data['minimum']
123
                    );
124
                    break;
125
                case Message::TYPE_CAA:
126
                    $binary = \pack(
127
                        'C*',
128
                        $record->data['flag'],
129
                        \strlen($record->data['tag'])
130
                    );
131
                    $binary .= $record->data['tag'];
132
                    $binary .= $record->data['value'];
133
                    break;
134
                case Message::TYPE_SSHFP:
135
                    $binary = \pack(
136
                        'CCH*',
137
                        $record->data['algorithm'],
138
                        $record->data['type'],
139
                        $record->data['fingerprint']
140
                    );
141
                    break;
142
                case Message::TYPE_OPT:
143
                    $binary = '';
144
                    foreach ($record->data as $opt => $value) {
145
                        if ($opt === Message::OPT_TCP_KEEPALIVE && $value !== null) {
146
                            $value = \pack('n', round($value * 10));
147
                        }
148
                        $binary .= \pack('n*', $opt, \strlen($value)) . $value;
149
                    }
150
                    break;
151
                default:
152
                    // RDATA is already stored as binary value for unknown record types
153
                    $binary = $record->data;
154
            }
155
 
156
            $data .= $this->domainNameToBinary($record->name);
157
            $data .= \pack('nnNn', $record->type, $record->class, $record->ttl, \strlen($binary));
158
            $data .= $binary;
159
        }
160
 
161
        return $data;
162
    }
163
 
164
    /**
165
     * @param string[] $texts
166
     * @return string
167
     */
168
    private function textsToBinary(array $texts)
169
    {
170
        $data = '';
171
        foreach ($texts as $text) {
172
            $data .= \chr(\strlen($text)) . $text;
173
        }
174
        return $data;
175
    }
176
 
177
    /**
178
     * @param string $host
179
     * @return string
180
     */
181
    private function domainNameToBinary($host)
182
    {
183
        if ($host === '') {
184
            return "\0";
185
        }
186
 
187
        // break up domain name at each dot that is not preceeded by a backslash (escaped notation)
188
        return $this->textsToBinary(
189
            \array_map(
190
                'stripcslashes',
191
                \preg_split(
192
                    '/(?<!\\\\)\./',
193
                    $host . '.'
194
                )
195
            )
196
        );
197
    }
198
}