<commit_before>##
# driver_dns.rb
# Created March, 2013
# By <NAME>
#
# See: LICENSE.txt
#
# The DNS dnscat server.
##

require 'rubydns'

require 'log'
require 'pp' # TODO: Debug

class DriverDNS
  # Use upstream DNS for name resolution.
  UPSTREAM = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53]])

  # Get a handle to these things
  Name = Resolv::DNS::Name
  IN = Resolv::DNS::Resource::IN

  MAX_A_RECORDS = 20   # A nice number that shouldn't cause a TCP switch
  MAX_AAAA_RECORDS = 5

  RECORD_TYPES = {
    IN::TXT => {
      :requires_domain => false,
      :max_length      => 250,
      :requires_hex    => true,
      :requires_name   => false,
      :encoder         => Proc.new() do |name|
         name.unpack("H*").pop
      end,
    },
    IN::MX => {
      :requires_domain => true,
      :max_length      => 240, # Shorter, because some periods need to be added
      :requires_hex    => true,
      :requires_name   => true,
      :encoder         => Proc.new() do |name|
         name.unpack("H*").pop.chars.each_slice(63).map(&:join).join(".")
      end,
    },
    IN::CNAME => {
      :requires_domain => true,
      :max_length      => 240, # Shorter, because some periods need to be added
      :requires_hex    => true,
      :requires_name   => true,
      :encoder         => Proc.new() do |name|
         name.unpack("H*").pop.chars.each_slice(63).map(&:join).join(".")
      end,
    },
    IN::A => {
      :requires_domain => false,
      :max_length      => (MAX_A_RECORDS * 4) - 1, # Length-prefixed, since we only have DWORD granularity
      :requires_hex    => false,
      :requires_name   => false,

      # Encode in length-prefixed dotted-decimal notation
      :encoder         => Proc.new() do |name|
         (name.length.chr + name).chars.each_slice(4).map(&:join).map do |ip|
           "%d.%d.%d.%d" % [
             (ip[0] || rand(255)).ord,
             (ip[1] || rand(255)).ord,
             (ip[2] || rand(255)).ord,
             (ip[3] || rand(255)).ord
           ]
         end
      end,
    },
    IN::AAAA => {
      :requires_domain => false,
      :max_length      => (MAX_AAAA_RECORDS * 16) - 1, # Length-prefixed, because low granularity
      :requires_hex    => false,
      :requires_name   => false,

      # Encode in length-prefixed IPv6 notation
      :encoder         => Proc.new() do |name|
         (name.length.chr + name).chars.each_slice(16).map(&:join).map do |ip|
           ip = ip.ljust(16, rand(255).chr)
           ip.bytes.each_slice(2).map do |octet|
             "%04x" % [octet[0] << 8 | octet[1]]
           end.join(":")
         end
      end,
    },

  }

  def initialize(host, port, domains, autodomain, passthrough)
    Log::WARNING(nil, "Starting Dnscat2 DNS server on #{host}:#{port} [domains = #{domains.nil? ? "n/a" : domains.join(", ")}]...")
    if(autodomain)
      Log::WARNING(nil, "Will also accept direct queries, if they're tagged properly!")
    end
    if(domains.nil? || domains.length == 0)
      Log::WARNING(nil, "No domains were selected, which means this server will only respond to direct queries")
    end

    @host        = host
    @port        = port
    @domains     = domains
    @autodomain  = autodomain
    @passthrough = passthrough
    @shown_pt    = false
  end


  # If domain is non-nil, match /(.*)\.domain/
  # If domain is nil, match /identifier\.(.*)/
  # If required_prefix is set, it only matches domains that contain that prefix
  #
  # The required prefix has to come first, if it's present
  def DriverDNS.get_domain_regex(domain, identifier, required_prefix = nil)<commit_msg>Cleaned up the ip transcoding, and changed the padding to be the number of bytes of padding (for no real reason, it just looks a little nicer)<commit_after>##
# driver_dns.rb
# Created March, 2013
# By <NAME>
#
# See: LICENSE.txt
#
# The DNS dnscat server.
##

require 'rubydns'

require 'log'
require 'pp' # TODO: Debug

class DriverDNS
  # Use upstream DNS for name resolution.
  UPSTREAM = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53]])

  # Get a handle to these things
  Name = Resolv::DNS::Name
  IN = Resolv::DNS::Resource::IN

  MAX_A_RECORDS = 20   # A nice number that shouldn't cause a TCP switch
  MAX_AAAA_RECORDS = 5

  RECORD_TYPES = {
    IN::TXT => {
      :requires_domain => false,
      :max_length      => 250,
      :requires_hex    => true,
      :requires_name   => false,
      :encoder         => Proc.new() do |name|
         name.unpack("H*").pop
      end,
    },
    IN::MX => {
      :requires_domain => true,
      :max_length      => 240, # Shorter, because some periods need to be added
      :requires_hex    => true,
      :requires_name   => true,
      :encoder         => Proc.new() do |name|
         name.unpack("H*").pop.chars.each_slice(63).map(&:join).join(".")
      end,
    },
    IN::CNAME => {
      :requires_domain => true,
      :max_length      => 240, # Shorter, because some periods need to be added
      :requires_hex    => true,
      :requires_name   => true,
      :encoder         => Proc.new() do |name|
         name.unpack("H*").pop.chars.each_slice(63).map(&:join).join(".")
      end,
    },
    IN::A => {
      :requires_domain => false,
      :max_length      => (MAX_A_RECORDS * 4) - 1, # Length-prefixed, since we only have DWORD granularity
      :requires_hex    => false,
      :requires_name   => false,

      # Encode in length-prefixed dotted-decimal notation
      :encoder         => Proc.new() do |name|
         (name.length.chr + name).chars.each_slice(4).map(&:join).map do |ip|
           ip = ip.ljust(4, (4 - (ip.length % 4)).chr)
           "%d.%d.%d.%d" % ip.bytes.to_a # return
         end
      end,
    },
    IN::AAAA => {
      :requires_domain => false,
      :max_length      => (MAX_AAAA_RECORDS * 16) - 1, # Length-prefixed, because low granularity
      :requires_hex    => false,
      :requires_name   => false,

      # Encode in length-prefixed IPv6 notation
      :encoder         => Proc.new() do |name|
         (name.length.chr + name).chars.each_slice(16).map(&:join).map do |ip|
           ip = ip.ljust(16, (16 - (ip.length % 16)).chr)
           ip.bytes.each_slice(2).map do |octet|
             "%04x" % [octet[0] << 8 | octet[1]]
           end.join(":") # return
         end
      end,
    },

  }

  def initialize(host, port, domains, autodomain, passthrough)
    Log::WARNING(nil, "Starting Dnscat2 DNS server on #{host}:#{port} [domains = #{domains.nil? ? "n/a" : domains.join(", ")}]...")
    if(autodomain)
      Log::WARNING(nil, "Will also accept direct queries, if they're tagged properly!")
    end
    if(domains.nil? || domains.length == 0)
      Log::WARNING(nil, "No domains were selected, which means this server will only respond to direct queries")
    end

    @host        = host
    @port        = port
    @domains     = domains
    @autodomain  = autodomain
    @passthrough = passthrough
    @shown_pt    = false
  end


  # If domain is non-nil, match /(.*)\.domain/
  # If domain is nil, match /identifier\.(.*)/
  # If required_prefix is set, it only matches domains that contain that prefix
  #
  # The required prefix has to come first, if it's present
  def DriverDNS.get_domain_regex(domain, identifier, required_prefix = nil)
{'fraction_non_alphanumeric': 0.14562174032390887, 'fraction_numerical': 0.01592094427669503, 'mean_word_length': 2.297285067873303, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '73921', 'n_tokens_mistral': 2587, 'n_tokens_neox': 2407, 'n_words': 629}