dns-sniper 0.0.1.pre2 → 0.0.1.pre7

Sign up to get free protection for your applications and to get access to all the features.
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