PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/code/classes/Daemon/DNSd/Packet.class.php

https://github.com/blekkzor/pinetd2
PHP | 302 lines | 238 code | 49 blank | 15 comment | 26 complexity | 7a0628b80bc4cb7b6089ad24aa7edab7 MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. namespace Daemon\DNSd;
  3. class Packet {
  4. const RCODE_FORMERR = 1; /*!< Format error (parser) */
  5. const RCODE_SERVFAIL = 2; /*!< Internal problem */
  6. const RCODE_NXDOMAIN = 3; /*!< No authority over provided domain */
  7. const RCODE_NOTIMP = 4;
  8. const RCODE_REFUSED = 5;
  9. const RCODE_YXDOMAIN = 6;
  10. const RCODE_YXRRSET = 7;
  11. const RCODE_NXRRSET = 8;
  12. const RCODE_NOTAUTH = 9;
  13. const RCODE_NOTZONE = 10;
  14. protected $packet_id = NULL;
  15. protected $flags = array();
  16. protected $question = array();
  17. protected $answer = array();
  18. protected $authority = array();
  19. protected $additional = array();
  20. protected $defaultDomain = '.';
  21. protected $_label_cache = array();
  22. protected $peer;
  23. public function __construct($peer) {
  24. $this->peer = $peer;
  25. }
  26. public function getPeer() {
  27. return $this->peer;
  28. }
  29. public function decode($pkt) {
  30. // unpack packet's header
  31. $data = unpack('npacket_id/nflags/nqdcount/nancount/nnscount/narcount', $pkt);
  32. $this->packet_id = $data['packet_id'];
  33. // decode flags
  34. $this->flags = $this->decodeFlags($data['flags']);
  35. $offset = 12; // just after "question"
  36. $this->question = $this->decodeQuestionRR($pkt, $offset, $data['qdcount']);
  37. $this->answer = $this->decodeRR($pkt, $offset, $data['ancount']);
  38. $this->authority = $this->decodeRR($pkt, $offset, $data['nscount']);
  39. $this->additional = $this->decodeRR($pkt, $offset, $data['arcount']);
  40. return true;
  41. }
  42. public function setDefaultDomain($domain = '.') {
  43. if (substr($domain, -1) != '.') $domain .= '.';
  44. $this->defaultDomain = $domain;
  45. }
  46. public function resetAnswer() {
  47. $this->answer = array();
  48. $this->authority = array();
  49. }
  50. public function hasAnswer() {
  51. return (bool)$this->answer;
  52. }
  53. public function hasAuthority() {
  54. return (bool)$this->authority;
  55. }
  56. public function encode() {
  57. $qdcount = count($this->question);
  58. $ancount = count($this->answer);
  59. $nscount = count($this->authority);
  60. $arcount = count($this->additional);
  61. $pkt = pack('nnnnnn', $this->packet_id, $this->encodeFlags($this->flags), $qdcount, $ancount, $nscount, $arcount);
  62. // Reset label compression cache
  63. $this->_label_cache = array();
  64. // encode question
  65. $pkt .= $this->encodeQuestionRR($this->question, strlen($pkt));
  66. // encode other fields
  67. $pkt .= $this->encodeRR($this->answer, strlen($pkt));
  68. $pkt .= $this->encodeRR($this->authority, strlen($pkt));
  69. $pkt .= $this->encodeRR($this->additional, strlen($pkt));
  70. return $pkt;
  71. }
  72. public function getQuestions() {
  73. return $this->question;
  74. }
  75. public function addAnswer($name, $value, $ttl = 86400, $class = 1) { // no const here, class 1 = IN
  76. $this->answer[] = array(
  77. 'name' => $name,
  78. 'class' => $class,
  79. 'ttl' => $ttl,
  80. 'data' => $value
  81. );
  82. }
  83. public function addAuthority($name, $value, $ttl = 86400, $class = 1) { // no const here, class 1 = IN
  84. $this->authority[] = array(
  85. 'name' => $name,
  86. 'class' => $class,
  87. 'ttl' => $ttl,
  88. 'data' => $value
  89. );
  90. }
  91. public function addAdditional($name, $value, $ttl = 86400, $class = 1) { // no const here, class 1 = IN
  92. $this->additional[] = array(
  93. 'name' => $name,
  94. 'class' => $class,
  95. 'ttl' => $ttl,
  96. 'data' => $value
  97. );
  98. }
  99. public function setFlag($flag, $value) {
  100. $this->flags[$flag] = $value;
  101. return true;
  102. }
  103. public function decodeLabel($pkt, &$offset) {
  104. $end_offset = NULL;
  105. $qname = '';
  106. while(1) {
  107. $len = ord($pkt[$offset]);
  108. $type = $len >> 6 & 0x2;
  109. if ($type) {
  110. switch($type) {
  111. case 0x2: // "DNS PACKET COMPRESSION", RFC 1035
  112. // switch to a different offset, but keep this one as "end of packet"
  113. $new_offset = unpack('noffset', substr($pkt, $offset, 2));
  114. $end_offset = $offset+2;
  115. $offset = $new_offset['offset'] & 0x3fff;
  116. break;
  117. case 0x1: // Extended label, RFC 2671
  118. break;
  119. }
  120. continue;
  121. }
  122. if ($len > (strlen($pkt) - $offset)) return NULL; // ouch! parse error!!
  123. if ($len == 0) {
  124. if ($qname == '') $qname = '.';
  125. ++$offset;
  126. break;
  127. }
  128. $qname .= substr($pkt, $offset+1, $len).'.';
  129. $offset += $len + 1;
  130. }
  131. if (!is_null($end_offset)) {
  132. $offset = $end_offset;
  133. }
  134. return $qname;
  135. }
  136. public function encodeLabel($str, $offset = NULL) {
  137. // encode a label :)
  138. $res = '';
  139. $in_offset = 0;
  140. if ($str == '.') {
  141. // special case: root label. Avoid looking up compression cache and stuff...
  142. return "\0";
  143. }
  144. if (substr($str, -1) != '.')
  145. $str .= '.' . $this->defaultDomain;
  146. while(1) {
  147. $pos = strpos($str, '.', $in_offset);
  148. if ($pos === false) { // end of string ?!
  149. return $res . "\0";
  150. }
  151. // did we cache?
  152. if (!is_null($offset)) {
  153. if (isset($this->_label_cache[strtolower(substr($str, $in_offset))])) {
  154. $code = (0x3 << 14) | $this->_label_cache[strtolower(substr($str, $in_offset))];
  155. return $res . pack('n', $code);
  156. }
  157. if ($offset < 0x3fff) $this->_label_cache[strtolower(substr($str, $in_offset))] = $offset;
  158. }
  159. $res .= chr($pos - $in_offset) . substr($str, $in_offset, $pos - $in_offset);
  160. $offset += ($pos - $in_offset) + 1;
  161. $in_offset = $pos + 1;
  162. }
  163. }
  164. protected function encodeRR($list, $offset) {
  165. $res = '';
  166. foreach($list as $rr) {
  167. $lbl = $this->encodeLabel($rr['name'], $offset);
  168. $res .= $lbl;
  169. $offset += strlen($lbl);
  170. if (!is_object($rr['data'])) {
  171. return false;
  172. }
  173. $offset += 10;
  174. $data = $rr['data']->encode(NULL, $offset);
  175. if (is_array($data)) {
  176. // overloading written data
  177. if (!isset($data['type'])) $data['type'] = $rr['data']->getType();
  178. if (!isset($data['data'])) $data['data'] = '';
  179. if (!isset($data['class'])) $data['class'] = $rr['class'];
  180. if (!isset($data['ttl'])) $data['ttl'] = $rr['ttl'];
  181. $offset += strlen($data['data']);
  182. $res .= pack('nnNn', $data['type'], $data['class'], $data['ttl'], strlen($data['data'])) . $data['data'];
  183. } else {
  184. $offset += strlen($data);
  185. $res .= pack('nnNn', $rr['data']->getType(), $rr['class'], $rr['ttl'], strlen($data)) . $data;
  186. }
  187. }
  188. return $res;
  189. }
  190. protected function encodeQuestionRR($list, $offset) {
  191. $res = '';
  192. foreach($list as $rr) {
  193. // qname, qtype & qclass
  194. $lbl = $this->encodeLabel($rr['qname'], $offset);
  195. $offset += strlen($lbl) + 4;
  196. $res .= $lbl;
  197. $res .= pack('nn', $rr['qtype'], $rr['qclass']);
  198. }
  199. return $res;
  200. }
  201. protected function encodeFlags(array $flags) {
  202. $val = 0;
  203. $val |= ($flags['qr'] & 0x1) << 15;
  204. $val |= ($flags['opcode'] & 0xf) << 11;
  205. $val |= ($flags['aa'] & 0x1) << 10;
  206. $val |= ($flags['tc'] & 0x1) << 9;
  207. $val |= ($flags['rd'] & 0x1) << 8;
  208. $val |= ($flags['ra'] & 0x1) << 7;
  209. $val |= ($flags['z'] & 0x7) << 4;
  210. $val |= ($flags['rcode'] & 0xf);
  211. return $val;
  212. }
  213. protected function decodeFlags($flags) {
  214. $res = array();
  215. $res['qr'] = $flags >> 15 & 0x1;
  216. $res['opcode'] = $flags >> 11 & 0xf;
  217. $res['aa'] = $flags >> 10 & 0x1;
  218. $res['tc'] = $flags >> 9 & 0x1;
  219. $res['rd'] = $flags >> 8 & 0x1;
  220. $res['ra'] = $flags >> 7 & 0x1;
  221. $res['z'] = $flags >> 4 & 0x7; // Reserved for future use (should be zero)
  222. $res['rcode'] = $flags & 0xf;
  223. return $res;
  224. }
  225. protected function decodeRR($pkt, &$offset, $count) {
  226. $res = array();
  227. for($i = 0; $i < $count; ++$i) {
  228. // read qname
  229. $qname = $this->decodeLabel($pkt, $offset);
  230. // read qtype & qclass
  231. $tmp = unpack('ntype/nclass/Nttl/ndlength', substr($pkt, $offset, 10));
  232. $tmp['name'] = $qname;
  233. $offset += 10;
  234. $tmp['data'] = Type::decode($this, $tmp, $tmp['type'], substr($pkt, $offset, $tmp['dlength']));
  235. $offset += $tmp['dlength'];
  236. $res[] = $tmp;
  237. }
  238. return $res;
  239. }
  240. protected function decodeQuestionRR($pkt, &$offset, $count) {
  241. $res = array();
  242. for($i = 0; $i < $count; ++$i) {
  243. // read qname
  244. $qname = $this->decodeLabel($pkt, $offset);
  245. // read qtype & qclass
  246. $tmp = unpack('nqtype/nqclass', substr($pkt, $offset, 4));
  247. $offset += 4;
  248. $tmp['qname'] = $qname;
  249. $res[] = $tmp;
  250. }
  251. return $res;
  252. }
  253. }