logstash-filter-dns 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZGQ1ZTdjOWUwOTFhNTljZDA2MTZkZWY4ZDIwMTI0N2Y3Mzg1YjJkMw==
5
+ data.tar.gz: !binary |-
6
+ MDlmNGYyMzQ0MGY3MjE0MWY3NWE4NzUwMTZhMTc2MjMyMjcxMDdkNw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZjNjNTNlMzE4N2YxMzQ3YWQ2OWNhOWNkMjdlNWQ3MmNmYWNkZTIwMmI3ODVk
10
+ NmE2ODlkOGZlNjE1NTg5MmY0NTdkODI2YTdiYTg3ZWQ4ZDEwMWQ5ZDc5ZWVi
11
+ Mjc5YzEwOTVkMDRkOGY2ZDk4OGU4Y2FiOTQxM2E3MzhkODlmYjg=
12
+ data.tar.gz: !binary |-
13
+ MDYwNjI5YWY2Y2RmMDIzOGMwZTgxNDJjMjQ1MjExODU3NzMxMDVlZGZiZjA0
14
+ ZjA2Zjk1MDc4NmIyYWM1YTM0MzMzN2VlZmI0YzkyMWFmZmJkYjY2MTE2ODQ0
15
+ ZWRlODM1NTQzYjc1NzMwY2U2YjM5OTk1Y2E4NDIwYTVkNzBjOGU=
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ .bundle
4
+ vendor
data/1795.patch ADDED
@@ -0,0 +1,61 @@
1
+ From 2fae9033755a082b282d9f2e070e0e026154d88a Mon Sep 17 00:00:00 2001
2
+ From: Joao Duarte <jsvduarte@gmail.com>
3
+ Date: Sat, 27 Sep 2014 23:59:50 +0200
4
+ Subject: [PATCH] Fix add_tag behaviour in dns filter
5
+
6
+ The filter should only modify the event's fields and tags if and only if
7
+ all resolves/reverses succeed. So we clone the event, modify the new
8
+ copy and return it if all operations succeed. Otherwise the original
9
+ event is not modified.
10
+
11
+ For performance reasons we could reverse the clone logic: clone the
12
+ event, modify the original event and, it case of failure, return the
13
+ backup.
14
+
15
+ Note: this changes the dns filter behaviour towards add_tag
16
+ ---
17
+ lib/logstash/filters/dns.rb | 12 ++++++---
18
+ spec/filters/dns_spec.rb | 60 ++++++++++++++++++++++++++++++++++++++++++++-
19
+ 2 files changed, 68 insertions(+), 4 deletions(-)
20
+
21
+ diff --git a/lib/logstash/filters/dns.rb b/lib/logstash/filters/dns.rb
22
+ index 46bb075..f23b819 100644
23
+ --- a/lib/logstash/filters/dns.rb
24
+ +++ b/lib/logstash/filters/dns.rb
25
+ @@ -70,11 +70,14 @@ def register
26
+ def filter(event)
27
+ return unless filter?(event)
28
+
29
+ + new_event = event.clone
30
+ +
31
+ if @resolve
32
+ begin
33
+ status = Timeout::timeout(@timeout) {
34
+ - resolve(event)
35
+ + resolve(new_event)
36
+ }
37
+ + return if status.nil?
38
+ rescue Timeout::Error
39
+ @logger.debug("DNS: resolve action timed out")
40
+ return
41
+ @@ -84,15 +87,18 @@ def filter(event)
42
+ if @reverse
43
+ begin
44
+ status = Timeout::timeout(@timeout) {
45
+ - reverse(event)
46
+ + reverse(new_event)
47
+ }
48
+ + return if status.nil?
49
+ rescue Timeout::Error
50
+ @logger.debug("DNS: reverse action timed out")
51
+ return
52
+ end
53
+ end
54
+
55
+ - filter_matched(event)
56
+ + filter_matched(new_event)
57
+ + yield new_event
58
+ + event.cancel
59
+ end
60
+
61
+ private
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+ gem 'rake'
3
+ gem 'gem_publisher'
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2012-2014 Elasticsearch <http://www.elasticsearch.org>
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ @files=[]
2
+
3
+ task :default do
4
+ system("rake -T")
5
+ end
6
+
@@ -0,0 +1,209 @@
1
+ # encoding: utf-8
2
+ # DNS Filter
3
+ #
4
+ # This filter will resolve any IP addresses from a field of your choosing.
5
+ #
6
+
7
+ require "logstash/filters/base"
8
+ require "logstash/namespace"
9
+
10
+ # The DNS filter performs a lookup (either an A record/CNAME record lookup
11
+ # or a reverse lookup at the PTR record) on records specified under the
12
+ # "reverse" and "resolve" arrays.
13
+ #
14
+ # The config should look like this:
15
+ #
16
+ # filter {
17
+ # dns {
18
+ # type => 'type'
19
+ # reverse => [ "source_host", "field_with_address" ]
20
+ # resolve => [ "field_with_fqdn" ]
21
+ # action => "replace"
22
+ # }
23
+ # }
24
+ #
25
+ # Caveats: at the moment, there's no way to tune the timeout with the 'resolv'
26
+ # core library. It does seem to be fixed in here:
27
+ #
28
+ # http://redmine.ruby-lang.org/issues/5100
29
+ #
30
+ # but isn't currently in JRuby.
31
+ class LogStash::Filters::DNS < LogStash::Filters::Base
32
+
33
+ config_name "dns"
34
+ milestone 2
35
+
36
+ # Reverse resolve one or more fields.
37
+ config :reverse, :validate => :array
38
+
39
+ # Forward resolve one or more fields.
40
+ config :resolve, :validate => :array
41
+
42
+ # Determine what action to do: append or replace the values in the fields
43
+ # specified under "reverse" and "resolve."
44
+ config :action, :validate => [ "append", "replace" ], :default => "append"
45
+
46
+ # Use custom nameserver.
47
+ config :nameserver, :validate => :string
48
+
49
+ # TODO(sissel): make 'action' required? This was always the intent, but it
50
+ # due to a typo it was never enforced. Thus the default behavior in past
51
+ # versions was 'append' by accident.
52
+
53
+ # resolv calls will be wrapped in a timeout instance
54
+ config :timeout, :validate => :number, :default => 2
55
+
56
+ public
57
+ def register
58
+ require "resolv"
59
+ require "timeout"
60
+ if @nameserver.nil?
61
+ @resolv = Resolv.new
62
+ else
63
+ @resolv = Resolv.new(resolvers=[::Resolv::Hosts.new, ::Resolv::DNS.new(:nameserver => [@nameserver], :search => [], :ndots => 1)])
64
+ end
65
+
66
+ @ip_validator = Resolv::AddressRegex
67
+ end # def register
68
+
69
+ public
70
+ def filter(event)
71
+ return unless filter?(event)
72
+
73
+ new_event = event.clone
74
+
75
+ if @resolve
76
+ begin
77
+ status = Timeout::timeout(@timeout) {
78
+ resolve(new_event)
79
+ }
80
+ return if status.nil?
81
+ rescue Timeout::Error
82
+ @logger.debug("DNS: resolve action timed out")
83
+ return
84
+ end
85
+ end
86
+
87
+ if @reverse
88
+ begin
89
+ status = Timeout::timeout(@timeout) {
90
+ reverse(new_event)
91
+ }
92
+ return if status.nil?
93
+ rescue Timeout::Error
94
+ @logger.debug("DNS: reverse action timed out")
95
+ return
96
+ end
97
+ end
98
+
99
+ filter_matched(new_event)
100
+ yield new_event
101
+ event.cancel
102
+ end
103
+
104
+ private
105
+ def resolve(event)
106
+ @resolve.each do |field|
107
+ is_array = false
108
+ raw = event[field]
109
+ if raw.is_a?(Array)
110
+ is_array = true
111
+ if raw.length > 1
112
+ @logger.warn("DNS: skipping resolve, can't deal with multiple values", :field => field, :value => raw)
113
+ return
114
+ end
115
+ raw = raw.first
116
+ end
117
+
118
+ begin
119
+ # in JRuby 1.7.11 outputs as US-ASCII
120
+ address = @resolv.getaddress(raw).force_encoding(Encoding::UTF_8)
121
+ rescue Resolv::ResolvError
122
+ @logger.debug("DNS: couldn't resolve the hostname.",
123
+ :field => field, :value => raw)
124
+ return
125
+ rescue Resolv::ResolvTimeout
126
+ @logger.debug("DNS: timeout on resolving the hostname.",
127
+ :field => field, :value => raw)
128
+ return
129
+ rescue SocketError => e
130
+ @logger.debug("DNS: Encountered SocketError.",
131
+ :field => field, :value => raw)
132
+ return
133
+ rescue NoMethodError => e
134
+ # see JRUBY-5647
135
+ @logger.debug("DNS: couldn't resolve the hostname.",
136
+ :field => field, :value => raw,
137
+ :extra => "NameError instead of ResolvError")
138
+ return
139
+ end
140
+
141
+ if @action == "replace"
142
+ if is_array
143
+ event[field] = [address]
144
+ else
145
+ event[field] = address
146
+ end
147
+ else
148
+ if !is_array
149
+ event[field] = [event[field], address]
150
+ else
151
+ event[field] << address
152
+ end
153
+ end
154
+
155
+ end
156
+ end
157
+
158
+ private
159
+ def reverse(event)
160
+ @reverse.each do |field|
161
+ raw = event[field]
162
+ is_array = false
163
+ if raw.is_a?(Array)
164
+ is_array = true
165
+ if raw.length > 1
166
+ @logger.warn("DNS: skipping reverse, can't deal with multiple values", :field => field, :value => raw)
167
+ return
168
+ end
169
+ raw = raw.first
170
+ end
171
+
172
+ if ! @ip_validator.match(raw)
173
+ @logger.debug("DNS: not an address",
174
+ :field => field, :value => event[field])
175
+ return
176
+ end
177
+ begin
178
+ # in JRuby 1.7.11 outputs as US-ASCII
179
+ hostname = @resolv.getname(raw).force_encoding(Encoding::UTF_8)
180
+ rescue Resolv::ResolvError
181
+ @logger.debug("DNS: couldn't resolve the address.",
182
+ :field => field, :value => raw)
183
+ return
184
+ rescue Resolv::ResolvTimeout
185
+ @logger.debug("DNS: timeout on resolving address.",
186
+ :field => field, :value => raw)
187
+ return
188
+ rescue SocketError => e
189
+ @logger.debug("DNS: Encountered SocketError.",
190
+ :field => field, :value => raw)
191
+ return
192
+ end
193
+
194
+ if @action == "replace"
195
+ if is_array
196
+ event[field] = [hostname]
197
+ else
198
+ event[field] = hostname
199
+ end
200
+ else
201
+ if !is_array
202
+ event[field] = [event[field], hostname]
203
+ else
204
+ event[field] << hostname
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end # class LogStash::Filters::DNS
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |s|
2
+
3
+ s.name = 'logstash-filter-dns'
4
+ s.version = '0.1.0'
5
+ s.licenses = ['Apache License (2.0)']
6
+ s.summary = "This filter will resolve any IP addresses from a field of your choosing."
7
+ s.description = "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' and 'resolve' arrays."
8
+ s.authors = ["Elasticsearch"]
9
+ s.email = 'richard.pijnenburg@elasticsearch.com'
10
+ s.homepage = "http://logstash.net/"
11
+ s.require_paths = ["lib"]
12
+
13
+ # Files
14
+ s.files = `git ls-files`.split($\)+::Dir.glob('vendor/*')
15
+
16
+ # Tests
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+
19
+ # Special flag to let us know this is actually a logstash plugin
20
+ s.metadata = { "logstash_plugin" => "true", "group" => "filter" }
21
+
22
+ # Gem dependencies
23
+ s.add_runtime_dependency 'logstash', '>= 1.4.0', '< 2.0.0'
24
+
25
+ end
26
+
@@ -0,0 +1,9 @@
1
+ require "gem_publisher"
2
+
3
+ desc "Publish gem to RubyGems.org"
4
+ task :publish_gem do |t|
5
+ gem_file = Dir.glob(File.expand_path('../*.gemspec',File.dirname(__FILE__))).first
6
+ gem = GemPublisher.publish_if_updated(gem_file, :rubygems)
7
+ puts "Published #{gem}" if gem
8
+ end
9
+
@@ -0,0 +1,169 @@
1
+ require "net/http"
2
+ require "uri"
3
+ require "digest/sha1"
4
+
5
+ def vendor(*args)
6
+ return File.join("vendor", *args)
7
+ end
8
+
9
+ directory "vendor/" => ["vendor"] do |task, args|
10
+ mkdir task.name
11
+ end
12
+
13
+ def fetch(url, sha1, output)
14
+
15
+ puts "Downloading #{url}"
16
+ actual_sha1 = download(url, output)
17
+
18
+ if actual_sha1 != sha1
19
+ fail "SHA1 does not match (expected '#{sha1}' but got '#{actual_sha1}')"
20
+ end
21
+ end # def fetch
22
+
23
+ def file_fetch(url, sha1)
24
+ filename = File.basename( URI(url).path )
25
+ output = "vendor/#{filename}"
26
+ task output => [ "vendor/" ] do
27
+ begin
28
+ actual_sha1 = file_sha1(output)
29
+ if actual_sha1 != sha1
30
+ fetch(url, sha1, output)
31
+ end
32
+ rescue Errno::ENOENT
33
+ fetch(url, sha1, output)
34
+ end
35
+ end.invoke
36
+
37
+ return output
38
+ end
39
+
40
+ def file_sha1(path)
41
+ digest = Digest::SHA1.new
42
+ fd = File.new(path, "r")
43
+ while true
44
+ begin
45
+ digest << fd.sysread(16384)
46
+ rescue EOFError
47
+ break
48
+ end
49
+ end
50
+ return digest.hexdigest
51
+ ensure
52
+ fd.close if fd
53
+ end
54
+
55
+ def download(url, output)
56
+ uri = URI(url)
57
+ digest = Digest::SHA1.new
58
+ tmp = "#{output}.tmp"
59
+ Net::HTTP.start(uri.host, uri.port, :use_ssl => (uri.scheme == "https")) do |http|
60
+ request = Net::HTTP::Get.new(uri.path)
61
+ http.request(request) do |response|
62
+ fail "HTTP fetch failed for #{url}. #{response}" if [200, 301].include?(response.code)
63
+ size = (response["content-length"].to_i || -1).to_f
64
+ count = 0
65
+ File.open(tmp, "w") do |fd|
66
+ response.read_body do |chunk|
67
+ fd.write(chunk)
68
+ digest << chunk
69
+ if size > 0 && $stdout.tty?
70
+ count += chunk.bytesize
71
+ $stdout.write(sprintf("\r%0.2f%%", count/size * 100))
72
+ end
73
+ end
74
+ end
75
+ $stdout.write("\r \r") if $stdout.tty?
76
+ end
77
+ end
78
+
79
+ File.rename(tmp, output)
80
+
81
+ return digest.hexdigest
82
+ rescue SocketError => e
83
+ puts "Failure while downloading #{url}: #{e}"
84
+ raise
85
+ ensure
86
+ File.unlink(tmp) if File.exist?(tmp)
87
+ end # def download
88
+
89
+ def untar(tarball, &block)
90
+ require "archive/tar/minitar"
91
+ tgz = Zlib::GzipReader.new(File.open(tarball))
92
+ # Pull out typesdb
93
+ tar = Archive::Tar::Minitar::Input.open(tgz)
94
+ tar.each do |entry|
95
+ path = block.call(entry)
96
+ next if path.nil?
97
+ parent = File.dirname(path)
98
+
99
+ mkdir_p parent unless File.directory?(parent)
100
+
101
+ # Skip this file if the output file is the same size
102
+ if entry.directory?
103
+ mkdir path unless File.directory?(path)
104
+ else
105
+ entry_mode = entry.instance_eval { @mode } & 0777
106
+ if File.exists?(path)
107
+ stat = File.stat(path)
108
+ # TODO(sissel): Submit a patch to archive-tar-minitar upstream to
109
+ # expose headers in the entry.
110
+ entry_size = entry.instance_eval { @size }
111
+ # If file sizes are same, skip writing.
112
+ next if stat.size == entry_size && (stat.mode & 0777) == entry_mode
113
+ end
114
+ puts "Extracting #{entry.full_name} from #{tarball} #{entry_mode.to_s(8)}"
115
+ File.open(path, "w") do |fd|
116
+ # eof? check lets us skip empty files. Necessary because the API provided by
117
+ # Archive::Tar::Minitar::Reader::EntryStream only mostly acts like an
118
+ # IO object. Something about empty files in this EntryStream causes
119
+ # IO.copy_stream to throw "can't convert nil into String" on JRuby
120
+ # TODO(sissel): File a bug about this.
121
+ while !entry.eof?
122
+ chunk = entry.read(16384)
123
+ fd.write(chunk)
124
+ end
125
+ #IO.copy_stream(entry, fd)
126
+ end
127
+ File.chmod(entry_mode, path)
128
+ end
129
+ end
130
+ tar.close
131
+ File.unlink(tarball) if File.file?(tarball)
132
+ end # def untar
133
+
134
+ def ungz(file)
135
+
136
+ outpath = file.gsub('.gz', '')
137
+ tgz = Zlib::GzipReader.new(File.open(file))
138
+ begin
139
+ File.open(outpath, "w") do |out|
140
+ IO::copy_stream(tgz, out)
141
+ end
142
+ File.unlink(file)
143
+ rescue
144
+ File.unlink(outpath) if File.file?(outpath)
145
+ raise
146
+ end
147
+ tgz.close
148
+ end
149
+
150
+ desc "Process any vendor files required for this plugin"
151
+ task "vendor" do |task, args|
152
+
153
+ @files.each do |file|
154
+ download = file_fetch(file['url'], file['sha1'])
155
+ if download =~ /.tar.gz/
156
+ prefix = download.gsub('.tar.gz', '').gsub('vendor/', '')
157
+ untar(download) do |entry|
158
+ if !file['files'].nil?
159
+ next unless file['files'].include?(entry.full_name.gsub(prefix, ''))
160
+ out = entry.full_name.split("/").last
161
+ end
162
+ File.join('vendor', out)
163
+ end
164
+ elsif download =~ /.gz/
165
+ ungz(download)
166
+ end
167
+ end
168
+
169
+ end
@@ -0,0 +1,209 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+ require "logstash/filters/dns"
4
+ require "resolv"
5
+
6
+ describe LogStash::Filters::DNS do
7
+ before(:each) do
8
+ allow_any_instance_of(Resolv).to receive(:getaddress).with("carrera.databits.net").and_return("199.192.228.250")
9
+ allow_any_instance_of(Resolv).to receive(:getaddress).with("does.not.exist").and_raise(Resolv::ResolvError)
10
+ allow_any_instance_of(Resolv).to receive(:getaddress).with("nonexistanthostname###.net").and_raise(Resolv::ResolvError)
11
+ allow_any_instance_of(Resolv).to receive(:getname).with("199.192.228.250").and_return("carrera.databits.net")
12
+ allow_any_instance_of(Resolv).to receive(:getname).with("127.0.0.1").and_return("localhost")
13
+ allow_any_instance_of(Resolv).to receive(:getname).with("128.0.0.1").and_raise(Resolv::ResolvError)
14
+ allow_any_instance_of(Resolv).to receive(:getname).with("199.192.228.250").and_return("carrera.databits.net")
15
+ end
16
+
17
+ describe "dns reverse lookup, replace (on a field)" do
18
+ config <<-CONFIG
19
+ filter {
20
+ dns {
21
+ reverse => "foo"
22
+ action => "replace"
23
+ }
24
+ }
25
+ CONFIG
26
+
27
+ sample("foo" => "199.192.228.250") do
28
+ insist { subject["foo"] } == "carrera.databits.net"
29
+ end
30
+ end
31
+
32
+ describe "dns reverse lookup, append" do
33
+ config <<-CONFIG
34
+ filter {
35
+ dns {
36
+ reverse => "foo"
37
+ action => "append"
38
+ }
39
+ }
40
+ CONFIG
41
+
42
+ sample("foo" => "199.192.228.250") do
43
+ insist { subject["foo"][0] } == "199.192.228.250"
44
+ insist { subject["foo"][1] } == "carrera.databits.net"
45
+ end
46
+ end
47
+
48
+ describe "dns reverse lookup, not an IP" do
49
+ config <<-CONFIG
50
+ filter {
51
+ dns {
52
+ reverse => "foo"
53
+ }
54
+ }
55
+ CONFIG
56
+
57
+ sample("foo" => "not.an.ip") do
58
+ insist { subject["foo"] } == "not.an.ip"
59
+ end
60
+ end
61
+
62
+ describe "dns resolve lookup, replace" do
63
+ config <<-CONFIG
64
+ filter {
65
+ dns {
66
+ resolve => ["host"]
67
+ action => "replace"
68
+ add_tag => ["success"]
69
+ }
70
+ }
71
+ CONFIG
72
+
73
+ sample("host" => "carrera.databits.net") do
74
+ insist { subject["host"] } == "199.192.228.250"
75
+ insist { subject["tags"] } == ["success"]
76
+ end
77
+ end
78
+
79
+ describe "dns fail resolve lookup, don't add tag" do
80
+ config <<-CONFIG
81
+ filter {
82
+ dns {
83
+ resolve => ["host1", "host2"]
84
+ action => "replace"
85
+ add_tag => ["success"]
86
+ }
87
+ }
88
+ CONFIG
89
+
90
+ sample("host1" => "carrera.databits.net", "host2" => "nonexistanthostname###.net") do
91
+ insist { subject["tags"] }.nil?
92
+ insist { subject["host1"] } == "carrera.databits.net"
93
+ insist { subject["host2"] } == "nonexistanthostname###.net"
94
+ end
95
+ end
96
+
97
+ describe "dns resolves lookups, adds tag" do
98
+ config <<-CONFIG
99
+ filter {
100
+ dns {
101
+ resolve => ["host1", "host2"]
102
+ action => "replace"
103
+ add_tag => ["success"]
104
+ }
105
+ }
106
+ CONFIG
107
+
108
+ sample("host1" => "carrera.databits.net", "host2" => "carrera.databits.net") do
109
+ insist { subject["tags"] } == ["success"]
110
+ end
111
+ end
112
+
113
+ describe "dns resolves and reverses, fails last, no tag" do
114
+ config <<-CONFIG
115
+ filter {
116
+ dns {
117
+ resolve => ["host1"]
118
+ reverse => ["ip1", "ip2"]
119
+ action => "replace"
120
+ add_tag => ["success"]
121
+ }
122
+ }
123
+ CONFIG
124
+
125
+ sample("host1" => "carrera.databits.net",
126
+ "ip1" => "127.0.0.1",
127
+ "ip2" => "128.0.0.1") do
128
+ insist { subject["tags"] }.nil?
129
+ insist { subject["host1"] } == "carrera.databits.net"
130
+ insist { subject["ip1"] } == "127.0.0.1"
131
+ insist { subject["ip2"] } == "128.0.0.1"
132
+ end
133
+ end
134
+
135
+ describe "dns resolve lookup, replace (on a field)" do
136
+ config <<-CONFIG
137
+ filter {
138
+ dns {
139
+ resolve => "foo"
140
+ action => "replace"
141
+ }
142
+ }
143
+ CONFIG
144
+
145
+ sample("foo" => "carrera.databits.net") do
146
+ insist { subject["foo"] } == "199.192.228.250"
147
+ end
148
+ end
149
+
150
+ describe "dns resolve lookup, skip multi-value" do
151
+ config <<-CONFIG
152
+ filter {
153
+ dns {
154
+ resolve => "foo"
155
+ action => "replace"
156
+ }
157
+ }
158
+ CONFIG
159
+
160
+ sample("foo" => ["carrera.databits.net", "foo.databits.net"]) do
161
+ insist { subject["foo"] } == ["carrera.databits.net", "foo.databits.net"]
162
+ end
163
+ end
164
+
165
+ describe "dns resolve lookup, append" do
166
+ config <<-CONFIG
167
+ filter {
168
+ dns {
169
+ resolve => "foo"
170
+ action => "append"
171
+ }
172
+ }
173
+ CONFIG
174
+
175
+ sample("foo" => "carrera.databits.net") do
176
+ insist { subject["foo"][0] } == "carrera.databits.net"
177
+ insist { subject["foo"][1] } == "199.192.228.250"
178
+ end
179
+ end
180
+
181
+ describe "dns resolve lookup, append with multi-value does nothing" do
182
+ config <<-CONFIG
183
+ filter {
184
+ dns {
185
+ resolve => "foo"
186
+ action => "append"
187
+ }
188
+ }
189
+ CONFIG
190
+
191
+ sample("foo" => ["carrera.databits.net", "foo.databits.net"]) do
192
+ insist { subject["foo"] } == ["carrera.databits.net", "foo.databits.net"]
193
+ end
194
+ end
195
+
196
+ describe "dns resolve lookup, not a valid hostname" do
197
+ config <<-CONFIG
198
+ filter {
199
+ dns {
200
+ resolve=> "foo"
201
+ }
202
+ }
203
+ CONFIG
204
+
205
+ sample("foo" => "does.not.exist") do
206
+ insist { subject["foo"] } == "does.not.exist"
207
+ end
208
+ end
209
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-filter-dns
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Elasticsearch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logstash
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.0
20
+ - - <
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.4.0
30
+ - - <
31
+ - !ruby/object:Gem::Version
32
+ version: 2.0.0
33
+ description: The DNS filter performs a lookup (either an A record/CNAME record lookup
34
+ or a reverse lookup at the PTR record) on records specified under the 'reverse'
35
+ and 'resolve' arrays.
36
+ email: richard.pijnenburg@elasticsearch.com
37
+ executables: []
38
+ extensions: []
39
+ extra_rdoc_files: []
40
+ files:
41
+ - .gitignore
42
+ - 1795.patch
43
+ - Gemfile
44
+ - LICENSE
45
+ - Rakefile
46
+ - lib/logstash/filters/dns.rb
47
+ - logstash-filter-dns.gemspec
48
+ - rakelib/publish.rake
49
+ - rakelib/vendor.rake
50
+ - spec/filters/dns_spec.rb
51
+ homepage: http://logstash.net/
52
+ licenses:
53
+ - Apache License (2.0)
54
+ metadata:
55
+ logstash_plugin: 'true'
56
+ group: filter
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 2.4.1
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: This filter will resolve any IP addresses from a field of your choosing.
77
+ test_files:
78
+ - spec/filters/dns_spec.rb