dns-sniper 0.0.1.pre2 → 0.0.1.pre7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e0cfd33b8c953620c67bd40ea77e26ed12f7a9a10716918ee130a87a8617975
4
- data.tar.gz: 9c74827c20d2d3a6cef9ca2876eb9659475401088e05d963778eed19c6d11374
3
+ metadata.gz: fdb713f65960730a1ac99906c65aeb79b30b69514eedab948ee20eb27bf30920
4
+ data.tar.gz: 28d04385a9759e9bada850e6c4dde26d7f3d983ddedd9d5782d31c6001cab19d
5
5
  SHA512:
6
- metadata.gz: 57d9eaeeb39531a8ec3a5ca9bc0ea21e4dd8277c97b7ecb01fbd49f3109db725efc58096ce60768fb20585d19de26880e34c97e10f2011d2e4ea4e58d09d8d00
7
- data.tar.gz: e673010ec9e40910411c4cb63f9f018c70faff0704bc96fc060900cf1188d74df37571821509e06bc98b7092c31b6cf0c8fab615f78fe086ecf892f199ddbbd8
6
+ metadata.gz: c02d1bb299780f04041d8899aeb72b7457ec8954184c95a0bae61731c1b36763f44055b41d4e8115c42bf0ccfdb68b407db3aef5ff71a3dc0e01d8dde0bcee99
7
+ data.tar.gz: fd3f08b34762d39e3610ad23810736a667e83f7b77459d8508e625f67007b0fa7adcd028091c65c4f05e28f39eacc6d805313e6733c9d8592f4657b5ade50e2b
data/Gemfile.lock CHANGED
@@ -1,26 +1,25 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sniper (0.0.1)
4
+ dns-sniper (0.0.1.pre6)
5
+ down (~> 5.1)
6
+ hosts_file (~> 1.0)
5
7
 
6
8
  GEM
7
9
  remote: https://rubygems.org/
8
10
  specs:
9
11
  addressable (2.7.0)
10
12
  public_suffix (>= 2.0.2, < 5.0)
11
- down (5.1.1)
13
+ down (5.2.2)
12
14
  addressable (~> 2.5)
13
15
  hosts_file (1.0.3)
14
- public_suffix (4.0.5)
16
+ public_suffix (4.0.6)
15
17
 
16
18
  PLATFORMS
17
19
  ruby
18
20
 
19
21
  DEPENDENCIES
20
- bundler (~> 2.1.2)
21
- down (~> 5.1)
22
- hosts_file (~> 1.0)
23
- sniper!
22
+ dns-sniper!
24
23
 
25
24
  BUNDLED WITH
26
25
  2.1.2
data/README.md CHANGED
@@ -40,26 +40,19 @@ require 'dns-sniper'
40
40
 
41
41
  hostnames = DNSSniper::Hostnames.new
42
42
 
43
- # Block domain names
44
- hostnames.add_from('https://pgl.yoyo.org/as/serverlist.php?hostformat=hosts;showintro=0;mimetype=plaintext') # From the web
45
- hostnames.add_from('https://raw.githubusercontent.com/brodyhoskins/dns-blocklists/master/tracking.list')
46
- hostnames.add_from('~/.config/dns-sniper/blocklists.list') # From filesystem
47
-
48
- # Manually add domain name
49
- hostnames.add('ads.yahoo.com')
50
- hostnames.add(['ads.doubleclick.net', 'ads.msn.com'])
51
-
52
- # Remove whitelisted domain names
53
- hostnames.remove_from('~/.config/dns-sniper/whitelisted-hostnames.list')
54
- hostnames.remove_from('https://example.com/whitelisted.hosts')
55
-
56
- # Manually remove domain name
57
- hostnames.remove('favoritewebsite.com')
58
- hostnames.remove(['favoritewebsite.com', 'otherfavoritewebsite.com'])
59
-
60
- # Convert to configuration file
61
- hostnames.to_format('dnsmasq')
62
- hostnames.to_format('unbound')
43
+ # Manually add blacklisted or whitelisted domains
44
+ hostnames.blacklist += 'ads.yahoo.com'
45
+ hostnames.whitelist += 'favoritewebsite.com'
46
+
47
+ # Use an Importer to process external lists
48
+ hostnames.blacklist += DNSSniper::DomainsImporter.new('https://raw.githubusercontent.com/brodyhoskins/dns-blocklists/master/tracking.list').hostnames
49
+ hostnames.blacklist += DNSSniper::HostsImporter.new('https://pgl.yoyo.org/as/serverlist.php?hostformat=hosts;showintro=0;mimetype=plaintext').hostnames
50
+
51
+ # Blocklist is accessible as an Array
52
+ hostnames.blocklist
53
+
54
+ # Use an Exporter to convert to other formats
55
+ UnboundExporter.new(hostnames.blocklist).data
63
56
  ```
64
57
 
65
58
  ### From CLI
@@ -71,7 +64,6 @@ Using the CLI version makes it easy to update configuration formats automaticall
71
64
  ```bash
72
65
  #!/usr/bin/env bash
73
66
 
74
- /path/to/dns-sniper -f ~/.config/dns-sniper/blacklist.list -w ~/.config/dns-sniper/whitelist.list -o unbound > /etc/unbound/unbound.conf.t/blocklist.conf
75
-
67
+ /path/to/dns-sniper --conf ~/.config/dns-sniper/dns-sniper.yml --output unbound > /etc/unbound/unbound.conf.d/blocklist.conf
76
68
  service unbound reload
77
69
  ```
data/bin/dns-sniper CHANGED
@@ -5,19 +5,16 @@ require 'optparse'
5
5
  require 'set'
6
6
  require 'dns-sniper'
7
7
 
8
- VALID_FORMATS = DNSSniper::Formatters.all.inject([]) { |arr, f| arr << f.name.to_s.sub('DNSSniper::', '').downcase }
9
- Options = Struct.new(:blacklist_urls_file, :whitelisted_hosts_file, :format)
8
+ VALID_FORMATS = DNSSniper::Exporters.all.inject([]) { |arr, f| arr << f.name.to_s.sub('DNSSniper::', '').sub('Exporter', '').downcase }
9
+ Options = Struct.new(:conf_path, :format)
10
10
 
11
11
  class Parser
12
12
  def self.parse(options)
13
13
  args = Options.new('world')
14
14
  opt_parser = OptionParser.new do |opts|
15
15
  opts.banner = "Usage: #{File.basename(__FILE__)} -f PATH -o FORMAT [options]\n#{File.basename(__FILE__)} combines online DNS blacklists and combines them into the desired configuration FORMAT.\n\n"
16
- opts.on('-f', '--blacklist=PATH', 'PATH to file with line-separated list of URL’s of blacklists') do |path|
17
- args.blacklist_urls_file = path
18
- end
19
- opts.on('-w', '--whitelist=path', 'Path to file with line-separated list of whitelisted hostnames') do |path|
20
- args.whitelisted_hosts_file = path
16
+ opts.on('-c', '--conf=PATH', 'PATH to YAML configuration file') do |path|
17
+ args.conf_path = path
21
18
  end
22
19
  opts.on('-o', '--output=FORMAT', "FORMAT to output — one of #{VALID_FORMATS.join(', ')}") do |format|
23
20
  args.format = format
@@ -34,20 +31,20 @@ end
34
31
 
35
32
  options = Parser.parse ARGV
36
33
 
37
- unless VALID_FORMATS.include?(options[:format])
38
- warn options[:format].blank? ? 'Error: Format not defined' : "Error: Invalid format \"#{options[:format]}\""
34
+ unless File.exist?(options[:conf_path])
35
+ warn "Error: Unable to access ”#{options[:conf_path]}"
39
36
  exit(-1)
40
37
  end
41
38
 
42
- unless File.exist?(options[:blacklist_urls_file])
43
- warn "Error: Unable to access ”#{options[:blacklist_urls_file]}”"
44
- exit(-1)
45
- end
46
-
47
- if options[:whitelisted_hosts_file] && !File.exist?(options[:whitelisted_hosts_file])
48
- warn "Error: Unable to access ”#{options[:whitelisted_hosts_file]}”"
39
+ exporter = DNSSniper.const_get("#{options[:format].to_s.split('_').map(&:capitalize).join}Exporter")
40
+ if !exporter
41
+ warn "Error: Invalid format #{options[:format]}"
49
42
  exit(-1)
50
43
  end
51
44
 
52
45
  hostnames = DNSSniper::Hostnames.new
53
- puts hostnames.add_from(File.open(options[:blacklist_urls_file]).readlines).remove_from(options[:whitelisted_hosts_file]).to_format(options[:format])
46
+ hostnames.blacklist += DNSSniper::ConfigurationImporter.new(options[:conf_path], list: :reject).hostnames
47
+ hostnames.whitelist += DNSSniper::ConfigurationImporter.new(options[:conf_path], list: :allow).hostnames
48
+
49
+ exporter = exporter.new(hostnames.blocklist)
50
+ puts exporter.data
@@ -0,0 +1,9 @@
1
+ :sources:
2
+ :allow:
3
+ - :importer: :domains
4
+ :uri: https://example.com/whitelisted.domains.list
5
+ :reject:
6
+ - :importer: :domains
7
+ :uri: https://raw.githubusercontent.com/brodyhoskins/dns-blocklists/master/tracking.list
8
+ - :importer: :hosts
9
+ :uri: https://pgl.yoyo.org/as/serverlist.php?hostformat=hosts;showintro=0;mimetype=plaintext
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.pre2'
7
- spec.date = '2020-11-01'
6
+ spec.version = '0.0.1.pre7'
7
+ spec.date = '2021-07-26'
8
8
 
9
9
  spec.authors = ['Brody Hoskins']
10
10
  spec.email = ['brody@brody.digital']
data/lib/dns-sniper.rb CHANGED
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'resolv'
4
+ require 'yaml'
5
+
6
+ require 'dns-sniper/exporter'
7
+ require 'dns-sniper/exporters'
3
8
  require 'dns-sniper/hostnames'
4
- require 'dns-sniper/formatter'
5
- require 'dns-sniper/formatters'
9
+ require 'dns-sniper/importer'
10
+ require 'dns-sniper/importers'
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DNSSniper
4
- class Formatter
4
+ class Exporter
5
+ attr_accessor :data, :hostnames
6
+
5
7
  def initialize(hostnames, options = {})
6
8
  @hostnames = hostnames
7
- @options = options
9
+ @data = output(options)
8
10
  end
9
11
 
10
- def output
12
+ def output(*)
11
13
  raise NotImplementedError, "Error: #output isn’t supported by #{self.class.name}"
12
14
  end
13
15
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ module Exporters
5
+ module_function
6
+
7
+ attr_reader :registered
8
+ @registered = []
9
+
10
+ def register(class_name, autoload_require)
11
+ DNSSniper.autoload(class_name, autoload_require)
12
+ @registered << class_name
13
+ end
14
+
15
+ def all
16
+ @registered.map { |name| DNSSniper.const_get(name) }
17
+ end
18
+
19
+ def find(name)
20
+ all.find { |c| c.name.downcase == name.to_s.downcase } or raise NameError, "Unknown exporter \"#{name}\""
21
+ end
22
+ end
23
+ end
24
+
25
+ DNSSniper::Exporters.register :Bind8Exporter, 'dns-sniper/exporters/bind8_exporter'
26
+ DNSSniper::Exporters.register :DnsmasqExporter, 'dns-sniper/exporters/dnsmasq_exporter'
27
+ DNSSniper::Exporters.register :HostsExporter, 'dns-sniper/exporters/hosts_exporter'
28
+ DNSSniper::Exporters.register :NetgearExporter, 'dns-sniper/exporters/netgear_exporter'
29
+ DNSSniper::Exporters.register :TextExporter, 'dns-sniper/exporters/text_exporter'
30
+ DNSSniper::Exporters.register :UnboundExporter, 'dns-sniper/exporters/unbound_exporter'
@@ -1,18 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DNSSniper
4
- class Bind8 < Formatter
5
- def initialize(hostnames, options = {})
6
- @hostnames = hostnames
7
- @options = options
8
- end
9
-
4
+ class Bind8Exporter < Exporter
10
5
  def output(options = {})
11
6
  raise ArgumentError, 'zone_file is required' unless defined?(options[:zone_file])
12
7
 
13
8
  str = ''.dup
14
9
  @hostnames.each do |hostname|
15
- str << "zone \"#{hostname}\" { type master; notify no; file \"#{options[:zone_file]}\"; };#{$INPUT_RECORD_SEPARATOR}"
10
+ str << "zone \"#{hostname}\" { type master; notify no; file \"#{options[:zone_file]}\"; };#{$/}"
16
11
  end
17
12
  str
18
13
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ class DnsmasqExporter < Exporter
5
+ def output(*)
6
+ str = ''.dup
7
+ @hostnames.each do |hostname|
8
+ str << "server=/#{hostname}/#{$/}"
9
+ end
10
+ str
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ class HostsExporter < Exporter
5
+ def output(*)
6
+ str = ''.dup
7
+ @hostnames.each do |hostname|
8
+ str << "127.0.0.1\t#{hostname}#{$/}"
9
+ end
10
+ str
11
+ end
12
+ end
13
+ end
@@ -1,13 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DNSSniper
4
- class Netgear < Formatter
5
- def initialize(hostnames, options = {})
6
- @hostnames = hostnames
7
- @options = options
8
- end
9
-
10
- def output(_options = {})
4
+ class NetgearExporter < Exporter
5
+ def output(*)
11
6
  str = ''.dup
12
7
  @hostnames.each_with_index do |hostname, i|
13
8
  str << "[517003_e]: #{i + 1}) #{hostname}\n"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ class TextExporter < Exporter
5
+ def output(_options = {})
6
+ @hostnames.to_a.join($/)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ class UnboundExporter < Exporter
5
+ def output(*)
6
+ str = ''.dup
7
+ str << "server:#{$/}"
8
+ @hostnames.each do |hostname|
9
+ str << " local-zone: \"#{hostname}\" static#{$/}"
10
+ end
11
+ str
12
+ end
13
+ end
14
+ end
@@ -7,151 +7,28 @@ require 'resolv'
7
7
 
8
8
  module DNSSniper
9
9
  class Hostnames
10
- def initialize(_options = {})
11
- @hostnames = [].to_set
12
- self
13
- end
14
-
15
- def add(hostnames)
16
- hostnames = clean(hostnames.class == String ? [hostnames] : hostnames)
17
- @hostnames += hostnames unless hostnames.empty?
18
- self
19
- end
20
-
21
- def add_from(paths_or_urls)
22
- return self unless paths_or_urls
23
-
24
- paths_or_urls = [paths_or_urls] if paths_or_urls.class == String
25
-
26
- paths_or_urls.each do |path_or_url|
27
- path_or_url = path_or_url.strip
28
-
29
- if File.exist?(path_or_url)
30
- contents = File.open(path_or_url).readlines
31
- else
32
- begin
33
- down = Down.download(path_or_url)
34
- path_or_url = down.path
35
- contents = down.readlines
36
- rescue Down::NotFound
37
- warn "\"#{path_or_url}\" does not exist"
38
- return self
39
- rescue Down::ResponseError
40
- warn "\"#{path_or_url}\": No data from server"
41
- return self
42
- end
43
- end
44
-
45
- case syntax(path_or_url, contents)
46
- when nil
47
- warn "Error: Syntax: Syntax of \"#{path_or_url}\" not recognized, ignored"
48
- return self
49
- when 'hosts'
50
- add from_hosts_file(path_or_url)
51
- when 'hostnames'
52
- add from_hostnames_file(contents)
53
- end
54
- end
55
-
56
- self
57
- end
58
-
59
- def remove(hostnames)
60
- hostnames = clean(hostnames.class == String ? [hostnames] : hostnames)
61
- @hostnames -= hostnames unless hostnames.empty?
62
- self
63
- end
64
-
65
- def remove_from(paths_or_urls)
66
- return self unless paths_or_urls
67
-
68
- paths_or_urls = [paths_or_urls] if paths_or_urls.class == String
69
-
70
- paths_or_urls.each do |path_or_url|
71
- path_or_url = path_or_url.strip
72
-
73
- if File.exist?(path_or_url)
74
- contents = File.open(path_or_url).readlines
75
- else
76
- begin
77
- down = Down.download(path_or_url)
78
- path_or_url = down.path
79
- contents = down.readlines
80
- rescue Down::NotFound
81
- warn "\"#{path_or_url}\" does not exist"
82
- return self
83
- rescue Down::ResponseError
84
- warn "\"#{path_or_url}\": No data from server"
85
- return self
86
- end
87
- end
88
-
89
- case syntax(path_or_url, contents)
90
- when nil
91
- warn "Error: Syntax: Syntax of \"#{path_or_url}\" not recognized, ignored"
92
- return self
93
- when 'hosts'
94
- remove from_hosts_file(path_or_url)
95
- when 'hostnames'
96
- remove from_hostnames_file(contents)
97
- end
98
- end
10
+ attr_accessor :blacklist
11
+ attr_accessor :whitelist
99
12
 
13
+ def initialize
14
+ @blacklist = []
15
+ @whitelist = []
100
16
  self
101
17
  end
102
18
 
103
- def to_format(format, options = {})
104
- format = format.capitalize
105
- begin
106
- klass = DNSSniper.const_get(format)
107
- klass.new(@hostnames.to_a).output(options)
108
- rescue NameError
109
- false
110
- end
111
- end
112
-
113
- def to_a
114
- @hostnames.to_a
115
- end
116
-
117
- private
19
+ def blocklist
20
+ blacklist = @blacklist
21
+ whitelist = @whitelist
118
22
 
119
- def clean(hostnames)
120
- cleaned_hostnames = []
121
- hostnames.each do |hostname|
122
- hostname = hostname.downcase.strip
123
- hostname = hostname.sub('www.', '')
124
- hostname_top_domain = "#{hostname.split('.')[-2]}.#{hostname.split('.')[-1]}"
125
-
126
- if !hostname.include?('#') && !['broadcasthost', 'localhost', ''].include?(hostname) && !@hostnames.include?(hostname_top_domain)
127
- cleaned_hostnames << hostname
128
- end
129
- end
130
- cleaned_hostnames
131
- end
132
-
133
- def syntax(path_or_url, contents)
134
- contents.each do |line|
135
- next if line.include?('#')
136
-
137
- line = line.downcase
138
-
139
- if line.strip.split(/\s/).first =~ Regexp.union([Resolv::IPv4::Regex, Resolv::IPv6::Regex])
140
- return 'hosts'
141
- elsif line.include?('.') && (!line.include? 'http') && path_or_url.end_with?('.list')
142
- return 'hostnames'
23
+ whitelist.each do |domain|
24
+ domain_parts = domain.split('.')
25
+ domain_parts.count.times do |count|
26
+ next if count == 1
27
+ whitelist += [domain_parts[count - 1, domain_parts.length].join('.')]
143
28
  end
144
29
  end
145
- nil
146
- end
147
-
148
- def from_hosts_file(path_or_url)
149
- # TODO: Remove downloading file twice
150
- HostsFile.load(path_or_url).map(&:name)
151
- end
152
30
 
153
- def from_hostnames_file(contents)
154
- contents.each { |line| add(line) }
31
+ blacklist - whitelist
155
32
  end
156
33
  end
157
34
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ class Importer
5
+ attr_accessor :uri, :hostnames
6
+
7
+ def initialize(uri, *)
8
+ @uri = uri
9
+ @hostnames = File.exist?(uri) ? import_file(uri) : import_uri(uri)
10
+ end
11
+
12
+ def import_file(*)
13
+ raise NotImplementedError, "#{self.class.name}: #import_file not supported"
14
+ end
15
+
16
+ def import_uri(*)
17
+ raise NotImplementedError, "#{self.class.name}: #import_uri not supported"
18
+ end
19
+
20
+ # Helper methods
21
+
22
+ def clean(domain)
23
+ domain = domain.split('#')[0] if domain[0] != '#' && domain.include?('#')
24
+ domain = domain.split(':')[0] if domain.include?(':') && !ip_addr?(domain)
25
+ domain = domain.sub('www.', '') if domain.start_with?('www.') && domain.scan('www.').count == 1
26
+
27
+ domain.chomp.gsub(/\s+/, '').downcase
28
+ end
29
+
30
+ def rejector(domain)
31
+ !domain?(domain)
32
+ end
33
+
34
+ def domain?(domain)
35
+ return false if domain == ''
36
+ return false if domain.gsub('#', '').gsub(/\s+/, '').empty?
37
+ return false if domain[0] == '#'
38
+ return false if domain.include?(':')
39
+ return false if domain.include?('?')
40
+ return false if ip_addr?(domain)
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]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}$/.match?(domain)
43
+
44
+ begin
45
+ return false if URI.parse(domain).is_a?(URI::HTTP)
46
+ rescue URI::InvalidURIError; end
47
+ true
48
+ end
49
+
50
+ def ip_addr?(domain)
51
+ domain =~ Regexp.union([Resolv::IPv4::Regex, Resolv::IPv6::Regex])
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ module Importers
5
+ module_function
6
+
7
+ attr_reader :registered
8
+ @registered = []
9
+
10
+ def register(class_name, autoload_require)
11
+ DNSSniper.autoload(class_name, autoload_require)
12
+ @registered << class_name
13
+ end
14
+
15
+ def all
16
+ @registered.map { |name| DNSSniper.const_get(name) }
17
+ end
18
+
19
+ def find(name)
20
+ all.find { |c| c.name.downcase == name.to_s.downcase } or raise NameError, "Unknown Importer \"#{name}\""
21
+ end
22
+ end
23
+ end
24
+
25
+ DNSSniper::Importers.register :ConfigurationImporter, 'dns-sniper/importers/configuration_importer'
26
+ DNSSniper::Importers.register :DomainsImporter, 'dns-sniper/importers/domains_importer'
27
+ DNSSniper::Importers.register :HostsImporter, 'dns-sniper/importers/hosts_importer'
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ class ConfigurationImporter < Importer
5
+ def initialize(uri, list:)
6
+ @uri = uri
7
+ @hostnames = File.exist?(uri) ? import_file(uri, list: list) : import_uri(uri, list: list)
8
+ end
9
+
10
+ def import_file(path, list:)
11
+ raise ArgumentError, "#{self.class.name}: #from_path requies list to be defined" unless list
12
+ return [].to_set unless File.exist?(path)
13
+
14
+ yaml = YAML.safe_load(File.read(path), permitted_classes: [Symbol])
15
+ return [].to_set unless yaml.dig(:sources)&.dig(list.to_sym)
16
+
17
+ hostnames = [].to_set
18
+ yaml.dig(:sources).dig(list.to_sym).each do |source|
19
+ return [].to_set unless source.dig(:importer)
20
+ return [].to_set unless source.dig(:uri)
21
+
22
+ importer = DNSSniper.const_get("#{source.dig(:importer).to_s.split('_').map(&:capitalize).join}Importer")
23
+ if !importer
24
+ next
25
+ else
26
+ importer = importer.new(source.dig(:uri))
27
+ hostnames += importer.hostnames
28
+ end
29
+ end
30
+
31
+ hostnames
32
+ end
33
+
34
+ def import_uri(_uri, list:)
35
+ raise NotImplementedError, "#{self.class.name}: #from_uri not supported"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ class DomainsImporter < Importer
5
+ def import_file(path)
6
+ return [].to_set unless File.exist?(path)
7
+
8
+ File.open(path).readlines(chomp: true).map { |hostname| clean(hostname) }.reject { |hostname| rejector(hostname) }.to_set
9
+ end
10
+
11
+ def import_uri(uri)
12
+ begin
13
+ down = Down.download(uri)
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
18
+
19
+ [].to_set
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DNSSniper
4
+ class HostsImporter < Importer
5
+ def import_file(path, *)
6
+ return [].to_set unless File.exist?(path)
7
+
8
+ HostsFile.load(path).map(&:name).map { |hostname| clean(hostname) }.reject { |hostname| rejector(hostname) }.to_set
9
+ end
10
+
11
+ def import_uri(uri, *)
12
+ begin
13
+ down = Down.download(uri)
14
+ path = down.path
15
+ rescue Down::InvalidUrl => e
16
+ warn "#{self.class.name}: #{e}"
17
+ end
18
+
19
+ if path
20
+ return HostsFile.load(path).map(&:name).map { |hostname| clean(hostname) }.reject { |hostname| rejector(hostname) }.to_set
21
+ end
22
+
23
+ [].to_set
24
+ end
25
+ end
26
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dns-sniper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre2
4
+ version: 0.0.1.pre7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brody Hoskins
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-01 00:00:00.000000000 Z
11
+ date: 2021-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: down
@@ -54,24 +54,30 @@ files:
54
54
  - README.md
55
55
  - Rakefile
56
56
  - bin/dns-sniper
57
+ - config.yml.example
57
58
  - dns-sniper.gemspec
58
59
  - lib/dns-sniper.rb
59
- - lib/dns-sniper/formatter.rb
60
- - lib/dns-sniper/formatters.rb
61
- - lib/dns-sniper/formatters/bind8.rb
62
- - lib/dns-sniper/formatters/dnsmasq.rb
63
- - lib/dns-sniper/formatters/hosts.rb
64
- - lib/dns-sniper/formatters/netgear.rb
65
- - lib/dns-sniper/formatters/text.rb
66
- - lib/dns-sniper/formatters/unbound.rb
60
+ - lib/dns-sniper/exporter.rb
61
+ - lib/dns-sniper/exporters.rb
62
+ - lib/dns-sniper/exporters/bind8_exporter.rb
63
+ - lib/dns-sniper/exporters/dnsmasq_exporter.rb
64
+ - lib/dns-sniper/exporters/hosts_exporter.rb
65
+ - lib/dns-sniper/exporters/netgear_exporter.rb
66
+ - lib/dns-sniper/exporters/text_exporter.rb
67
+ - lib/dns-sniper/exporters/unbound_exporter.rb
67
68
  - lib/dns-sniper/hostnames.rb
69
+ - lib/dns-sniper/importer.rb
70
+ - lib/dns-sniper/importers.rb
71
+ - lib/dns-sniper/importers/configuration_importer.rb
72
+ - lib/dns-sniper/importers/domains_importer.rb
73
+ - lib/dns-sniper/importers/hosts_importer.rb
68
74
  homepage: https://github.com/brodyhoskins/dns-sniper
69
75
  licenses:
70
76
  - MIT
71
77
  metadata:
72
78
  homepage_uri: https://github.com/brodyhoskins/dns-sniper
73
79
  source_code_uri: https://github.com/brodyhoskins/dns-sniper
74
- post_install_message:
80
+ post_install_message:
75
81
  rdoc_options: []
76
82
  require_paths:
77
83
  - lib
@@ -86,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
92
  - !ruby/object:Gem::Version
87
93
  version: 1.3.1
88
94
  requirements: []
89
- rubygems_version: 3.0.3
90
- signing_key:
95
+ rubygems_version: 3.2.15
96
+ signing_key:
91
97
  specification_version: 4
92
98
  summary: Combine DNS blacklists into desired configuration format
93
99
  test_files: []
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DNSSniper
4
- module Formatters
5
- extend self
6
- # module_function
7
-
8
- attr_reader :registered
9
- @registered = []
10
-
11
- def register(class_name, autoload_require)
12
- DNSSniper.autoload(class_name, autoload_require)
13
- @registered << class_name
14
- end
15
-
16
- def all
17
- @registered.map { |name| DNSSniper.const_get(name) }
18
- end
19
-
20
- def find(name)
21
- all.find { |c| c.name.downcase == name.to_s.downcase } or raise NameError, "Unknown formatter \"#{name}\""
22
- end
23
- end
24
- end
25
-
26
- DNSSniper::Formatters.register :Bind8, 'dns-sniper/formatters/bind8'
27
- DNSSniper::Formatters.register :Dnsmasq, 'dns-sniper/formatters/dnsmasq'
28
- DNSSniper::Formatters.register :Hosts, 'dns-sniper/formatters/hosts'
29
- DNSSniper::Formatters.register :Netgear, 'dns-sniper/formatters/netgear'
30
- DNSSniper::Formatters.register :Text, 'dns-sniper/formatters/text'
31
- DNSSniper::Formatters.register :Unbound, 'dns-sniper/formatters/unbound'
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DNSSniper
4
- class Dnsmasq < Formatter
5
- def initialize(hostnames, options = {})
6
- @hostnames = hostnames
7
- @options = options
8
- end
9
-
10
- def output(_options = {})
11
- str = ''.dup
12
- @hostnames.each do |hostname|
13
- str << "server=/#{hostname}/#{$INPUT_RECORD_SEPARATOR}"
14
- end
15
- str
16
- end
17
- end
18
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DNSSniper
4
- class Hosts < Formatter
5
- def initialize(hostnames, options = {})
6
- @hostnames = hostnames
7
- @options = options
8
- end
9
-
10
- def output(_options = {})
11
- str = ''.dup
12
- @hostnames.each do |hostname|
13
- str << "127.0.0.1\t#{hostname}#{$INPUT_RECORD_SEPARATOR}"
14
- end
15
- str
16
- end
17
- end
18
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DNSSniper
4
- class Text < Formatter
5
- def initialize(hostnames, options = {})
6
- @hostnames = hostnames
7
- @options = options
8
- end
9
-
10
- def output(_options = {})
11
- @hostnames.to_a.join($INPUT_RECORD_SEPARATOR)
12
- end
13
- end
14
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DNSSniper
4
- class Unbound < Formatter
5
- def initialize(hostnames, options = {})
6
- @hostnames = hostnames
7
- @options = options
8
- end
9
-
10
- def output(_options = {})
11
- str = ''.dup
12
- str << "server:#{$INPUT_RECORD_SEPARATOR}"
13
- @hostnames.each do |hostname|
14
- str << " local-zone: \"#{hostname}\" static#{$INPUT_RECORD_SEPARATOR}"
15
- end
16
- str
17
- end
18
- end
19
- end