dns-sniper 0.0.1pre

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9c6bab67f9579e0762d197d29dd188ced90277fa05573bb84e472be2c42a5a3e
4
+ data.tar.gz: 4123ba5a1c02a7f5d4972055b4b1743aae7bd4f995cd1336626a3f8a68ca7e23
5
+ SHA512:
6
+ metadata.gz: 479adbba0f5d557ef178acadc65461ee0be27684f4a4ab95bdd8f2b129a15b2583658fcea86193ee1c30519c5dba712b6c784619d79eaa345b7fcd993a1a19c1
7
+ data.tar.gz: b5ef2952418417f81d8c47b7cfe3052fe5e18242d5d87c3328f9713b5175bf94f48dc0ac3e3a9c8b4ddcc944637f2d8aea07fef00d15dc2b84c24e8f72fc3b8c
@@ -0,0 +1,9 @@
1
+ .DS_Store
2
+ .DS_Store?
3
+ ._*
4
+ .Spotlight-V100
5
+ .Trashes
6
+ ehthumbs.db
7
+ Thumbs.db
8
+
9
+ .ruby-version
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,26 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sniper (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.7.0)
10
+ public_suffix (>= 2.0.2, < 5.0)
11
+ down (5.1.1)
12
+ addressable (~> 2.5)
13
+ hosts_file (1.0.3)
14
+ public_suffix (4.0.5)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ bundler (~> 2.1.2)
21
+ down (~> 5.1)
22
+ hosts_file (~> 1.0)
23
+ sniper!
24
+
25
+ BUNDLED WITH
26
+ 2.1.2
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+ require "set"
5
+ require "dns-sniper"
6
+
7
+ VALID_FORMATS = ["text", "unbound"]
8
+ Options = Struct.new(:blacklist_urls_file, :whitelisted_hosts_file, :format)
9
+
10
+ class Parser
11
+ def self.parse(options)
12
+ args = Options.new("world")
13
+ opt_parser = OptionParser.new do |opts|
14
+ 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"
15
+ opts.on("-f", "--blacklist=PATH", "PATH to file with line-separated list of URL’s of blacklists") do |path|
16
+ args.blacklist_urls_file = path
17
+ end
18
+ opts.on("-w", "--whitelist=path", "Path to file with line-separated list of whitelisted hostnames") do |path|
19
+ args.whitelisted_hosts_file = path
20
+ end
21
+ opts.on("-o", "--output=FORMAT", "FORMAT to output — one of #{VALID_FORMATS.join(", ")}") do |format|
22
+ args.format = format
23
+ end
24
+ opts.on("-h", "--help", "Prints this help") do
25
+ puts opts
26
+ exit
27
+ end
28
+ end
29
+ opt_parser.parse!(options)
30
+ return args
31
+ end
32
+ end
33
+
34
+ options = Parser.parse ARGV
35
+
36
+ unless VALID_FORMATS.include?(options[:format])
37
+ STDERR.puts "Error: Format ”#{options[:format]}” unrecognized"
38
+ exit(-1)
39
+ end
40
+
41
+ unless File.exist?(options[:blacklist_urls_file])
42
+ STDERR.puts "Error: Unable to access ”#{options[:blacklist_urls_file]}”"
43
+ exit(-1)
44
+ end
45
+
46
+ if options[:whitelisted_hosts_file] and not File.exist?(options[:whitelisted_hosts_file])
47
+ STDERR.puts "Error: Unable to access ”#{options[:whitelisted_hosts_file]}”"
48
+ exit(-1)
49
+ end
50
+
51
+ hostnames = DNSSniper::Hostnames.new
52
+ puts hostnames.add_from(File.open(options[:blacklist_urls_file]).readlines).remove_from(options[:whitelisted_hosts_file]).to_format(options[:format])
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "dns-sniper"
3
+ spec.license = "MIT"
4
+ spec.version = "0.0.1pre"
5
+ spec.date = "2020-08-13"
6
+ spec.authors = ["Brody Hoskins"]
7
+ spec.email = ["brody@brody.digital"]
8
+
9
+ spec.summary = "Command line utility that combines online DNS blacklists and combines them into the desired configuration format"
10
+ spec.description = "Command line utility that combines online DNS blacklists and combines them into the desired configuration format"
11
+ spec.homepage = "https://github.com/brodyhoskins/dns-sniper"
12
+
13
+ spec.files = `git ls-files`.split($/)
14
+
15
+ spec.bindir = "bin"
16
+ spec.executables = "dns-sniper"
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_development_dependency "bundler", "~> 2.1.2"
20
+
21
+ spec.add_development_dependency "down", "~> 5.1"
22
+ spec.add_development_dependency "hosts_file", "~> 1.0"
23
+ end
@@ -0,0 +1,3 @@
1
+ require "dns-sniper/hostnames"
2
+ require "dns-sniper/formatter"
3
+ require "dns-sniper/formatters"
@@ -0,0 +1,12 @@
1
+ module DNSSniper
2
+ class Formatter
3
+ def initialize(hostnames, options = {})
4
+ @hostnames = hostnames
5
+ @options = options
6
+ end
7
+
8
+ def output
9
+ raise NotImplementedError, "Error: #output isn’t supported by #{self.class.name}"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module DNSSniper
2
+ module Formatters
3
+ extend self
4
+
5
+ attr_reader :registered
6
+ @registered = []
7
+
8
+ def register(class_name, autoload_require)
9
+ DNSSniper.autoload(class_name, autoload_require)
10
+ self.registered << class_name
11
+ end
12
+
13
+ def all
14
+ Sniper::Formatters.registered.map { |name| Sniper.const_get(name) }
15
+ end
16
+
17
+ def find(name)
18
+ all.find { |c| c.name.downcase == name.to_s.downcase } or raise NameError, "unknown carrier #{name}"
19
+ end
20
+ end
21
+ end
22
+
23
+ DNSSniper::Formatters.register :Text, "dns-sniper/formatters/text"
24
+ DNSSniper::Formatters.register :Unbound, "dns-sniper/formatters/unbound"
@@ -0,0 +1,12 @@
1
+ module DNSSniper
2
+ class Text < Formatter
3
+ def initialize(hostnames, options = {})
4
+ @hostnames = hostnames
5
+ @options = options
6
+ end
7
+
8
+ def output
9
+ @hostnames.to_a.join($/)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module DNSSniper
2
+ class Unbound < Formatter
3
+ def initialize(hostnames, options = {})
4
+ @hostnames = hostnames
5
+ @options = options
6
+ end
7
+
8
+ def output
9
+ str = "server:#{$/}"
10
+ @hostnames.each do |hostname|
11
+ str << " local-zone: \"#{hostname}\" static#{$/}"
12
+ end
13
+ str
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,176 @@
1
+ require "down"
2
+ require "hosts_file"
3
+ require "open-uri"
4
+ require "resolv"
5
+
6
+ module DNSSniper
7
+ class Hostnames
8
+ def initialize(options = {})
9
+ @hostnames = [].to_set
10
+ self
11
+ end
12
+
13
+ def add(hostname)
14
+ hostname = clean(hostname)
15
+ @hostnames << hostname if hostname
16
+ self
17
+ end
18
+
19
+ def add_many(hostnames)
20
+ hostnames.each { |hostname| add(hostname) }
21
+ self
22
+ end
23
+
24
+ def add_from(paths_or_urls)
25
+ return self unless paths_or_urls
26
+ if (paths_or_urls.class == String)
27
+ paths_or_urls = [paths_or_urls]
28
+ end
29
+
30
+ paths_or_urls.each do |path_or_url|
31
+ path_or_url = path_or_url.strip
32
+
33
+ if File.exist?(path_or_url)
34
+ contents = File.open(path_or_url).readlines
35
+ else
36
+ begin
37
+ down = Down.download(path_or_url)
38
+ path_or_url = down.path
39
+ contents = down.readlines
40
+ rescue Down::NotFound
41
+ STDERR.puts "\"#{path_or_url}\" does not exist"
42
+ return self
43
+ rescue Down::ResponseError
44
+ STDERR.puts "\"#{path_or_url}\": No data from server"
45
+ return self
46
+ end
47
+ end
48
+
49
+ case syntax(path_or_url, contents)
50
+ when nil
51
+ STDERR.puts "Error: Syntax: Syntax of \"#{path_or_url}\" not recognized, ignored"
52
+ return self
53
+ when "hosts"
54
+ add_many from_hosts_file(path_or_url)
55
+ when "hostnames"
56
+ add_many from_hostnames_file(contents)
57
+ end
58
+ end
59
+
60
+ self
61
+ end
62
+
63
+ def remove(hostname)
64
+ hostname = clean(hostname)
65
+ @hostnames = @hostnames - [hostname] if hostname
66
+ self
67
+ end
68
+
69
+ def remove_many(hostnames)
70
+ hostnames.each { |hostname| remove(hostname) }
71
+ self
72
+ end
73
+
74
+ def remove_from(paths_or_urls)
75
+ return self unless paths_or_urls
76
+ if (paths_or_urls.class == String)
77
+ paths_or_urls = [paths_or_urls]
78
+ end
79
+
80
+ paths_or_urls.each do |path_or_url|
81
+ path_or_url = path_or_url.strip
82
+
83
+ if File.exist?(path_or_url)
84
+ contents = File.open(path_or_url).readlines
85
+ else
86
+ begin
87
+ down = Down.download(path_or_url)
88
+ path_or_url = down.path
89
+ contents = down.readlines
90
+ rescue Down::NotFound
91
+ STDERR.puts "\"#{path_or_url}\" does not exist"
92
+ return self
93
+ rescue Down::ResponseError
94
+ STDERR.puts "\"#{path_or_url}\": No data from server"
95
+ return self
96
+ end
97
+ end
98
+
99
+ case syntax(path_or_url, contents)
100
+ when nil
101
+ STDERR.puts "Error: Syntax: Syntax of \"#{path_or_url}\" not recognized, ignored"
102
+ return self
103
+ when "hosts"
104
+ remove_many from_hosts_file(path_or_url)
105
+ when "hostnames"
106
+ remove_many from_hostnames_file(contents)
107
+ end
108
+ end
109
+
110
+ self
111
+ end
112
+
113
+ def to_format(format)
114
+ format = format.capitalize
115
+ begin
116
+ klass = Sniper.const_get(format)
117
+ klass.new(@hostnames.to_a).output
118
+ rescue NameError
119
+ return false
120
+ end
121
+ end
122
+
123
+ def to_a
124
+ @hostnames.to_a
125
+ end
126
+
127
+ def to_text
128
+ @hostnames.to_a.join("\n")
129
+ end
130
+
131
+ def to_unbound
132
+ str = "server:\n"
133
+ @hostnames.each do |hostname|
134
+ str << " local-zone: \"#{hostname}\" static\n"
135
+ end
136
+ str
137
+ end
138
+
139
+ private
140
+
141
+ def clean(hostname)
142
+ hostname = hostname.downcase.strip
143
+ hostname = hostname.sub("www.", "")
144
+ hostname_top_domain = "#{hostname.split(".")[-2]}.#{hostname.split(".")[-1]}"
145
+
146
+ if not hostname.include?("#") and not ["broadcasthost", "localhost", ""].include?(hostname) and not @hostnames.include?(hostname_top_domain)
147
+ hostname
148
+ else
149
+ nil
150
+ end
151
+ end
152
+
153
+ def syntax(path_or_url, contents)
154
+ contents.each do |line|
155
+ next if line.include?("#")
156
+ line = line.downcase
157
+
158
+ if line.strip.split(/\s/).first =~ Regexp.union([Resolv::IPv4::Regex, Resolv::IPv6::Regex])
159
+ return "hosts"
160
+ elsif line.include? "." and not line.include? "http" and path_or_url.end_with?(".list")
161
+ return "hostnames"
162
+ end
163
+ end
164
+ nil
165
+ end
166
+
167
+ def from_hosts_file(path_or_url)
168
+ # TODO: Remove downloading file twice
169
+ HostsFile.load(path_or_url).map(&:name)
170
+ end
171
+
172
+ def from_hostnames_file(contents)
173
+ contents.each { |line| add(line) }
174
+ end
175
+ end
176
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dns-sniper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1pre
5
+ platform: ruby
6
+ authors:
7
+ - Brody Hoskins
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.2
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: down
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: hosts_file
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description: Command line utility that combines online DNS blacklists and combines
56
+ them into the desired configuration format
57
+ email:
58
+ - brody@brody.digital
59
+ executables:
60
+ - dns-sniper
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".gitignore"
65
+ - Gemfile
66
+ - Gemfile.lock
67
+ - Rakefile
68
+ - bin/dns-sniper
69
+ - dns-sniper.gemspec
70
+ - lib/dns-sniper.rb
71
+ - lib/dns-sniper/formatter.rb
72
+ - lib/dns-sniper/formatters.rb
73
+ - lib/dns-sniper/formatters/text.rb
74
+ - lib/dns-sniper/formatters/unbound.rb
75
+ - lib/dns-sniper/hostnames.rb
76
+ homepage: https://github.com/brodyhoskins/dns-sniper
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">"
92
+ - !ruby/object:Gem::Version
93
+ version: 1.3.1
94
+ requirements: []
95
+ rubygems_version: 3.0.3
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Command line utility that combines online DNS blacklists and combines them
99
+ into the desired configuration format
100
+ test_files: []