Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/proto/dns/server.rb
Views: 11704
# -*- coding: binary -*-12require 'rex/socket'34module Rex5module Proto6module DNS78class Server910class MockDnsClient11attr_reader :peerhost, :peerport, :srvsock1213#14# Create mock DNS client15#16# @param host [String] PeerHost IP address17# @param port [Fixnum] PeerPort integer18def initialize(host, port, sock)19@peerhost = host20@peerport = port21@srvsock = sock22end2324#25# Test method to prevent GC/ObjectSpace abuse via class lookups26#27def mock_dns_client?28true29end3031def write(data)32srvsock.sendto(data, peerhost, peerport)33end34end3536include Rex::IO::GramServer3738Packet = Rex::Proto::DNS::Packet39#40# Create DNS Server41#42# @param lhost [String] Listener address43# @param lport [Fixnum] Listener port44# @param udp [TrueClass, FalseClass] Listen on UDP socket45# @param tcp [TrueClass, FalseClass] Listen on TCP socket46# @param res [Rex::Proto::DNS::Resolver] Resolver to use, nil to create a fresh one47# @param ctx [Hash] Framework context for sockets48# @param dblock [Proc] Handler for :dispatch_request flow control interception49# @param sblock [Proc] Handler for :send_response flow control interception50#51# @return [Rex::Proto::DNS::Server] DNS Server object52attr_accessor :serve_tcp, :serve_udp, :fwd_res, :cache, :start_cache53attr_reader :serve_udp, :serve_tcp, :sock_options, :lock, :udp_sock, :tcp_sock54def initialize(lhost = '0.0.0.0', lport = 53, udp = true, tcp = false, start_cache = true, res = nil, comm = nil, ctx = {}, dblock = nil, sblock = nil)5556@serve_udp = udp57@serve_tcp = tcp58@sock_options = {59'LocalHost' => lhost,60'LocalPort' => lport,61'Context' => ctx,62'Comm' => comm63}64self.fwd_res = res.nil? ? Rex::Proto::DNS::Resolver.new(:comm => comm, :context => ctx) : res65self.listener_thread = nil66self.dispatch_request_proc = dblock67self.send_response_proc = sblock68self.start_cache = start_cache69self.cache = Rex::Proto::DNS::Cache.new70@lock = Mutex.new71end7273#74# Switch DNS forwarders in resolver with thread safety75#76# @param ns [Array, String] List of (or single) nameservers to use77def switchns(ns = [])78if ns.respond_to?(:split)79ns = [ns]80end81self.lock.synchronize do82self.fwd_res.nameserver = ns83end84end8586#87# Check if server is running88#89def running?90self.listener_thread and self.listener_thread.alive?91end9293#94# Start the DNS server and cache95# @param start_cache [TrueClass, FalseClass] stop the cache96def start9798if self.serve_udp99@udp_sock = Rex::Socket::Udp.create(self.sock_options)100self.listener_thread = Rex::ThreadFactory.spawn("UDPDNSServerListener", false) {101monitor_listener102}103end104105if self.serve_tcp106@tcp_sock = Rex::Socket::TcpServer.create(self.sock_options)107self.tcp_sock.on_client_data_proc = Proc.new { |cli|108on_client_data(cli)109}110self.tcp_sock.start111if !self.serve_udp112self.listener_thread = tcp_sock.listener_thread113end114end115116self.cache.start if self.start_cache117end118119#120# Stop the DNS server and cache121#122# @param flush_cache [TrueClass,FalseClass] Flush eDNS cache on stop123def stop(flush_cache = false)124ensure_close = [self.udp_sock, self.tcp_sock].compact125begin126self.listener_thread.kill if self.listener_thread.respond_to?(:kill)127self.listener_thread = nil128ensure129while csock = ensure_close.shift130csock.stop if csock.respond_to?(:stop)131csock.close unless csock.respond_to?(:close) and csock.closed?132end133end134self.cache.stop(flush_cache)135end136137#138# Process client request, handled with dispatch_request_proc if set139#140# @param cli [Rex::Socket::Tcp, Rex::Socket::Udp] Client sending the request141# @param data [String] raw DNS request data142def dispatch_request(cli, data)143if self.dispatch_request_proc144self.dispatch_request_proc.call(cli,data)145else146default_dispatch_request(cli,data)147end148end149150#151# Default DNS request dispatcher, attempts to find152# response records in cache or forwards request upstream153#154# @param cli [Rex::Socket::Tcp, Rex::Socket::Udp] Client sending the request155# @param data [String] raw DNS request data156def default_dispatch_request(cli,data)157return if data.strip.empty?158req = Packet.encode_drb(data)159forward = req.dup160# Find cached items, remove request from forwarded packet161req.question.each do |ques|162cached = self.cache.find(ques.qname, ques.qtype)163if cached.empty?164next165else166req.instance_variable_set(:@answer, (req.answer + cached).uniq)167forward.question.delete(ques)168end169end170# Forward remaining requests, cache responses171if forward.question.count > 0 and @fwd_res172forwarded = self.fwd_res.send(forward)173req.instance_variable_set(:@answer, (req.answer + forwarded.answer).uniq)174forwarded.answer.each do |ans|175self.cache.cache_record(ans)176end177req.header.ra = true # Set recursion bit178end179# Finalize answers in response180# Check for empty response prior to sending181if req.answer.size < 1182req.header.rCode = Dnsruby::RCode::NOERROR183end184req.header.qr = true # Set response bit185send_response(cli, req.data)186end187188#189# Returns the hardcore alias for the DNS service190#191def self.hardcore_alias(*args)192"#{(args[0] || '')}-#{(args[1] || '')}-#{args[5] || ''}"193end194195#196# DNS server.197#198def alias199"DNS Server"200end201202203protected204#205# This method monitors the listener socket for new connections and calls206# the +on_client_connect+ callback routine.207#208def monitor_listener209while true210rds = [self.udp_sock]211wds = []212eds = [self.udp_sock]213214r,_,_ = ::IO.select(rds,wds,eds,1)215216if (r != nil and r[0] == self.udp_sock)217buf,host,port = self.udp_sock.recvfrom(65535)218# Mock up a client object for sending back data219cli = MockDnsClient.new(host, port, r[0])220dispatch_request(cli, buf)221end222end223end224225#226# Processes request coming from client227#228# @param cli [Rex::Socket::Tcp] Client sending request229def on_client_data(cli)230begin231data = cli.read(65535)232233raise ::EOFError if not data234raise ::EOFError if data.empty?235dispatch_request(cli, data)236rescue EOFError => e237self.tcp_socket.close_client(cli) if cli238raise e239end240end241242end243244end245end246end247248249