ParserTest.php 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. <?php
  2. namespace React\Tests\Dns\Protocol;
  3. use React\Dns\Model\Message;
  4. use React\Dns\Protocol\Parser;
  5. use React\Tests\Dns\TestCase;
  6. class ParserTest extends TestCase
  7. {
  8. private $parser;
  9. /**
  10. * @before
  11. */
  12. public function setUpParser()
  13. {
  14. $this->parser = new Parser();
  15. }
  16. /**
  17. * @dataProvider provideConvertTcpDumpToBinary
  18. */
  19. public function testConvertTcpDumpToBinary($expected, $data)
  20. {
  21. $this->assertSame($expected, $this->convertTcpDumpToBinary($data));
  22. }
  23. public function provideConvertTcpDumpToBinary()
  24. {
  25. return array(
  26. array(chr(0x72).chr(0x62), "72 62"),
  27. array(chr(0x72).chr(0x62).chr(0x01).chr(0x00), "72 62 01 00"),
  28. array(chr(0x72).chr(0x62).chr(0x01).chr(0x00).chr(0x00).chr(0x01), "72 62 01 00 00 01"),
  29. array(chr(0x01).chr(0x00).chr(0x01), "01 00 01"),
  30. );
  31. }
  32. public function testParseRequest()
  33. {
  34. $data = "";
  35. $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
  36. $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
  37. $data .= "00 01 00 01"; // question: type A, class IN
  38. $data = $this->convertTcpDumpToBinary($data);
  39. $request = $this->parser->parseMessage($data);
  40. $this->assertFalse(isset($request->data));
  41. $this->assertFalse(isset($request->consumed));
  42. $this->assertSame(0x7262, $request->id);
  43. $this->assertSame(false, $request->qr);
  44. $this->assertSame(Message::OPCODE_QUERY, $request->opcode);
  45. $this->assertSame(false, $request->aa);
  46. $this->assertSame(false, $request->tc);
  47. $this->assertSame(true, $request->rd);
  48. $this->assertSame(false, $request->ra);
  49. $this->assertSame(Message::RCODE_OK, $request->rcode);
  50. $this->assertCount(1, $request->questions);
  51. $this->assertSame('igor.io', $request->questions[0]->name);
  52. $this->assertSame(Message::TYPE_A, $request->questions[0]->type);
  53. $this->assertSame(Message::CLASS_IN, $request->questions[0]->class);
  54. }
  55. public function testParseResponse()
  56. {
  57. $data = "";
  58. $data .= "72 62 81 80 00 01 00 01 00 00 00 00"; // header
  59. $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
  60. $data .= "00 01 00 01"; // question: type A, class IN
  61. $data .= "c0 0c"; // answer: offset pointer to igor.io
  62. $data .= "00 01 00 01"; // answer: type A, class IN
  63. $data .= "00 01 51 80"; // answer: ttl 86400
  64. $data .= "00 04"; // answer: rdlength 4
  65. $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131
  66. $data = $this->convertTcpDumpToBinary($data);
  67. $response = $this->parser->parseMessage($data);
  68. $this->assertSame(0x7262, $response->id);
  69. $this->assertSame(true, $response->qr);
  70. $this->assertSame(Message::OPCODE_QUERY, $response->opcode);
  71. $this->assertSame(false, $response->aa);
  72. $this->assertSame(false, $response->tc);
  73. $this->assertSame(true, $response->rd);
  74. $this->assertSame(true, $response->ra);
  75. $this->assertSame(Message::RCODE_OK, $response->rcode);
  76. $this->assertCount(1, $response->questions);
  77. $this->assertSame('igor.io', $response->questions[0]->name);
  78. $this->assertSame(Message::TYPE_A, $response->questions[0]->type);
  79. $this->assertSame(Message::CLASS_IN, $response->questions[0]->class);
  80. $this->assertCount(1, $response->answers);
  81. $this->assertSame('igor.io', $response->answers[0]->name);
  82. $this->assertSame(Message::TYPE_A, $response->answers[0]->type);
  83. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  84. $this->assertSame(86400, $response->answers[0]->ttl);
  85. $this->assertSame('178.79.169.131', $response->answers[0]->data);
  86. }
  87. public function testParseRequestWithTwoQuestions()
  88. {
  89. $data = "";
  90. $data .= "72 62 01 00 00 02 00 00 00 00 00 00"; // header
  91. $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
  92. $data .= "00 01 00 01"; // question: type A, class IN
  93. $data .= "03 77 77 77 04 69 67 6f 72 02 69 6f 00"; // question: www.igor.io
  94. $data .= "00 01 00 01"; // question: type A, class IN
  95. $data = $this->convertTcpDumpToBinary($data);
  96. $request = $this->parser->parseMessage($data);
  97. $this->assertCount(2, $request->questions);
  98. $this->assertSame('igor.io', $request->questions[0]->name);
  99. $this->assertSame(Message::TYPE_A, $request->questions[0]->type);
  100. $this->assertSame(Message::CLASS_IN, $request->questions[0]->class);
  101. $this->assertSame('www.igor.io', $request->questions[1]->name);
  102. $this->assertSame(Message::TYPE_A, $request->questions[1]->type);
  103. $this->assertSame(Message::CLASS_IN, $request->questions[1]->class);
  104. }
  105. public function testParseAnswerWithInlineData()
  106. {
  107. $data = "";
  108. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  109. $data .= "00 01 00 01"; // answer: type A, class IN
  110. $data .= "00 01 51 80"; // answer: ttl 86400
  111. $data .= "00 04"; // answer: rdlength 4
  112. $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131
  113. $response = $this->parseAnswer($data);
  114. $this->assertCount(1, $response->answers);
  115. $this->assertSame('igor.io', $response->answers[0]->name);
  116. $this->assertSame(Message::TYPE_A, $response->answers[0]->type);
  117. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  118. $this->assertSame(86400, $response->answers[0]->ttl);
  119. $this->assertSame('178.79.169.131', $response->answers[0]->data);
  120. }
  121. public function testParseAnswerWithExcessiveTtlReturnsZeroTtl()
  122. {
  123. $data = "";
  124. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  125. $data .= "00 01 00 01"; // answer: type A, class IN
  126. $data .= "ff ff ff ff"; // answer: ttl 2^32 - 1
  127. $data .= "00 04"; // answer: rdlength 4
  128. $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131
  129. $response = $this->parseAnswer($data);
  130. $this->assertCount(1, $response->answers);
  131. $this->assertSame('igor.io', $response->answers[0]->name);
  132. $this->assertSame(Message::TYPE_A, $response->answers[0]->type);
  133. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  134. $this->assertSame(0, $response->answers[0]->ttl);
  135. $this->assertSame('178.79.169.131', $response->answers[0]->data);
  136. }
  137. public function testParseAnswerWithTtlExactlyBoundaryReturnsZeroTtl()
  138. {
  139. $data = "";
  140. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  141. $data .= "00 01 00 01"; // answer: type A, class IN
  142. $data .= "80 00 00 00"; // answer: ttl 2^31
  143. $data .= "00 04"; // answer: rdlength 4
  144. $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131
  145. $response = $this->parseAnswer($data);
  146. $this->assertCount(1, $response->answers);
  147. $this->assertSame('igor.io', $response->answers[0]->name);
  148. $this->assertSame(Message::TYPE_A, $response->answers[0]->type);
  149. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  150. $this->assertSame(0, $response->answers[0]->ttl);
  151. $this->assertSame('178.79.169.131', $response->answers[0]->data);
  152. }
  153. public function testParseAnswerWithMaximumTtlReturnsExactTtl()
  154. {
  155. $data = "";
  156. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  157. $data .= "00 01 00 01"; // answer: type A, class IN
  158. $data .= "7f ff ff ff"; // answer: ttl 2^31 - 1
  159. $data .= "00 04"; // answer: rdlength 4
  160. $data .= "b2 4f a9 83"; // answer: rdata 178.79.169.131
  161. $response = $this->parseAnswer($data);
  162. $this->assertCount(1, $response->answers);
  163. $this->assertSame('igor.io', $response->answers[0]->name);
  164. $this->assertSame(Message::TYPE_A, $response->answers[0]->type);
  165. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  166. $this->assertSame(0x7fffffff, $response->answers[0]->ttl);
  167. $this->assertSame('178.79.169.131', $response->answers[0]->data);
  168. }
  169. public function testParseAnswerWithUnknownType()
  170. {
  171. $data = "";
  172. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  173. $data .= "23 28 00 01"; // answer: type 9000, class IN
  174. $data .= "00 01 51 80"; // answer: ttl 86400
  175. $data .= "00 05"; // answer: rdlength 5
  176. $data .= "68 65 6c 6c 6f"; // answer: rdata "hello"
  177. $response = $this->parseAnswer($data);
  178. $this->assertCount(1, $response->answers);
  179. $this->assertSame('igor.io', $response->answers[0]->name);
  180. $this->assertSame(9000, $response->answers[0]->type);
  181. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  182. $this->assertSame(86400, $response->answers[0]->ttl);
  183. $this->assertSame('hello', $response->answers[0]->data);
  184. }
  185. public function testParseResponseWithCnameAndOffsetPointers()
  186. {
  187. $data = "";
  188. $data .= "9e 8d 81 80 00 01 00 01 00 00 00 00"; // header
  189. $data .= "04 6d 61 69 6c 06 67 6f 6f 67 6c 65 03 63 6f 6d 00"; // question: mail.google.com
  190. $data .= "00 05 00 01"; // question: type CNAME, class IN
  191. $data .= "c0 0c"; // answer: offset pointer to mail.google.com
  192. $data .= "00 05 00 01"; // answer: type CNAME, class IN
  193. $data .= "00 00 a8 9c"; // answer: ttl 43164
  194. $data .= "00 0f"; // answer: rdlength 15
  195. $data .= "0a 67 6f 6f 67 6c 65 6d 61 69 6c 01 6c"; // answer: rdata googlemail.l.
  196. $data .= "c0 11"; // answer: rdata offset pointer to google.com
  197. $data = $this->convertTcpDumpToBinary($data);
  198. $response = $this->parser->parseMessage($data);
  199. $this->assertCount(1, $response->questions);
  200. $this->assertSame('mail.google.com', $response->questions[0]->name);
  201. $this->assertSame(Message::TYPE_CNAME, $response->questions[0]->type);
  202. $this->assertSame(Message::CLASS_IN, $response->questions[0]->class);
  203. $this->assertCount(1, $response->answers);
  204. $this->assertSame('mail.google.com', $response->answers[0]->name);
  205. $this->assertSame(Message::TYPE_CNAME, $response->answers[0]->type);
  206. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  207. $this->assertSame(43164, $response->answers[0]->ttl);
  208. $this->assertSame('googlemail.l.google.com', $response->answers[0]->data);
  209. }
  210. public function testParseAAAAResponse()
  211. {
  212. $data = "";
  213. $data .= "cd 72 81 80 00 01 00 01 00 00 00 00 06"; // header
  214. $data .= "67 6f 6f 67 6c 65 03 63 6f 6d 00"; // question: google.com
  215. $data .= "00 1c 00 01"; // question: type AAAA, class IN
  216. $data .= "c0 0c"; // answer: offset pointer to google.com
  217. $data .= "00 1c 00 01"; // answer: type AAAA, class IN
  218. $data .= "00 00 01 2b"; // answer: ttl 299
  219. $data .= "00 10"; // answer: rdlength 16
  220. $data .= "2a 00 14 50 40 09 08 09 00 00 00 00 00 00 20 0e"; // answer: 2a00:1450:4009:809::200e
  221. $data = $this->convertTcpDumpToBinary($data);
  222. $response = $this->parser->parseMessage($data);
  223. $this->assertSame(0xcd72, $response->id);
  224. $this->assertSame(true, $response->qr);
  225. $this->assertSame(Message::OPCODE_QUERY, $response->opcode);
  226. $this->assertSame(false, $response->aa);
  227. $this->assertSame(false, $response->tc);
  228. $this->assertSame(true, $response->rd);
  229. $this->assertSame(true, $response->ra);
  230. $this->assertSame(Message::RCODE_OK, $response->rcode);
  231. $this->assertCount(1, $response->questions);
  232. $this->assertSame('google.com', $response->questions[0]->name);
  233. $this->assertSame(Message::TYPE_AAAA, $response->questions[0]->type);
  234. $this->assertSame(Message::CLASS_IN, $response->questions[0]->class);
  235. $this->assertCount(1, $response->answers);
  236. $this->assertSame('google.com', $response->answers[0]->name);
  237. $this->assertSame(Message::TYPE_AAAA, $response->answers[0]->type);
  238. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  239. $this->assertSame(299, $response->answers[0]->ttl);
  240. $this->assertSame('2a00:1450:4009:809::200e', $response->answers[0]->data);
  241. }
  242. public function testParseTXTResponse()
  243. {
  244. $data = "";
  245. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  246. $data .= "00 10 00 01"; // answer: type TXT, class IN
  247. $data .= "00 01 51 80"; // answer: ttl 86400
  248. $data .= "00 06"; // answer: rdlength 6
  249. $data .= "05 68 65 6c 6c 6f"; // answer: rdata length 5: hello
  250. $response = $this->parseAnswer($data);
  251. $this->assertCount(1, $response->answers);
  252. $this->assertSame('igor.io', $response->answers[0]->name);
  253. $this->assertSame(Message::TYPE_TXT, $response->answers[0]->type);
  254. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  255. $this->assertSame(86400, $response->answers[0]->ttl);
  256. $this->assertSame(array('hello'), $response->answers[0]->data);
  257. }
  258. public function testParseSPFResponse()
  259. {
  260. $data = "";
  261. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  262. $data .= "00 63 00 01"; // answer: type SPF, class IN
  263. $data .= "00 01 51 80"; // answer: ttl 86400
  264. $data .= "00 06"; // answer: rdlength 6
  265. $data .= "05 68 65 6c 6c 6f"; // answer: rdata length 5: hello
  266. $response = $this->parseAnswer($data);
  267. $this->assertCount(1, $response->answers);
  268. $this->assertSame('igor.io', $response->answers[0]->name);
  269. $this->assertSame(Message::TYPE_SPF, $response->answers[0]->type);
  270. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  271. $this->assertSame(86400, $response->answers[0]->ttl);
  272. $this->assertSame(array('hello'), $response->answers[0]->data);
  273. }
  274. public function testParseTXTResponseMultiple()
  275. {
  276. $data = "";
  277. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  278. $data .= "00 10 00 01"; // answer: type TXT, class IN
  279. $data .= "00 01 51 80"; // answer: ttl 86400
  280. $data .= "00 0C"; // answer: rdlength 12
  281. $data .= "05 68 65 6c 6c 6f 05 77 6f 72 6c 64"; // answer: rdata length 5: hello, length 5: world
  282. $response = $this->parseAnswer($data);
  283. $this->assertCount(1, $response->answers);
  284. $this->assertSame('igor.io', $response->answers[0]->name);
  285. $this->assertSame(Message::TYPE_TXT, $response->answers[0]->type);
  286. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  287. $this->assertSame(86400, $response->answers[0]->ttl);
  288. $this->assertSame(array('hello', 'world'), $response->answers[0]->data);
  289. }
  290. public function testParseMXResponse()
  291. {
  292. $data = "";
  293. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  294. $data .= "00 0f 00 01"; // answer: type MX, class IN
  295. $data .= "00 01 51 80"; // answer: ttl 86400
  296. $data .= "00 09"; // answer: rdlength 9
  297. $data .= "00 0a 05 68 65 6c 6c 6f 00"; // answer: rdata priority 10: hello
  298. $response = $this->parseAnswer($data);
  299. $this->assertCount(1, $response->answers);
  300. $this->assertSame('igor.io', $response->answers[0]->name);
  301. $this->assertSame(Message::TYPE_MX, $response->answers[0]->type);
  302. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  303. $this->assertSame(86400, $response->answers[0]->ttl);
  304. $this->assertSame(array('priority' => 10, 'target' => 'hello'), $response->answers[0]->data);
  305. }
  306. public function testParseSRVResponse()
  307. {
  308. $data = "";
  309. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  310. $data .= "00 21 00 01"; // answer: type SRV, class IN
  311. $data .= "00 01 51 80"; // answer: ttl 86400
  312. $data .= "00 0C"; // answer: rdlength 12
  313. $data .= "00 0a 00 14 1F 90 04 74 65 73 74 00"; // answer: rdata priority 10, weight 20, port 8080 test
  314. $response = $this->parseAnswer($data);
  315. $this->assertCount(1, $response->answers);
  316. $this->assertSame('igor.io', $response->answers[0]->name);
  317. $this->assertSame(Message::TYPE_SRV, $response->answers[0]->type);
  318. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  319. $this->assertSame(86400, $response->answers[0]->ttl);
  320. $this->assertSame(
  321. array(
  322. 'priority' => 10,
  323. 'weight' => 20,
  324. 'port' => 8080,
  325. 'target' => 'test'
  326. ),
  327. $response->answers[0]->data
  328. );
  329. }
  330. public function testParseMessageResponseWithTwoAnswers()
  331. {
  332. $data = "";
  333. $data .= "bc 73 81 80 00 01 00 02 00 00 00 00"; // header
  334. $data .= "02 69 6f 0d 77 68 6f 69 73 2d 73 65 72 76 65 72 73 03 6e 65 74 00";
  335. // question: io.whois-servers.net
  336. $data .= "00 01 00 01"; // question: type A, class IN
  337. $data .= "c0 0c"; // answer: offset pointer to io.whois-servers.net
  338. $data .= "00 05 00 01"; // answer: type CNAME, class IN
  339. $data .= "00 00 00 29"; // answer: ttl 41
  340. $data .= "00 0e"; // answer: rdlength 14
  341. $data .= "05 77 68 6f 69 73 03 6e 69 63 02 69 6f 00"; // answer: rdata whois.nic.io
  342. $data .= "c0 32"; // answer: offset pointer to whois.nic.io
  343. $data .= "00 01 00 01"; // answer: type CNAME, class IN
  344. $data .= "00 00 0d f7"; // answer: ttl 3575
  345. $data .= "00 04"; // answer: rdlength 4
  346. $data .= "c1 df 4e 98"; // answer: rdata 193.223.78.152
  347. $data = $this->convertTcpDumpToBinary($data);
  348. $response = $this->parser->parseMessage($data);
  349. $this->assertCount(1, $response->questions);
  350. $this->assertSame('io.whois-servers.net', $response->questions[0]->name);
  351. $this->assertSame(Message::TYPE_A, $response->questions[0]->type);
  352. $this->assertSame(Message::CLASS_IN, $response->questions[0]->class);
  353. $this->assertCount(2, $response->answers);
  354. $this->assertSame('io.whois-servers.net', $response->answers[0]->name);
  355. $this->assertSame(Message::TYPE_CNAME, $response->answers[0]->type);
  356. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  357. $this->assertSame(41, $response->answers[0]->ttl);
  358. $this->assertSame('whois.nic.io', $response->answers[0]->data);
  359. $this->assertSame('whois.nic.io', $response->answers[1]->name);
  360. $this->assertSame(Message::TYPE_A, $response->answers[1]->type);
  361. $this->assertSame(Message::CLASS_IN, $response->answers[1]->class);
  362. $this->assertSame(3575, $response->answers[1]->ttl);
  363. $this->assertSame('193.223.78.152', $response->answers[1]->data);
  364. }
  365. public function testParseMessageResponseWithTwoAuthorityRecords()
  366. {
  367. $data = "";
  368. $data .= "bc 73 81 80 00 01 00 00 00 02 00 00"; // header
  369. $data .= "02 69 6f 0d 77 68 6f 69 73 2d 73 65 72 76 65 72 73 03 6e 65 74 00";
  370. // question: io.whois-servers.net
  371. $data .= "00 01 00 01"; // question: type A, class IN
  372. $data .= "c0 0c"; // authority: offset pointer to io.whois-servers.net
  373. $data .= "00 05 00 01"; // authority: type CNAME, class IN
  374. $data .= "00 00 00 29"; // authority: ttl 41
  375. $data .= "00 0e"; // authority: rdlength 14
  376. $data .= "05 77 68 6f 69 73 03 6e 69 63 02 69 6f 00"; // authority: rdata whois.nic.io
  377. $data .= "c0 32"; // authority: offset pointer to whois.nic.io
  378. $data .= "00 01 00 01"; // authority: type CNAME, class IN
  379. $data .= "00 00 0d f7"; // authority: ttl 3575
  380. $data .= "00 04"; // authority: rdlength 4
  381. $data .= "c1 df 4e 98"; // authority: rdata 193.223.78.152
  382. $data = $this->convertTcpDumpToBinary($data);
  383. $response = $this->parser->parseMessage($data);
  384. $this->assertCount(1, $response->questions);
  385. $this->assertSame('io.whois-servers.net', $response->questions[0]->name);
  386. $this->assertSame(Message::TYPE_A, $response->questions[0]->type);
  387. $this->assertSame(Message::CLASS_IN, $response->questions[0]->class);
  388. $this->assertCount(0, $response->answers);
  389. $this->assertCount(2, $response->authority);
  390. $this->assertSame('io.whois-servers.net', $response->authority[0]->name);
  391. $this->assertSame(Message::TYPE_CNAME, $response->authority[0]->type);
  392. $this->assertSame(Message::CLASS_IN, $response->authority[0]->class);
  393. $this->assertSame(41, $response->authority[0]->ttl);
  394. $this->assertSame('whois.nic.io', $response->authority[0]->data);
  395. $this->assertSame('whois.nic.io', $response->authority[1]->name);
  396. $this->assertSame(Message::TYPE_A, $response->authority[1]->type);
  397. $this->assertSame(Message::CLASS_IN, $response->authority[1]->class);
  398. $this->assertSame(3575, $response->authority[1]->ttl);
  399. $this->assertSame('193.223.78.152', $response->authority[1]->data);
  400. }
  401. public function testParseMessageResponseWithAnswerAndAdditionalRecord()
  402. {
  403. $data = "";
  404. $data .= "bc 73 81 80 00 01 00 01 00 00 00 01"; // header
  405. $data .= "02 69 6f 0d 77 68 6f 69 73 2d 73 65 72 76 65 72 73 03 6e 65 74 00";
  406. // question: io.whois-servers.net
  407. $data .= "00 01 00 01"; // question: type A, class IN
  408. $data .= "c0 0c"; // answer: offset pointer to io.whois-servers.net
  409. $data .= "00 05 00 01"; // answer: type CNAME, class IN
  410. $data .= "00 00 00 29"; // answer: ttl 41
  411. $data .= "00 0e"; // answer: rdlength 14
  412. $data .= "05 77 68 6f 69 73 03 6e 69 63 02 69 6f 00"; // answer: rdata whois.nic.io
  413. $data .= "c0 32"; // additional: offset pointer to whois.nic.io
  414. $data .= "00 01 00 01"; // additional: type CNAME, class IN
  415. $data .= "00 00 0d f7"; // additional: ttl 3575
  416. $data .= "00 04"; // additional: rdlength 4
  417. $data .= "c1 df 4e 98"; // additional: rdata 193.223.78.152
  418. $data = $this->convertTcpDumpToBinary($data);
  419. $response = $this->parser->parseMessage($data);
  420. $this->assertCount(1, $response->questions);
  421. $this->assertSame('io.whois-servers.net', $response->questions[0]->name);
  422. $this->assertSame(Message::TYPE_A, $response->questions[0]->type);
  423. $this->assertSame(Message::CLASS_IN, $response->questions[0]->class);
  424. $this->assertCount(1, $response->answers);
  425. $this->assertSame('io.whois-servers.net', $response->answers[0]->name);
  426. $this->assertSame(Message::TYPE_CNAME, $response->answers[0]->type);
  427. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  428. $this->assertSame(41, $response->answers[0]->ttl);
  429. $this->assertSame('whois.nic.io', $response->answers[0]->data);
  430. $this->assertCount(0, $response->authority);
  431. $this->assertCount(1, $response->additional);
  432. $this->assertSame('whois.nic.io', $response->additional[0]->name);
  433. $this->assertSame(Message::TYPE_A, $response->additional[0]->type);
  434. $this->assertSame(Message::CLASS_IN, $response->additional[0]->class);
  435. $this->assertSame(3575, $response->additional[0]->ttl);
  436. $this->assertSame('193.223.78.152', $response->additional[0]->data);
  437. }
  438. public function testParseNSResponse()
  439. {
  440. $data = "";
  441. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  442. $data .= "00 02 00 01"; // answer: type NS, class IN
  443. $data .= "00 01 51 80"; // answer: ttl 86400
  444. $data .= "00 07"; // answer: rdlength 7
  445. $data .= "05 68 65 6c 6c 6f 00"; // answer: rdata hello
  446. $response = $this->parseAnswer($data);
  447. $this->assertCount(1, $response->answers);
  448. $this->assertSame('igor.io', $response->answers[0]->name);
  449. $this->assertSame(Message::TYPE_NS, $response->answers[0]->type);
  450. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  451. $this->assertSame(86400, $response->answers[0]->ttl);
  452. $this->assertSame('hello', $response->answers[0]->data);
  453. }
  454. public function testParseSSHFPResponse()
  455. {
  456. $data = "";
  457. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  458. $data .= "00 2c 00 01"; // answer: type SSHFP, class IN
  459. $data .= "00 01 51 80"; // answer: ttl 86400
  460. $data .= "00 06"; // answer: rdlength 6
  461. $data .= "01 01 69 ac 09 0c"; // answer: algorithm 1 (RSA), type 1 (SHA-1), fingerprint "69ac090c"
  462. $response = $this->parseAnswer($data);
  463. $this->assertCount(1, $response->answers);
  464. $this->assertSame('igor.io', $response->answers[0]->name);
  465. $this->assertSame(Message::TYPE_SSHFP, $response->answers[0]->type);
  466. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  467. $this->assertSame(86400, $response->answers[0]->ttl);
  468. $this->assertSame(array('algorithm' => 1, 'type' => 1, 'fingerprint' => '69ac090c'), $response->answers[0]->data);
  469. }
  470. public function testParseOptResponseWithoutOptions()
  471. {
  472. $data = "";
  473. $data .= "00"; // answer: empty domain
  474. $data .= "00 29 03 e8"; // answer: type OPT, class 1000 UDP size
  475. $data .= "00 00 00 00"; // answer: ttl 0
  476. $data .= "00 00"; // answer: rdlength 0
  477. $response = $this->parseAnswer($data);
  478. $this->assertCount(1, $response->answers);
  479. $this->assertSame('', $response->answers[0]->name);
  480. $this->assertSame(Message::TYPE_OPT, $response->answers[0]->type);
  481. $this->assertSame(1000, $response->answers[0]->class);
  482. $this->assertSame(0, $response->answers[0]->ttl);
  483. $this->assertSame(array(), $response->answers[0]->data);
  484. }
  485. public function testParseOptResponseWithOptTcpKeepaliveDesired()
  486. {
  487. $data = "";
  488. $data .= "00"; // answer: empty domain
  489. $data .= "00 29 03 e8"; // answer: type OPT, class 1000 UDP size
  490. $data .= "00 00 00 00"; // answer: ttl 0
  491. $data .= "00 04"; // answer: rdlength 4
  492. $data .= "00 0b 00 00"; // OPT_TCP_KEEPALIVE=null encoded
  493. $response = $this->parseAnswer($data);
  494. $this->assertCount(1, $response->answers);
  495. $this->assertSame('', $response->answers[0]->name);
  496. $this->assertSame(Message::TYPE_OPT, $response->answers[0]->type);
  497. $this->assertSame(1000, $response->answers[0]->class);
  498. $this->assertSame(0, $response->answers[0]->ttl);
  499. $this->assertSame(array(Message::OPT_TCP_KEEPALIVE => null), $response->answers[0]->data);
  500. }
  501. public function testParseOptResponseWithOptTcpKeepaliveGiven()
  502. {
  503. $data = "";
  504. $data .= "00"; // answer: empty domain
  505. $data .= "00 29 03 e8"; // answer: type OPT, class 1000 UDP size
  506. $data .= "00 00 00 00"; // answer: ttl 0
  507. $data .= "00 06"; // answer: rdlength 4
  508. $data .= "00 0b 00 02 00 0c"; // OPT_TCP_KEEPALIVE=1.2s encoded
  509. $response = $this->parseAnswer($data);
  510. $this->assertCount(1, $response->answers);
  511. $this->assertSame('', $response->answers[0]->name);
  512. $this->assertSame(Message::TYPE_OPT, $response->answers[0]->type);
  513. $this->assertSame(1000, $response->answers[0]->class);
  514. $this->assertSame(0, $response->answers[0]->ttl);
  515. $this->assertSame(array(Message::OPT_TCP_KEEPALIVE => 1.2), $response->answers[0]->data);
  516. }
  517. public function testParseOptResponseWithCustomOptions()
  518. {
  519. $data = "";
  520. $data .= "00"; // answer: empty domain
  521. $data .= "00 29 03 e8"; // answer: type OPT, class 1000 UDP size
  522. $data .= "00 00 00 00"; // answer: ttl 0
  523. $data .= "00 0b"; // answer: rdlength 11
  524. $data .= "00 a0 00 03 66 6f 6f"; // OPT code 0xa0 encoded
  525. $data .= "00 01 00 00 "; // OPT code 0x01 encoded
  526. $response = $this->parseAnswer($data);
  527. $this->assertCount(1, $response->answers);
  528. $this->assertSame('', $response->answers[0]->name);
  529. $this->assertSame(Message::TYPE_OPT, $response->answers[0]->type);
  530. $this->assertSame(1000, $response->answers[0]->class);
  531. $this->assertSame(0, $response->answers[0]->ttl);
  532. $this->assertSame(array(0xa0 => 'foo', 0x01 => ''), $response->answers[0]->data);
  533. }
  534. public function testParseSOAResponse()
  535. {
  536. $data = "";
  537. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  538. $data .= "00 06 00 01"; // answer: type SOA, class IN
  539. $data .= "00 01 51 80"; // answer: ttl 86400
  540. $data .= "00 27"; // answer: rdlength 39
  541. $data .= "02 6e 73 05 68 65 6c 6c 6f 00"; // answer: rdata ns.hello (mname)
  542. $data .= "01 65 05 68 65 6c 6c 6f 00"; // answer: rdata e.hello (rname)
  543. $data .= "78 49 28 D5 00 00 2a 30 00 00 0e 10"; // answer: rdata 2018060501, 10800, 3600
  544. $data .= "00 09 3a 80 00 00 0e 10"; // answer: 605800, 3600
  545. $response = $this->parseAnswer($data);
  546. $this->assertCount(1, $response->answers);
  547. $this->assertSame('igor.io', $response->answers[0]->name);
  548. $this->assertSame(Message::TYPE_SOA, $response->answers[0]->type);
  549. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  550. $this->assertSame(86400, $response->answers[0]->ttl);
  551. $this->assertSame(
  552. array(
  553. 'mname' => 'ns.hello',
  554. 'rname' => 'e.hello',
  555. 'serial' => 2018060501,
  556. 'refresh' => 10800,
  557. 'retry' => 3600,
  558. 'expire' => 604800,
  559. 'minimum' => 3600
  560. ),
  561. $response->answers[0]->data
  562. );
  563. }
  564. public function testParseCAAResponse()
  565. {
  566. $data = "";
  567. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  568. $data .= "01 01 00 01"; // answer: type CAA, class IN
  569. $data .= "00 01 51 80"; // answer: ttl 86400
  570. $data .= "00 16"; // answer: rdlength 22
  571. $data .= "00 05 69 73 73 75 65"; // answer: rdata 0, issue
  572. $data .= "6c 65 74 73 65 6e 63 72 79 70 74 2e 6f 72 67"; // answer: letsencrypt.org
  573. $response = $this->parseAnswer($data);
  574. $this->assertCount(1, $response->answers);
  575. $this->assertSame('igor.io', $response->answers[0]->name);
  576. $this->assertSame(Message::TYPE_CAA, $response->answers[0]->type);
  577. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  578. $this->assertSame(86400, $response->answers[0]->ttl);
  579. $this->assertSame(array('flag' => 0, 'tag' => 'issue', 'value' => 'letsencrypt.org'), $response->answers[0]->data);
  580. }
  581. public function testParsePTRResponse()
  582. {
  583. $data = "";
  584. $data .= "5d d8 81 80 00 01 00 01 00 00 00 00"; // header
  585. $data .= "01 34 01 34 01 38 01 38 07 69 6e"; // question: 4.4.8.8.in-addr.arpa
  586. $data .= "2d 61 64 64 72 04 61 72 70 61 00"; // question (continued)
  587. $data .= "00 0c 00 01"; // question: type PTR, class IN
  588. $data .= "c0 0c"; // answer: offset pointer to rdata
  589. $data .= "00 0c 00 01"; // answer: type PTR, class IN
  590. $data .= "00 01 51 7f"; // answer: ttl 86399
  591. $data .= "00 20"; // answer: rdlength 32
  592. $data .= "13 67 6f 6f 67 6c 65 2d 70 75 62 6c 69 63 2d 64"; // answer: rdata google-public-dns-b.google.com.
  593. $data .= "6e 73 2d 62 06 67 6f 6f 67 6c 65 03 63 6f 6d 00";
  594. $data = $this->convertTcpDumpToBinary($data);
  595. $response = $this->parser->parseMessage($data);
  596. $this->assertSame(0x5dd8, $response->id);
  597. $this->assertSame(true, $response->qr);
  598. $this->assertSame(Message::OPCODE_QUERY, $response->opcode);
  599. $this->assertSame(false, $response->aa);
  600. $this->assertSame(false, $response->tc);
  601. $this->assertSame(true, $response->rd);
  602. $this->assertSame(true, $response->ra);
  603. $this->assertSame(Message::RCODE_OK, $response->rcode);
  604. $this->assertCount(1, $response->questions);
  605. $this->assertSame('4.4.8.8.in-addr.arpa', $response->questions[0]->name);
  606. $this->assertSame(Message::TYPE_PTR, $response->questions[0]->type);
  607. $this->assertSame(Message::CLASS_IN, $response->questions[0]->class);
  608. $this->assertCount(1, $response->answers);
  609. $this->assertSame('4.4.8.8.in-addr.arpa', $response->answers[0]->name);
  610. $this->assertSame(Message::TYPE_PTR, $response->answers[0]->type);
  611. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  612. $this->assertSame(86399, $response->answers[0]->ttl);
  613. $this->assertSame('google-public-dns-b.google.com', $response->answers[0]->data);
  614. }
  615. public function testParsePTRResponseWithSpecialCharactersEscaped()
  616. {
  617. $data = "";
  618. $data .= "5d d8 81 80 00 01 00 01 00 00 00 00"; // header
  619. $data .= "08 5f 70 72 69 6e 74 65 72 04 5f 74 63 70 06 64 6e 73 2d 73 64 03 6f 72 67 00"; // question: _printer._tcp.dns-sd.org
  620. $data .= "00 0c 00 01"; // question: type PTR, class IN
  621. $data .= "c0 0c"; // answer: offset pointer to rdata
  622. $data .= "00 0c 00 01"; // answer: type PTR, class IN
  623. $data .= "00 01 51 7f"; // answer: ttl 86399
  624. $data .= "00 17"; // answer: rdlength 23
  625. $data .= "14 33 72 64 2e 20 46 6c 6f 6f 72 20 43 6f 70 79 20 52 6f 6f 6d"; // answer: rdata "3rd. Floor Copy Room" …
  626. $data .= "c0 0c"; // answer: offset pointer to rdata
  627. $data = $this->convertTcpDumpToBinary($data);
  628. $response = $this->parser->parseMessage($data);
  629. $this->assertCount(1, $response->questions);
  630. $this->assertSame('_printer._tcp.dns-sd.org', $response->questions[0]->name);
  631. $this->assertSame(Message::TYPE_PTR, $response->questions[0]->type);
  632. $this->assertSame(Message::CLASS_IN, $response->questions[0]->class);
  633. $this->assertCount(1, $response->answers);
  634. $this->assertSame('_printer._tcp.dns-sd.org', $response->answers[0]->name);
  635. $this->assertSame(Message::TYPE_PTR, $response->answers[0]->type);
  636. $this->assertSame(Message::CLASS_IN, $response->answers[0]->class);
  637. $this->assertSame(86399, $response->answers[0]->ttl);
  638. $this->assertSame('3rd\.\ Floor\ Copy\ Room._printer._tcp.dns-sd.org', $response->answers[0]->data);
  639. }
  640. public function testParseIncompleteQuestionThrows()
  641. {
  642. $data = "";
  643. $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
  644. $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
  645. //$data .= "00 01 00 01"; // question: type A, class IN
  646. $data = $this->convertTcpDumpToBinary($data);
  647. $this->setExpectedException('InvalidArgumentException');
  648. $this->parser->parseMessage($data);
  649. }
  650. public function testParseIncompleteQuestionLabelThrows()
  651. {
  652. $data = "";
  653. $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
  654. $data .= "04 69 67"; // question: ig …?
  655. $data = $this->convertTcpDumpToBinary($data);
  656. $this->setExpectedException('InvalidArgumentException');
  657. $this->parser->parseMessage($data);
  658. }
  659. public function testParseIncompleteQuestionNameThrows()
  660. {
  661. $data = "";
  662. $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
  663. $data .= "04 69 67 6f 72"; // question: igor. …?
  664. $data = $this->convertTcpDumpToBinary($data);
  665. $this->setExpectedException('InvalidArgumentException');
  666. $this->parser->parseMessage($data);
  667. }
  668. public function testParseIncompleteOffsetPointerInQuestionNameThrows()
  669. {
  670. $data = "";
  671. $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
  672. $data .= "ff"; // question: incomplete offset pointer
  673. $data = $this->convertTcpDumpToBinary($data);
  674. $this->setExpectedException('InvalidArgumentException');
  675. $this->parser->parseMessage($data);
  676. }
  677. public function testParseInvalidOffsetPointerInQuestionNameThrows()
  678. {
  679. $data = "";
  680. $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
  681. $data .= "ff ff"; // question: offset pointer to invalid address
  682. $data = $this->convertTcpDumpToBinary($data);
  683. $this->setExpectedException('InvalidArgumentException');
  684. $this->parser->parseMessage($data);
  685. }
  686. public function testParseInvalidOffsetPointerToSameLabelInQuestionNameThrows()
  687. {
  688. $data = "";
  689. $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
  690. $data .= "c0 0c"; // question: offset pointer to invalid address
  691. $data = $this->convertTcpDumpToBinary($data);
  692. $this->setExpectedException('InvalidArgumentException');
  693. $this->parser->parseMessage($data);
  694. }
  695. public function testParseInvalidOffsetPointerToPreviousLabelInQuestionNameThrows()
  696. {
  697. $data = "";
  698. $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
  699. $data .= "02 69 6f c0 0c"; // question: offset pointer to previous label
  700. $data = $this->convertTcpDumpToBinary($data);
  701. $this->setExpectedException('InvalidArgumentException');
  702. $this->parser->parseMessage($data);
  703. }
  704. public function testParseInvalidOffsetPointerToStartOfMessageInQuestionNameThrows()
  705. {
  706. $data = "";
  707. $data .= "72 62 01 00 00 01 00 00 00 00 00 00"; // header
  708. $data .= "c0 00"; // question: offset pointer to start of message
  709. $data = $this->convertTcpDumpToBinary($data);
  710. $this->setExpectedException('InvalidArgumentException');
  711. $this->parser->parseMessage($data);
  712. }
  713. public function testParseIncompleteAnswerFieldsThrows()
  714. {
  715. $data = "";
  716. $data .= "72 62 81 80 00 01 00 01 00 00 00 00"; // header
  717. $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
  718. $data .= "00 01 00 01"; // question: type A, class IN
  719. $data .= "c0 0c"; // answer: offset pointer to igor.io
  720. $data = $this->convertTcpDumpToBinary($data);
  721. $this->setExpectedException('InvalidArgumentException');
  722. $this->parser->parseMessage($data);
  723. }
  724. public function testParseMessageResponseWithIncompleteAuthorityRecordThrows()
  725. {
  726. $data = "";
  727. $data .= "72 62 81 80 00 01 00 00 00 01 00 00"; // header
  728. $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
  729. $data .= "00 01 00 01"; // question: type A, class IN
  730. $data .= "c0 0c"; // authority: offset pointer to igor.io
  731. $data = $this->convertTcpDumpToBinary($data);
  732. $this->setExpectedException('InvalidArgumentException');
  733. $this->parser->parseMessage($data);
  734. }
  735. public function testParseMessageResponseWithIncompleteAdditionalRecordThrows()
  736. {
  737. $data = "";
  738. $data .= "72 62 81 80 00 01 00 00 00 00 00 01"; // header
  739. $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
  740. $data .= "00 01 00 01"; // question: type A, class IN
  741. $data .= "c0 0c"; // additional: offset pointer to igor.io
  742. $data = $this->convertTcpDumpToBinary($data);
  743. $this->setExpectedException('InvalidArgumentException');
  744. $this->parser->parseMessage($data);
  745. }
  746. public function testParseIncompleteAnswerRecordDataThrows()
  747. {
  748. $data = "";
  749. $data .= "72 62 81 80 00 01 00 01 00 00 00 00"; // header
  750. $data .= "04 69 67 6f 72 02 69 6f 00"; // question: igor.io
  751. $data .= "00 01 00 01"; // question: type A, class IN
  752. $data .= "c0 0c"; // answer: offset pointer to igor.io
  753. $data .= "00 01 00 01"; // answer: type A, class IN
  754. $data .= "00 01 51 80"; // answer: ttl 86400
  755. $data .= "00 04"; // answer: rdlength 4
  756. $data = $this->convertTcpDumpToBinary($data);
  757. $this->setExpectedException('InvalidArgumentException');
  758. $this->parser->parseMessage($data);
  759. }
  760. public function testParseInvalidNSResponseWhereDomainNameIsMissing()
  761. {
  762. $data = "";
  763. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  764. $data .= "00 02 00 01"; // answer: type NS, class IN
  765. $data .= "00 01 51 80"; // answer: ttl 86400
  766. $data .= "00 00"; // answer: rdlength 0
  767. $this->setExpectedException('InvalidArgumentException');
  768. $this->parseAnswer($data);
  769. }
  770. public function testParseInvalidAResponseWhereIPIsMissing()
  771. {
  772. $data = "";
  773. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  774. $data .= "00 01 00 01"; // answer: type A, class IN
  775. $data .= "00 01 51 80"; // answer: ttl 86400
  776. $data .= "00 00"; // answer: rdlength 0
  777. $this->setExpectedException('InvalidArgumentException');
  778. $this->parseAnswer($data);
  779. }
  780. public function testParseInvalidAAAAResponseWhereIPIsMissing()
  781. {
  782. $data = "";
  783. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  784. $data .= "00 1c 00 01"; // answer: type AAAA, class IN
  785. $data .= "00 01 51 80"; // answer: ttl 86400
  786. $data .= "00 00"; // answer: rdlength 0
  787. $this->setExpectedException('InvalidArgumentException');
  788. $this->parseAnswer($data);
  789. }
  790. public function testParseInvalidTXTResponseWhereTxtChunkExceedsLimit()
  791. {
  792. $data = "";
  793. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  794. $data .= "00 10 00 01"; // answer: type TXT, class IN
  795. $data .= "00 01 51 80"; // answer: ttl 86400
  796. $data .= "00 06"; // answer: rdlength 6
  797. $data .= "06 68 65 6c 6c 6f 6f"; // answer: rdata length 6: helloo
  798. $this->setExpectedException('InvalidArgumentException');
  799. $this->parseAnswer($data);
  800. }
  801. public function testParseInvalidMXResponseWhereDomainNameIsIncomplete()
  802. {
  803. $data = "";
  804. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  805. $data .= "00 0f 00 01"; // answer: type MX, class IN
  806. $data .= "00 01 51 80"; // answer: ttl 86400
  807. $data .= "00 08"; // answer: rdlength 8
  808. $data .= "00 0a 05 68 65 6c 6c 6f"; // answer: rdata priority 10: hello (missing label end)
  809. $this->setExpectedException('InvalidArgumentException');
  810. $this->parseAnswer($data);
  811. }
  812. public function testParseInvalidMXResponseWhereDomainNameIsMissing()
  813. {
  814. $data = "";
  815. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  816. $data .= "00 0f 00 01"; // answer: type MX, class IN
  817. $data .= "00 01 51 80"; // answer: ttl 86400
  818. $data .= "00 02"; // answer: rdlength 2
  819. $data .= "00 0a"; // answer: rdata priority 10
  820. $this->setExpectedException('InvalidArgumentException');
  821. $this->parseAnswer($data);
  822. }
  823. public function testParseInvalidSRVResponseWhereDomainNameIsIncomplete()
  824. {
  825. $data = "";
  826. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  827. $data .= "00 21 00 01"; // answer: type SRV, class IN
  828. $data .= "00 01 51 80"; // answer: ttl 86400
  829. $data .= "00 0b"; // answer: rdlength 11
  830. $data .= "00 0a 00 14 1F 90 04 74 65 73 74"; // answer: rdata priority 10, weight 20, port 8080 test (missing label end)
  831. $this->setExpectedException('InvalidArgumentException');
  832. $this->parseAnswer($data);
  833. }
  834. public function testParseInvalidSRVResponseWhereDomainNameIsMissing()
  835. {
  836. $data = "";
  837. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  838. $data .= "00 21 00 01"; // answer: type SRV, class IN
  839. $data .= "00 01 51 80"; // answer: ttl 86400
  840. $data .= "00 06"; // answer: rdlength 6
  841. $data .= "00 0a 00 14 1F 90"; // answer: rdata priority 10, weight 20, port 8080
  842. $this->setExpectedException('InvalidArgumentException');
  843. $this->parseAnswer($data);
  844. }
  845. public function testParseInvalidSSHFPResponseWhereRecordIsTooSmall()
  846. {
  847. $data = "";
  848. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  849. $data .= "00 2c 00 01"; // answer: type SSHFP, class IN
  850. $data .= "00 01 51 80"; // answer: ttl 86400
  851. $data .= "00 02"; // answer: rdlength 2
  852. $data .= "01 01"; // answer: algorithm 1 (RSA), type 1 (SHA), missing fingerprint
  853. $this->setExpectedException('InvalidArgumentException');
  854. $this->parseAnswer($data);
  855. }
  856. public function testParseInvalidOPTResponseWhereRecordIsTooSmall()
  857. {
  858. $data = "";
  859. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  860. $data .= "00 29 03 e8"; // answer: type OPT, 1000 bytes max size
  861. $data .= "00 00 00 00"; // answer: ttl 0
  862. $data .= "00 03"; // answer: rdlength 3
  863. $data .= "00 00 00"; // answer: type 0, length incomplete
  864. $this->setExpectedException('InvalidArgumentException');
  865. $this->parseAnswer($data);
  866. }
  867. public function testParseInvalidOPTResponseWhereRecordLengthDoesNotMatchOptType()
  868. {
  869. $data = "";
  870. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  871. $data .= "00 29 03 e8"; // answer: type OPT, 1000 bytes max size
  872. $data .= "00 00 00 00"; // answer: ttl 0
  873. $data .= "00 07"; // answer: rdlength 7
  874. $data .= "00 0b 00 03 01 02 03"; // answer: type OPT_TCP_KEEPALIVE, length 3 instead of 2
  875. $this->setExpectedException('InvalidArgumentException');
  876. $this->parseAnswer($data);
  877. }
  878. public function testParseInvalidSOAResponseWhereFlagsAreMissing()
  879. {
  880. $data = "";
  881. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  882. $data .= "00 06 00 01"; // answer: type SOA, class IN
  883. $data .= "00 01 51 80"; // answer: ttl 86400
  884. $data .= "00 13"; // answer: rdlength 19
  885. $data .= "02 6e 73 05 68 65 6c 6c 6f 00"; // answer: rdata ns.hello (mname)
  886. $data .= "01 65 05 68 65 6c 6c 6f 00"; // answer: rdata e.hello (rname)
  887. $this->setExpectedException('InvalidArgumentException');
  888. $this->parseAnswer($data);
  889. }
  890. public function testParseInvalidCAAResponseEmtpyData()
  891. {
  892. $data = "";
  893. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  894. $data .= "01 01 00 01"; // answer: type CAA, class IN
  895. $data .= "00 01 51 80"; // answer: ttl 86400
  896. $data .= "00 00"; // answer: rdlength 0
  897. $this->setExpectedException('InvalidArgumentException');
  898. $this->parseAnswer($data);
  899. }
  900. public function testParseInvalidCAAResponseMissingValue()
  901. {
  902. $data = "";
  903. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  904. $data .= "01 01 00 01"; // answer: type CAA, class IN
  905. $data .= "00 01 51 80"; // answer: ttl 86400
  906. $data .= "00 07"; // answer: rdlength 22
  907. $data .= "00 05 69 73 73 75 65"; // answer: rdata 0, issue
  908. $this->setExpectedException('InvalidArgumentException');
  909. $this->parseAnswer($data);
  910. }
  911. public function testParseInvalidCAAResponseIncompleteTag()
  912. {
  913. $data = "";
  914. $data .= "04 69 67 6f 72 02 69 6f 00"; // answer: igor.io
  915. $data .= "01 01 00 01"; // answer: type CAA, class IN
  916. $data .= "00 01 51 80"; // answer: ttl 86400
  917. $data .= "00 0c"; // answer: rdlength 22
  918. $data .= "00 ff 69 73 73 75 65"; // answer: rdata 0, issue (incomplete due to invalid tag length)
  919. $data .= "68 65 6c 6c 6f"; // answer: hello
  920. $this->setExpectedException('InvalidArgumentException');
  921. $this->parseAnswer($data);
  922. }
  923. private function convertTcpDumpToBinary($input)
  924. {
  925. // sudo ngrep -d en1 -x port 53
  926. return pack('H*', str_replace(' ', '', $input));
  927. }
  928. private function parseAnswer($answerData)
  929. {
  930. $data = "72 62 81 80 00 00 00 01 00 00 00 00"; // header with one answer only
  931. $data .= $answerData;
  932. $data = $this->convertTcpDumpToBinary($data);
  933. return $this->parser->parseMessage($data);
  934. }
  935. }