1<?php 2 3/** 4 * DNS Library for handling lookups and updates. 5 * 6 * Copyright (c) 2020, Mike Pultz <mike@mikepultz.com>. All rights reserved. 7 * 8 * See LICENSE for more details. 9 * 10 * @category Networking 11 * @package Net_DNS2 12 * @author Mike Pultz <mike@mikepultz.com> 13 * @copyright 2020 Mike Pultz <mike@mikepultz.com> 14 * @license http://www.opensource.org/licenses/bsd-license.php BSD License 15 * @link https://netdns2.com/ 16 * @since File available since Release 0.6.0 17 * 18 */ 19 20/** 21 * This is the main resolver class, providing DNS query functions. 22 * 23 */ 24class Net_DNS2_Resolver extends Net_DNS2 25{ 26 /** 27 * Constructor - creates a new Net_DNS2_Resolver object 28 * 29 * @param mixed $options either an array with options or null 30 * 31 * @access public 32 * 33 */ 34 public function __construct(array $options = null) 35 { 36 parent::__construct($options); 37 } 38 39 /** 40 * does a basic DNS lookup query 41 * 42 * @param string $name the DNS name to loookup 43 * @param string $type the name of the RR type to lookup 44 * @param string $class the name of the RR class to lookup 45 * 46 * @return Net_DNS2_Packet_Response object 47 * @throws Net_DNS2_Exception 48 * @access public 49 * 50 */ 51 public function query($name, $type = 'A', $class = 'IN') 52 { 53 // 54 // make sure we have some name servers set 55 // 56 $this->checkServers(Net_DNS2::RESOLV_CONF); 57 58 // 59 // we dont' support incremental zone tranfers; so if it's requested, a full 60 // zone transfer can be returned 61 // 62 if ($type == 'IXFR') { 63 64 $type = 'AXFR'; 65 } 66 67 // 68 // if the name *looks* too short, then append the domain from the config 69 // 70 if ( (strpos($name, '.') === false) && ($type != 'PTR') ) { 71 72 $name .= '.' . strtolower($this->domain); 73 } 74 75 // 76 // create a new packet based on the input 77 // 78 $packet = new Net_DNS2_Packet_Request($name, $type, $class); 79 80 // 81 // check for an authentication method; either TSIG or SIG 82 // 83 if ( ($this->auth_signature instanceof Net_DNS2_RR_TSIG) 84 || ($this->auth_signature instanceof Net_DNS2_RR_SIG) 85 ) { 86 $packet->additional[] = $this->auth_signature; 87 $packet->header->arcount = count($packet->additional); 88 } 89 90 // 91 // check for the DNSSEC flag, and if it's true, then add an OPT 92 // RR to the additional section, and set the DO flag to 1. 93 // 94 if ($this->dnssec == true) { 95 96 // 97 // create a new OPT RR 98 // 99 $opt = new Net_DNS2_RR_OPT(); 100 101 // 102 // set the DO flag, and the other values 103 // 104 $opt->do = 1; 105 $opt->class = $this->dnssec_payload_size; 106 107 // 108 // add the RR to the additional section. 109 // 110 $packet->additional[] = $opt; 111 $packet->header->arcount = count($packet->additional); 112 } 113 114 // 115 // set the DNSSEC AD or CD bits 116 // 117 if ($this->dnssec_ad_flag == true) { 118 119 $packet->header->ad = 1; 120 } 121 if ($this->dnssec_cd_flag == true) { 122 123 $packet->header->cd = 1; 124 } 125 126 // 127 // if caching is turned on, then check then hash the question, and 128 // do a cache lookup. 129 // 130 // don't use the cache for zone transfers 131 // 132 $packet_hash = ''; 133 134 if ( ($this->use_cache == true) && ($this->cacheable($type) == true) ) { 135 136 // 137 // open the cache 138 // 139 $this->cache->open( 140 $this->cache_file, $this->cache_size, $this->cache_serializer 141 ); 142 143 // 144 // build the key and check for it in the cache. 145 // 146 $packet_hash = md5( 147 $packet->question[0]->qname . '|' . $packet->question[0]->qtype 148 ); 149 150 if ($this->cache->has($packet_hash)) { 151 152 return $this->cache->get($packet_hash); 153 } 154 } 155 156 // 157 // set the RD (recursion desired) bit to 1 / 0 depending on the config 158 // setting. 159 // 160 if ($this->recurse == false) { 161 $packet->header->rd = 0; 162 } else { 163 $packet->header->rd = 1; 164 } 165 166 // 167 // send the packet and get back the response 168 // 169 // *always* use TCP for zone transfers- does this cause any problems? 170 // 171 $response = $this->sendPacket( 172 $packet, ($type == 'AXFR') ? true : $this->use_tcp 173 ); 174 175 // 176 // if strict mode is enabled, then make sure that the name that was 177 // looked up is *actually* in the response object. 178 // 179 // only do this is strict_query_mode is turned on, AND we've received 180 // some answers; no point doing any else if there were no answers. 181 // 182 if ( ($this->strict_query_mode == true) 183 && ($response->header->ancount > 0) 184 ) { 185 186 $found = false; 187 188 // 189 // look for the requested name/type/class 190 // 191 foreach ($response->answer as $index => $object) { 192 193 if ( (strcasecmp(trim($object->name, '.'), trim($packet->question[0]->qname, '.')) == 0) 194 && ($object->type == $packet->question[0]->qtype) 195 && ($object->class == $packet->question[0]->qclass) 196 ) { 197 $found = true; 198 break; 199 } 200 } 201 202 // 203 // if it's not found, then unset the answer section; it's not correct to 204 // throw an exception here; if the hostname didn't exist, then 205 // sendPacket() would have already thrown an NXDOMAIN error- so the host 206 // *exists*, but just not the request type/class. 207 // 208 // the correct response in this case, is an empty answer section; the 209 // authority section may still have usual information, like a SOA record. 210 // 211 if ($found == false) { 212 213 $response->answer = []; 214 $response->header->ancount = 0; 215 } 216 } 217 218 // 219 // cache the response object 220 // 221 if ( ($this->use_cache == true) && ($this->cacheable($type) == true) ) { 222 223 $this->cache->put($packet_hash, $response); 224 } 225 226 return $response; 227 } 228 229 /** 230 * does an inverse query for the given RR; most DNS servers do not implement 231 * inverse queries, but they should be able to return "not implemented" 232 * 233 * @param Net_DNS2_RR $rr the RR object to lookup 234 * 235 * @return Net_DNS2_RR object 236 * @throws Net_DNS2_Exception 237 * @access public 238 * 239 */ 240 public function iquery(Net_DNS2_RR $rr) 241 { 242 // 243 // make sure we have some name servers set 244 // 245 $this->checkServers(Net_DNS2::RESOLV_CONF); 246 247 // 248 // create an empty packet 249 // 250 $packet = new Net_DNS2_Packet_Request($rr->name, 'A', 'IN'); 251 252 // 253 // unset the question 254 // 255 $packet->question = []; 256 $packet->header->qdcount = 0; 257 258 // 259 // set the opcode to IQUERY 260 // 261 $packet->header->opcode = Net_DNS2_Lookups::OPCODE_IQUERY; 262 263 // 264 // add the given RR as the answer 265 // 266 $packet->answer[] = $rr; 267 $packet->header->ancount = 1; 268 269 // 270 // check for an authentication method; either TSIG or SIG 271 // 272 if ( ($this->auth_signature instanceof Net_DNS2_RR_TSIG) 273 || ($this->auth_signature instanceof Net_DNS2_RR_SIG) 274 ) { 275 $packet->additional[] = $this->auth_signature; 276 $packet->header->arcount = count($packet->additional); 277 } 278 279 // 280 // send the packet and get back the response 281 // 282 return $this->sendPacket($packet, $this->use_tcp); 283 } 284} 285