dns-sniper 0.0.1.pre7 → 0.0.1.pre10
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 +4 -4
- data/Gemfile.lock +17 -5
- data/README.md +5 -2
- data/dns-sniper.gemspec +3 -2
- data/lib/dns-sniper/conditional_fetch.rb +111 -0
- data/lib/dns-sniper/hostnames.rb +8 -7
- data/lib/dns-sniper/importer.rb +4 -4
- data/lib/dns-sniper/importers/configuration_importer.rb +5 -5
- data/lib/dns-sniper/importers/domains_importer.rb +7 -11
- data/lib/dns-sniper/importers/hosts_importer.rb +8 -13
- data/lib/dns-sniper.rb +10 -1
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04db0c0c25e714a9540154cef9dcc6352101fc4111dec94804d298f70fa609c3
|
4
|
+
data.tar.gz: 8d55fd5b07b905aaf1ee93261b78e9d1c9429a7bf0baafc7144d04557ffbab5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7a5b16bc843c1e593e7cca234fd4dfff5c6347f0af2723a863d420bf045cd1a1e0f53e181581ce273738e1e7e24ac078d1f92b6671101d0980379ce9f13f64e
|
7
|
+
data.tar.gz: 5e73efea0322fc1662312619dca0cac2333ea8fd2b6842f85fbe5fecf2e71621433726fdecc07c8f01054eeed13cc150b84d616bc2969a1bc76848cc577e55cc
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,31 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dns-sniper (0.0.1.
|
4
|
+
dns-sniper (0.0.1.pre10)
|
5
|
+
activesupport (>= 4.2, < 7.0.3)
|
5
6
|
down (~> 5.1)
|
6
7
|
hosts_file (~> 1.0)
|
7
8
|
|
8
9
|
GEM
|
9
10
|
remote: https://rubygems.org/
|
10
11
|
specs:
|
11
|
-
|
12
|
+
activesupport (7.0.2.4)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (>= 1.6, < 2)
|
15
|
+
minitest (>= 5.1)
|
16
|
+
tzinfo (~> 2.0)
|
17
|
+
addressable (2.8.0)
|
12
18
|
public_suffix (>= 2.0.2, < 5.0)
|
13
|
-
|
14
|
-
|
19
|
+
concurrent-ruby (1.1.10)
|
20
|
+
down (5.3.1)
|
21
|
+
addressable (~> 2.8)
|
15
22
|
hosts_file (1.0.3)
|
23
|
+
i18n (1.10.0)
|
24
|
+
concurrent-ruby (~> 1.0)
|
25
|
+
minitest (5.15.0)
|
16
26
|
public_suffix (4.0.6)
|
27
|
+
tzinfo (2.0.4)
|
28
|
+
concurrent-ruby (~> 1.0)
|
17
29
|
|
18
30
|
PLATFORMS
|
19
31
|
ruby
|
@@ -22,4 +34,4 @@ DEPENDENCIES
|
|
22
34
|
dns-sniper!
|
23
35
|
|
24
36
|
BUNDLED WITH
|
25
|
-
2.
|
37
|
+
2.3.7
|
data/README.md
CHANGED
@@ -51,8 +51,11 @@ hostnames.blacklist += DNSSniper::HostsImporter.new('https://pgl.yoyo.org/as/ser
|
|
51
51
|
# Blocklist is accessible as an Array
|
52
52
|
hostnames.blocklist
|
53
53
|
|
54
|
-
#
|
55
|
-
|
54
|
+
# List available formats
|
55
|
+
DNSSniper::Exporters.all
|
56
|
+
|
57
|
+
# Convert to desired format
|
58
|
+
hostnames.to_unbound
|
56
59
|
```
|
57
60
|
|
58
61
|
### From CLI
|
data/dns-sniper.gemspec
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = 'dns-sniper'
|
5
5
|
spec.license = 'MIT'
|
6
|
-
spec.version = '0.0.1.
|
7
|
-
spec.date = '
|
6
|
+
spec.version = '0.0.1.pre10'
|
7
|
+
spec.date = '2022-03-07'
|
8
8
|
|
9
9
|
spec.authors = ['Brody Hoskins']
|
10
10
|
spec.email = ['brody@brody.digital']
|
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.bindir = 'bin'
|
31
31
|
spec.executables = 'dns-sniper'
|
32
32
|
|
33
|
+
spec.add_dependency 'activesupport', '>= 4.2', '< 7.0.3'
|
33
34
|
spec.add_dependency 'down', '~> 5.1'
|
34
35
|
spec.add_dependency 'hosts_file', '~> 1.0'
|
35
36
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DNSSniper
|
4
|
+
class ConditionalFetch
|
5
|
+
attr_reader :data, :headers, :response
|
6
|
+
|
7
|
+
def initialize(uri, options = {})
|
8
|
+
@options = options
|
9
|
+
@uri = uri
|
10
|
+
@uri = CGI.escape_html(@uri).to_s
|
11
|
+
@uri = URI.parse(@uri)
|
12
|
+
|
13
|
+
@data = nil
|
14
|
+
@headers = nil
|
15
|
+
@response = nil
|
16
|
+
|
17
|
+
fetch
|
18
|
+
end
|
19
|
+
|
20
|
+
# Headers
|
21
|
+
|
22
|
+
def etag
|
23
|
+
@etag ||= @headers&.dig('etag')
|
24
|
+
end
|
25
|
+
|
26
|
+
def expires_at
|
27
|
+
@expires_at ||= !date || !max_age ? nil : (date + max_age)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def fetch
|
33
|
+
last_etag = @options[:last_etag]
|
34
|
+
last_updated_at = @options[:last_updated_at]
|
35
|
+
|
36
|
+
http = Net::HTTP.new(@uri.host, @uri.port)
|
37
|
+
http.use_ssl = true if @uri.instance_of?(URI::HTTPS)
|
38
|
+
|
39
|
+
if @options[:last_updated_at] || @options[:last_etag]
|
40
|
+
response = http.head(@uri.request_uri)
|
41
|
+
if response.is_a?(Net::HTTPSuccess)
|
42
|
+
@headers = response&.each_header.to_h
|
43
|
+
@response = response
|
44
|
+
@data = response&.body
|
45
|
+
|
46
|
+
return self unless update?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
request = Net::HTTP::Get.new(@uri.request_uri)
|
51
|
+
response = http.request(request)
|
52
|
+
|
53
|
+
@headers = response.each_header&.to_h
|
54
|
+
@response = response
|
55
|
+
@data = response&.body
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def update?
|
61
|
+
last_etag = @options[:last_etag]
|
62
|
+
last_updated_at = @options[:last_updated_at]
|
63
|
+
|
64
|
+
return last_etag != etag if last_etag && etag && etag.strip != ''
|
65
|
+
|
66
|
+
if last_updated_at && created_at
|
67
|
+
if max_age
|
68
|
+
return (last_updated_at + max_age) < created_at
|
69
|
+
elsif age
|
70
|
+
return (last_updated_at + age) < created_at
|
71
|
+
else
|
72
|
+
return last_updated_at < created_at
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
# Headers
|
80
|
+
|
81
|
+
def age
|
82
|
+
@age ||= @headers&.dig('age')&.to_i
|
83
|
+
end
|
84
|
+
|
85
|
+
def cache_control
|
86
|
+
return @cache_control if @cache_control
|
87
|
+
|
88
|
+
header = @headers&.dig('cache-control')
|
89
|
+
@cache_control = if !header || !header.include?('max-age=')
|
90
|
+
nil
|
91
|
+
else
|
92
|
+
header
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def created_at
|
97
|
+
!date || !age ? nil : (date - age)
|
98
|
+
end
|
99
|
+
|
100
|
+
def date
|
101
|
+
return @date if @date
|
102
|
+
|
103
|
+
date = @headers&.dig('date')
|
104
|
+
@date = date ? Time.rfc2822(date) : nil
|
105
|
+
end
|
106
|
+
|
107
|
+
def max_age
|
108
|
+
@max_age ||= cache_control ? cache_control.split('=')[1]&.to_i : nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/dns-sniper/hostnames.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'down'
|
4
|
-
require 'hosts_file'
|
5
|
-
require 'open-uri'
|
6
|
-
require 'resolv'
|
7
|
-
|
8
3
|
module DNSSniper
|
9
4
|
class Hostnames
|
10
|
-
attr_accessor :blacklist
|
11
|
-
|
5
|
+
attr_accessor :blacklist, :whitelist
|
6
|
+
|
7
|
+
Exporters.all.each do |exporter|
|
8
|
+
format = exporter.to_s.split('::').last[..-9].tableize.singularize
|
9
|
+
|
10
|
+
define_method(:"to_#{format}") { exporter.new(blocklist).data }
|
11
|
+
end
|
12
12
|
|
13
13
|
def initialize
|
14
14
|
@blacklist = []
|
@@ -24,6 +24,7 @@ module DNSSniper
|
|
24
24
|
domain_parts = domain.split('.')
|
25
25
|
domain_parts.count.times do |count|
|
26
26
|
next if count == 1
|
27
|
+
|
27
28
|
whitelist += [domain_parts[count - 1, domain_parts.length].join('.')]
|
28
29
|
end
|
29
30
|
end
|
data/lib/dns-sniper/importer.rb
CHANGED
@@ -4,16 +4,16 @@ module DNSSniper
|
|
4
4
|
class Importer
|
5
5
|
attr_accessor :uri, :hostnames
|
6
6
|
|
7
|
-
def initialize(uri,
|
7
|
+
def initialize(uri, options = {})
|
8
8
|
@uri = uri
|
9
|
-
@hostnames = File.exist?(uri) ? import_file(uri) : import_uri(uri)
|
9
|
+
@hostnames = File.exist?(uri) ? import_file(uri, options) : import_uri(uri, options)
|
10
10
|
end
|
11
11
|
|
12
12
|
def import_file(*)
|
13
13
|
raise NotImplementedError, "#{self.class.name}: #import_file not supported"
|
14
14
|
end
|
15
15
|
|
16
|
-
def import_uri(
|
16
|
+
def import_uri(_uri, _options = {})
|
17
17
|
raise NotImplementedError, "#{self.class.name}: #import_uri not supported"
|
18
18
|
end
|
19
19
|
|
@@ -39,7 +39,7 @@ module DNSSniper
|
|
39
39
|
return false if domain.include?('?')
|
40
40
|
return false if ip_addr?(domain)
|
41
41
|
return false unless /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/.match?(domain)
|
42
|
-
return false unless /^[a-z0-9]+([
|
42
|
+
return false unless /^[a-z0-9]+([\-.]{1}[a-z0-9]+)*\.[a-z]{2,5}$/.match?(domain)
|
43
43
|
|
44
44
|
begin
|
45
45
|
return false if URI.parse(domain).is_a?(URI::HTTP)
|
@@ -9,15 +9,15 @@ module DNSSniper
|
|
9
9
|
|
10
10
|
def import_file(path, list:)
|
11
11
|
raise ArgumentError, "#{self.class.name}: #from_path requies list to be defined" unless list
|
12
|
-
return []
|
12
|
+
return [] unless File.exist?(path)
|
13
13
|
|
14
14
|
yaml = YAML.safe_load(File.read(path), permitted_classes: [Symbol])
|
15
|
-
return []
|
15
|
+
return [] unless yaml.dig(:sources)&.dig(list.to_sym)
|
16
16
|
|
17
|
-
hostnames = []
|
17
|
+
hostnames = []
|
18
18
|
yaml.dig(:sources).dig(list.to_sym).each do |source|
|
19
|
-
return []
|
20
|
-
return []
|
19
|
+
return [] unless source.dig(:importer)
|
20
|
+
return [] unless source.dig(:uri)
|
21
21
|
|
22
22
|
importer = DNSSniper.const_get("#{source.dig(:importer).to_s.split('_').map(&:capitalize).join}Importer")
|
23
23
|
if !importer
|
@@ -2,21 +2,17 @@
|
|
2
2
|
|
3
3
|
module DNSSniper
|
4
4
|
class DomainsImporter < Importer
|
5
|
-
def import_file(path)
|
6
|
-
return []
|
5
|
+
def import_file(path, *)
|
6
|
+
return [] unless File.exist?(path)
|
7
7
|
|
8
|
-
File.open(path).readlines(chomp: true).map { |hostname| clean(hostname) }.reject { |hostname| rejector(hostname) }
|
8
|
+
File.open(path).readlines(chomp: true).map { |hostname| clean(hostname) }.reject { |hostname| rejector(hostname) }
|
9
9
|
end
|
10
10
|
|
11
|
-
def import_uri(uri)
|
12
|
-
|
13
|
-
|
14
|
-
return down.readlines(chomp: true).map { |hostname| clean(hostname) }.reject { |hostname| rejector(hostname) }.to_set
|
15
|
-
rescue Down::InvalidUrl => e
|
16
|
-
warn "#{self.class.name}: #{e}"
|
17
|
-
end
|
11
|
+
def import_uri(uri, options = {})
|
12
|
+
data = ConditionalFetch.new(uri, options).data
|
13
|
+
return [] unless data
|
18
14
|
|
19
|
-
|
15
|
+
data.split(/\n+|\r+/).reject(&:empty?)
|
20
16
|
end
|
21
17
|
end
|
22
18
|
end
|
@@ -3,24 +3,19 @@
|
|
3
3
|
module DNSSniper
|
4
4
|
class HostsImporter < Importer
|
5
5
|
def import_file(path, *)
|
6
|
-
return []
|
6
|
+
return [] unless File.exist?(path)
|
7
7
|
|
8
|
-
HostsFile.load(path).map(&:name).map { |hostname| clean(hostname) }.reject { |hostname| rejector(hostname) }
|
8
|
+
HostsFile.load(path).map(&:name).map { |hostname| clean(hostname) }.reject { |hostname| rejector(hostname) }
|
9
9
|
end
|
10
10
|
|
11
|
-
def import_uri(uri,
|
12
|
-
|
13
|
-
down = Down.download(uri)
|
14
|
-
path = down.path
|
15
|
-
rescue Down::InvalidUrl => e
|
16
|
-
warn "#{self.class.name}: #{e}"
|
17
|
-
end
|
11
|
+
def import_uri(uri, options = {})
|
12
|
+
data = ConditionalFetch.new(uri, options).data || ''
|
18
13
|
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
arr = []
|
15
|
+
domains = HostsFile::Parser.new(data)
|
16
|
+
domains.each { |domain| arr << domain.name }
|
22
17
|
|
23
|
-
|
18
|
+
arr
|
24
19
|
end
|
25
20
|
end
|
26
21
|
end
|
data/lib/dns-sniper.rb
CHANGED
@@ -1,10 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/all'
|
4
|
+
require 'cgi'
|
5
|
+
require 'down'
|
6
|
+
require 'hosts_file'
|
7
|
+
require 'net/http'
|
8
|
+
require 'net/https'
|
9
|
+
require 'open-uri'
|
3
10
|
require 'resolv'
|
11
|
+
require 'time'
|
4
12
|
require 'yaml'
|
5
13
|
|
14
|
+
require 'dns-sniper/conditional_fetch'
|
6
15
|
require 'dns-sniper/exporter'
|
7
16
|
require 'dns-sniper/exporters'
|
8
17
|
require 'dns-sniper/hostnames'
|
9
18
|
require 'dns-sniper/importer'
|
10
|
-
require 'dns-sniper/importers'
|
19
|
+
require 'dns-sniper/importers'
|
metadata
CHANGED
@@ -1,15 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dns-sniper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.
|
4
|
+
version: 0.0.1.pre10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brody Hoskins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 7.0.3
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 7.0.3
|
13
33
|
- !ruby/object:Gem::Dependency
|
14
34
|
name: down
|
15
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -57,6 +77,7 @@ files:
|
|
57
77
|
- config.yml.example
|
58
78
|
- dns-sniper.gemspec
|
59
79
|
- lib/dns-sniper.rb
|
80
|
+
- lib/dns-sniper/conditional_fetch.rb
|
60
81
|
- lib/dns-sniper/exporter.rb
|
61
82
|
- lib/dns-sniper/exporters.rb
|
62
83
|
- lib/dns-sniper/exporters/bind8_exporter.rb
|
@@ -92,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
113
|
- !ruby/object:Gem::Version
|
93
114
|
version: 1.3.1
|
94
115
|
requirements: []
|
95
|
-
rubygems_version: 3.
|
116
|
+
rubygems_version: 3.4.19
|
96
117
|
signing_key:
|
97
118
|
specification_version: 4
|
98
119
|
summary: Combine DNS blacklists into desired configuration format
|