Path: blob/master/lib/rex/proto/dns/cache.rb
19500 views
# -*- coding: binary -*-12require 'rex/socket'34module Rex5module Proto6module DNS7class Cache8attr_reader :records, :lock, :monitor_thread9include Rex::Proto::DNS::Constants10# class DNSRecordError < ::Exception11#12# Create DNS cache13#14def initialize15@records = {}16@lock = Mutex.new17end1819#20# Find entries in cache, substituting names for '*' in return21#22# @param search [String] Name or address to search for23# @param type [Dnsruby::Types] Record type to search for24#25# @return [Array] Records found26def find(search, type = Dnsruby::Types::A)27self.records.select do |record,expire|28record.type == type and (expire < 1 or expire > ::Time.now.to_i) and29(30record.name == '*' or31record.name.to_s == search.to_s or record.name.to_s[0..-2] == search.to_s or32( record.respond_to?(:address) and record.address.to_s == search.to_s )33)34end.keys.map do |record|35if search.to_s.match(MATCH_HOSTNAME) and record.name == '*'36record = Dnsruby::RR.create(name: name, type: type, address: address)37else38record39end40end41end4243#44# Add record to cache, only when "running"45#46# @param record [Dnsruby::RR] Record to cache47def cache_record(record, expire: true)48return unless @monitor_thread4950unless record.is_a?(Dnsruby::RR)51raise "Invalid record for cache entry (not an RR) - #{record.inspect}"52end5354unless (!record.respond_to?(:address) || Rex::Socket.is_ip_addr?(record.address.to_s))55raise "Invalid record for cache entry (no IP address) - #{record.inspect}"56end5758unless record.name.to_s.match(MATCH_HOSTNAME)59raise "Invalid record for cache entry (invalid hostname) - #{record.inspect}"60end6162add(record, expire ? (::Time.now.to_i + record.ttl) : 0)63end6465#66# Delete all cache entries, this is different from pruning because the67# record's expiration is ignored68#69def flush70self.records.each {|rec, _| delete(rec)}71end7273#74# Prune cache entries75#76# @param before [Fixnum] Time in seconds before which records are evicted77def prune(before = ::Time.now.to_i)78self.records.select do |rec, expire|79expire > 0 and expire < before80end.each {|rec, exp| delete(rec)}81end8283#84# Start the cache monitor85#86def start87@monitor_thread = Rex::ThreadFactory.spawn("DNSServerCacheMonitor", false) {88while true89prune90Rex::ThreadSafe.sleep(0.5)91end92} unless @monitor_thread93end9495#96# Stop the cache monitor97#98# @param flush [TrueClass,FalseClass] Remove non-static entries99def stop(flush = false)100self.monitor_thread.kill unless @monitor_thread.nil?101@monitor_thread = nil102if flush103self.records.select do |rec, expire|104rec.ttl > 0105end.each {|rec| delete(rec)}106end107end108109protected110111#112# Add a record to the cache with thread safety113#114# @param record [Dnsruby::RR] Record to add115# @param expire [Fixnum] Time in seconds when record becomes stale116def add(record, expire = 0)117self.lock.synchronize do118self.records[record] = expire119end120end121122#123# Delete a record from the cache with thread safety124#125# @param record [Dnsruby::RR] Record to delete126def delete(record)127self.lock.synchronize do128self.records.delete(record)129end130end131end # Cache132end133end134end135136137