friendship ended with social-app. php is my new best friend
1<?php 2 3namespace React\Dns\Protocol; 4 5use React\Dns\Model\Message; 6use React\Dns\Model\Record; 7use React\Dns\Query\Query; 8 9final 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 case Message::TYPE_SPF: 96 $binary = $this->textsToBinary($record->data); 97 break; 98 case Message::TYPE_MX: 99 $binary = \pack( 100 'n', 101 $record->data['priority'] 102 ); 103 $binary .= $this->domainNameToBinary($record->data['target']); 104 break; 105 case Message::TYPE_SRV: 106 $binary = \pack( 107 'n*', 108 $record->data['priority'], 109 $record->data['weight'], 110 $record->data['port'] 111 ); 112 $binary .= $this->domainNameToBinary($record->data['target']); 113 break; 114 case Message::TYPE_SOA: 115 $binary = $this->domainNameToBinary($record->data['mname']); 116 $binary .= $this->domainNameToBinary($record->data['rname']); 117 $binary .= \pack( 118 'N*', 119 $record->data['serial'], 120 $record->data['refresh'], 121 $record->data['retry'], 122 $record->data['expire'], 123 $record->data['minimum'] 124 ); 125 break; 126 case Message::TYPE_CAA: 127 $binary = \pack( 128 'C*', 129 $record->data['flag'], 130 \strlen($record->data['tag']) 131 ); 132 $binary .= $record->data['tag']; 133 $binary .= $record->data['value']; 134 break; 135 case Message::TYPE_SSHFP: 136 $binary = \pack( 137 'CCH*', 138 $record->data['algorithm'], 139 $record->data['type'], 140 $record->data['fingerprint'] 141 ); 142 break; 143 case Message::TYPE_OPT: 144 $binary = ''; 145 foreach ($record->data as $opt => $value) { 146 if ($opt === Message::OPT_TCP_KEEPALIVE && $value !== null) { 147 $value = \pack('n', round($value * 10)); 148 } 149 $binary .= \pack('n*', $opt, \strlen((string) $value)) . $value; 150 } 151 break; 152 default: 153 // RDATA is already stored as binary value for unknown record types 154 $binary = $record->data; 155 } 156 157 $data .= $this->domainNameToBinary($record->name); 158 $data .= \pack('nnNn', $record->type, $record->class, $record->ttl, \strlen($binary)); 159 $data .= $binary; 160 } 161 162 return $data; 163 } 164 165 /** 166 * @param string[] $texts 167 * @return string 168 */ 169 private function textsToBinary(array $texts) 170 { 171 $data = ''; 172 foreach ($texts as $text) { 173 $data .= \chr(\strlen($text)) . $text; 174 } 175 return $data; 176 } 177 178 /** 179 * @param string $host 180 * @return string 181 */ 182 private function domainNameToBinary($host) 183 { 184 if ($host === '') { 185 return "\0"; 186 } 187 188 // break up domain name at each dot that is not preceeded by a backslash (escaped notation) 189 return $this->textsToBinary( 190 \array_map( 191 'stripcslashes', 192 \preg_split( 193 '/(?<!\\\\)\./', 194 $host . '.' 195 ) 196 ) 197 ); 198 } 199}