class LogStash::Filters::DNS
The DNS
filter performs a lookup (either an A record/CNAME record lookup or a reverse lookup at the PTR record) on records specified under the ‘reverse` arrays or respectively under the `resolve` arrays.
The config should look like this:
- source,ruby
-
filter {
dns { reverse => [ "source_host", "field_with_address" ] resolve => [ "field_with_fqdn" ] action => "replace" }
}
This filter, like all filters, only processes 1 event at a time, so the use of this plugin can significantly slow down your pipeline’s throughput if you have a high latency network. By way of example, if each DNS
lookup takes 2 milliseconds, the maximum throughput you can achieve with a single filter worker is 500 events per second (1000 milliseconds / 2 milliseconds).
Attributes
failed_cache[R]
hit_cache[R]
Public Instance Methods
filter(event)
click to toggle source
# File lib/logstash/filters/dns.rb, line 115 def filter(event) if @resolve return if resolve(event).nil? end if @reverse return if reverse(event).nil? end filter_matched(event) end
register()
click to toggle source
# File lib/logstash/filters/dns.rb, line 96 def register if @nameserver.nil? && @hostsfile.nil? @resolv = Resolv.new(default_resolvers) else @resolv = Resolv.new(build_resolvers) end if @hit_cache_size > 0 @hit_cache = LruRedux::TTL::ThreadSafeCache.new(@hit_cache_size, @hit_cache_ttl) end if @failed_cache_size > 0 @failed_cache = LruRedux::TTL::ThreadSafeCache.new(@failed_cache_size, @failed_cache_ttl) end @ip_validator = Resolv::AddressRegex end
Private Instance Methods
build_resolvers()
click to toggle source
# File lib/logstash/filters/dns.rb, line 143 def build_resolvers build_user_host_resolvers.concat([::Resolv::Hosts.new]).concat(build_user_dns_resolver) end
build_user_dns_resolver()
click to toggle source
# File lib/logstash/filters/dns.rb, line 152 def build_user_dns_resolver return [] if @nameserver.nil? || @nameserver.empty? [dns_resolver(normalised_nameserver)] end
build_user_host_resolvers()
click to toggle source
# File lib/logstash/filters/dns.rb, line 147 def build_user_host_resolvers return [] if @hostsfile.nil? || @hostsfile.empty? @hostsfile.map{|fn| ::Resolv::Hosts.new(fn)} end
default_dns_resolver()
click to toggle source
# File lib/logstash/filters/dns.rb, line 133 def default_dns_resolver dns_resolver(nil) end
default_resolvers()
click to toggle source
# File lib/logstash/filters/dns.rb, line 129 def default_resolvers [::Resolv::Hosts.new, default_dns_resolver] end
dns_resolver(args=nil)
click to toggle source
# File lib/logstash/filters/dns.rb, line 137 def dns_resolver(args=nil) dns_resolver = ::Resolv::DNS.new(args) dns_resolver.timeouts = @timeout dns_resolver end
getaddress(name)
click to toggle source
# File lib/logstash/filters/dns.rb, line 385 def getaddress(name) idn = IDN.toASCII(name) address = resolv_getaddress_or_nil(@resolv, idn) address && address.force_encoding(Encoding::UTF_8) end
getname(address)
click to toggle source
# File lib/logstash/filters/dns.rb, line 378 def getname(address) name = resolv_getname_or_nil(@resolv, address) name && name.force_encoding(Encoding::UTF_8) name && IDN.toUnicode(name) end
normalised_nameserver()
click to toggle source
# File lib/logstash/filters/dns.rb, line 157 def normalised_nameserver nameserver_hash = @nameserver.kind_of?(Hash) ? @nameserver.dup : { 'address' => @nameserver } nameserver = nameserver_hash.delete('address') || fail(LogStash::ConfigurationError, "DNS Filter: `nameserver` hash must include `address` (got `#{@nameserver}`)") nameserver = Array(nameserver).map(&:to_s) nameserver.empty? && fail(LogStash::ConfigurationError, "DNS Filter: `nameserver` addresses, when specified, cannot be empty (got `#{@nameserver}`)") search = nameserver_hash.delete('search') || [] search = Array(search).map(&:to_s) search.size > 6 && fail(LogStash::ConfigurationError, "DNS Filter: A maximum of 6 `search` domains are accepted (got `#{@nameserver}`)") ndots = nameserver_hash.delete('ndots') || 1 ndots = Integer(ndots) ndots <= 0 && fail(LogStash::ConfigurationError, "DNS Filter: `ndots` must be positive (got `#{@nameserver}`)") fail(LogStash::ConfigurationError, "Unknown `nameserver` argument(s): #{nameserver_hash}") unless nameserver_hash.empty? { :nameserver => nameserver, :search => search, :ndots => ndots } end
resolv_getaddress_or_nil(resolver, name)
click to toggle source
# File lib/logstash/filters/dns.rb, line 406 def resolv_getaddress_or_nil(resolver, name) # `Resolv#each_address` yields to the provided block zero or more times; # to prevent it from yielding multiple times when more than one match # is found, we return directly in the block. # See also `Resolv#getaddress` resolver.each_address(name) do |address| return address end # If no match was found, we return nil. return nil end
resolv_getname_or_nil(resolver, address)
click to toggle source
# File lib/logstash/filters/dns.rb, line 392 def resolv_getname_or_nil(resolver, address) # `Resolv#each_name` yields to the provided block zero or more times; # to prevent it from yielding multiple times when more than one match # is found, we return directly in the block. # See also `Resolv#getname` resolver.each_name(address) do |name| return name end # If no match was found, we return nil. return nil end
resolve(event)
click to toggle source
# File lib/logstash/filters/dns.rb, line 181 def resolve(event) @resolve.each do |field| is_array = false raw = event.get(field) if raw.nil? @logger.warn("DNS filter could not resolve missing field", :field => field) next end if raw.is_a?(Array) is_array = true if raw.length > 1 @logger.warn("DNS: skipping resolve, can't deal with multiple values", :field => field, :value => raw) return end raw = raw.first end if !raw.kind_of?(String) @logger.warn("DNS: skipping resolve, can't deal with non-string values", :field => field, :value => raw) return end begin return if @failed_cache && @failed_cache[raw] # recently failed resolv, skip if @hit_cache address = @hit_cache[raw] if address.nil? if address = retriable_getaddress(raw) @hit_cache[raw] = address end end else address = retriable_getaddress(raw) end if address.nil? @failed_cache[raw] = true if @failed_cache @logger.debug("DNS: couldn't resolve the hostname.", :field => field, :value => raw) return end rescue Resolv::ResolvTimeout @failed_cache[raw] = true if @failed_cache @logger.debug("DNS: timeout on resolving the hostname.", :field => field, :value => raw) @tag_on_timeout.each { |tag| event.tag(tag) } return rescue SocketError => e @logger.error("DNS: Encountered SocketError.", :field => field, :value => raw, :message => e.message) return rescue Java::JavaLang::IllegalArgumentException => e @logger.error("DNS: Unable to parse address.", :field => field, :value => raw, :message => e.message) return rescue => e @logger.error("DNS: Unexpected Error.", :field => field, :value => raw, :message => e.message) return end if @action == "replace" if is_array event.set(field, [address]) else event.set(field, address) end else if !is_array event.set(field, [event.get(field), address]) else arr = event.get(field) arr << address event.set(field, arr) end end end end
retriable_getaddress(name)
click to toggle source
# File lib/logstash/filters/dns.rb, line 371 def retriable_getaddress(name) retriable_request do getaddress(name) end end
retriable_getname(address)
click to toggle source
# File lib/logstash/filters/dns.rb, line 364 def retriable_getname(address) retriable_request do getname(address) end end
retriable_request(&block)
click to toggle source
# File lib/logstash/filters/dns.rb, line 349 def retriable_request(&block) tries = 0 begin block.call rescue Resolv::ResolvTimeout, SocketError if tries < @max_retries tries = tries + 1 retry else raise end end end
reverse(event)
click to toggle source
# File lib/logstash/filters/dns.rb, line 263 def reverse(event) @reverse.each do |field| raw = event.get(field) if raw.nil? @logger.warn("DNS filter could not perform reverse lookup on missing field", :field => field) next end is_array = false if raw.is_a?(Array) is_array = true if raw.length > 1 @logger.warn("DNS: skipping reverse, can't deal with multiple values", :field => field, :value => raw) return end raw = raw.first end if !raw.kind_of?(String) @logger.warn("DNS: skipping reverse, can't deal with non-string values", :field => field, :value => raw) return end if ! @ip_validator.match(raw) @logger.debug("DNS: not an address", :field => field, :value => event.get(field)) return end begin return if @failed_cache && @failed_cache.key?(raw) # recently failed resolv, skip if @hit_cache hostname = @hit_cache[raw] if hostname.nil? if hostname = retriable_getname(raw) @hit_cache[raw] = hostname end end else hostname = retriable_getname(raw) end if hostname.nil? @failed_cache[raw] = true if @failed_cache @logger.debug("DNS: couldn't resolve the address.", :field => field, :value => raw) return end rescue Resolv::ResolvTimeout @failed_cache[raw] = true if @failed_cache @logger.debug("DNS: timeout on resolving address.", :field => field, :value => raw) @tag_on_timeout.each { |tag| event.tag(tag) } return rescue SocketError => e @logger.error("DNS: Encountered SocketError.", :field => field, :value => raw, :message => e.message) return rescue Java::JavaLang::IllegalArgumentException => e @logger.error("DNS: Unable to parse address.", :field => field, :value => raw, :message => e.message) return rescue => e @logger.error("DNS: Unexpected Error.", :field => field, :value => raw, :message => e.message) return end if @action == "replace" if is_array event.set(field, [hostname]) else event.set(field, hostname) end else if !is_array event.set(field, [event.get(field), hostname]) else arr = event.get(field) arr << hostname event.set(field, arr) end end end end