/** * @param string $question * @param string $type * @return DNSAnswer|false */ public function query($question, $type = 'A') { $this->clearError(); $typeid = $this->types->getByName($type); if ($typeid === false) { $this->setError('Invalid Query Type ' . $type); return false; } if ($this->udp) { $host = 'udp://' . $this->server; } else { $host = $this->server; } $errno = 0; $errstr = ''; if (!($socket = fsockopen($host, $this->port, $errno, $errstr, $this->timeout))) { $this->setError('Failed to Open Socket'); return false; } // handles timeout on stream read set using timeout as well stream_set_timeout($socket, $this->timeout); // Split Into Labels if (preg_match('/[a-z|A-Z]/', $question) == 0 && $question != '.') { // IP Address // reverse ARPA format $labels = array_reverse(explode('.', $question)); $labels[] = 'IN-ADDR'; $labels[] = 'ARPA'; } else { if ($question == '.') { $labels = array(''); } else { // hostname $labels = explode('.', $question); } } $question_binary = ''; foreach ($labels as $label) { if ($label != '') { $size = strlen($label); $question_binary .= pack('C', $size); // size byte first $question_binary .= $label; // then the label } else { //$size = 0; //$question_binary.=pack('C',$size); //$question_binary.=pack('C',$labels[$a]); } } $question_binary .= pack('C', 0); // end it off $this->debug('Question: ' . $question . ' (type=' . $type . '/' . $typeid . ')'); $id = rand(1, 255) | rand(0, 255) << 8; // generate the ID // Set standard codes and flags $flags = 0x100 & 0x300; // recursion & queryspecmask $opcode = 0x0; // opcode // Build the header $header = ''; $header .= pack('n', $id); $header .= pack('n', $opcode | $flags); $header .= pack('nnnn', 1, 0, 0, 0); $header .= $question_binary; $header .= pack('n', $typeid); $header .= pack('n', 0x1); // internet class $headersize = strlen($header); $headersizebin = pack('n', $headersize); $this->debug('Header Length: ' . $headersize . ' Bytes'); $this->debugBinary($header); if ($this->udp && $headersize >= 512) { $this->setError('Question too big for UDP (' . $headersize . ' bytes)'); fclose($socket); return false; } if ($this->udp) { // UDP method if (!fwrite($socket, $header, $headersize)) { $this->setError('Failed to write question to socket'); fclose($socket); return false; } if (!($this->rawbuffer = fread($socket, 4096))) { // read until the end with UDP $this->setError('Failed to read data buffer'); fclose($socket); return false; } } else { // TCP // write the socket if (!fwrite($socket, $headersizebin)) { $this->setError('Failed to write question length to TCP socket'); fclose($socket); return false; } if (!fwrite($socket, $header, $headersize)) { $this->setError('Failed to write question to TCP socket'); fclose($socket); return false; } if (!($returnsize = fread($socket, 2))) { $this->setError('Failed to read size from TCP socket'); fclose($socket); return false; } $tmplen = unpack('nlength', $returnsize); $datasize = $tmplen['length']; $this->debug('TCP Stream Length Limit ' . $datasize); if (!($this->rawbuffer = fread($socket, $datasize))) { $this->setError('Failed to read data buffer'); fclose($socket); return false; } } fclose($socket); $buffersize = strlen($this->rawbuffer); $this->debug('Read Buffer Size ' . $buffersize); if ($buffersize < 12) { $this->setError('Return Buffer too Small'); return false; } $this->rawheader = substr($this->rawbuffer, 0, 12); // first 12 bytes is the header $this->rawresponse = substr($this->rawbuffer, 12); // after that the response $this->responsecounter = 12; // start parsing response counter from 12 - no longer using response so can do pointers $this->debugBinary($this->rawbuffer); $this->header = unpack('nid/nspec/nqdcount/nancount/nnscount/narcount', $this->rawheader); $id = $this->header['id']; $rcode = $this->header['spec'] & 15; $z = $this->header['spec'] >> 4 & 7; $ra = $this->header['spec'] >> 7 & 1; $rd = $this->header['spec'] >> 8 & 1; $tc = $this->header['spec'] >> 9 & 1; $aa = $this->header['spec'] >> 10 & 1; $opcode = $this->header['spec'] >> 11 & 15; $type = $this->header['spec'] >> 15 & 1; $this->debug("ID={$id}, Type={$type}, OPCODE={$opcode}, AA={$aa}, TC={$tc}, RD={$rd}, RA={$ra}, RCODE={$rcode}"); if ($tc == 1 && $this->udp) { // Truncation detected $this->setError('Response too big for UDP, retry with TCP'); return false; } $answers = $this->header['ancount']; $this->debug('Query Returned ' . $answers . ' Answers'); $dns_answer = new DNSAnswer(); // Deal with the header question data if ($this->header['qdcount'] > 0) { $this->debug('Found ' . $this->header['qdcount'] . ' Questions'); for ($a = 0; $a < $this->header['qdcount']; $a++) { $c = 1; while ($c != 0) { $c = hexdec(bin2hex($this->readResponse(1))); } $this->readResponse(4); } } // New Functional Method for ($a = 0; $a < $this->header['ancount']; $a++) { $record = $this->readRecord(); $dns_answer->addResult(new DNSResult($record['header']['type'], $record['typeid'], $record['header']['class'], $record['header']['ttl'], $record['data'], $record['domain'], $record['string'], $record['extras'])); } $this->lastnameservers = new DNSAnswer(); for ($a = 0; $a < $this->header['nscount']; $a++) { $record = $this->readRecord(); $this->lastnameservers->addResult(new DNSResult($record['header']['type'], $record['typeid'], $record['header']['class'], $record['header']['ttl'], $record['data'], $record['domain'], $record['string'], $record['extras'])); } $this->lastadditional = new DNSAnswer(); for ($a = 0; $a < $this->header['arcount']; $a++) { $record = $this->readRecord(); $this->lastadditional->addResult(new DNSResult($record['header']['type'], $record['typeid'], $record['header']['class'], $record['header']['ttl'], $record['data'], $record['domain'], $record['string'], $record['extras'])); } return $dns_answer; }