passivedns-client 1.1.1 → 1.3.0

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
  SHA1:
3
- metadata.gz: 06d7ff1c185b1d52a22829c78ac3f393527f4d61
4
- data.tar.gz: b6fb23b393f3d87948da754ce6c4a69476fb280f
3
+ metadata.gz: 41a0a59329a330d5830052e03a99a39f9ce5a819
4
+ data.tar.gz: 361993fddb9ef65fef2f4721dba03257b818dddd
5
5
  SHA512:
6
- metadata.gz: ade09d0cbf82ea14e6ea216b228e9b1434d5cac2cfc3ab54145ca7be77142880f0fedc220e77839422f9e38976752fbdf6ffdf68fd3ecfde61792ea91d8bc2cd
7
- data.tar.gz: 08d846c8d67746c2864b627346641079c8daba8efe151334ff284b5bc7d2922deac58628d294104571f32481e67759903d13cdc8e1f916b050625c4f8bd9b94c
6
+ metadata.gz: edbb6d872501226acc1aa067a432b63908f9063657f708d1ccf234cd207c681b85eb071c405a7fa772ac51acda85cb6cc71db2d18109282856dd91e19c87061a
7
+ data.tar.gz: 56c04abc0c89251b2303721a2ded1dee6da155bd4a66c43bb12de5ab52e3884fe32b21ec463c083d0e7900ed862068f31c343d58c3bd51d9142e855c908178fb
data/bin/pdnstool CHANGED
@@ -4,198 +4,211 @@ require 'structformatter'
4
4
  require 'getoptlong'
5
5
  require 'yaml'
6
6
 
7
- #if __FILE__ == $0
8
- def pdnslookup(state,pdnsclient,recursedepth=1,wait=0,debug=false)
9
- puts "pdnslookup: #{state.level} #{recursedepth}" if debug
10
- level = 0
11
- while level < recursedepth
12
- puts "pdnslookup: #{level} < #{recursedepth}" if debug
13
- state.each_query(recursedepth) do |q|
14
- rv = pdnsclient.query(q)
15
- if rv
16
- rv.each do |r|
17
- if ["A","AAAA","NS","CNAME","PTR"].index(r.rrtype)
18
- puts "pdnslookup: #{r.to_s}" if debug
19
- state.add_result(r)
20
- end
7
+ def pdnslookup(state,pdnsclient,recursedepth=1,wait=0,debug=false,limit=nil)
8
+ puts "pdnslookup: #{state.level} #{recursedepth}" if debug
9
+ level = 0
10
+ while level < recursedepth
11
+ puts "pdnslookup: #{level} < #{recursedepth}" if debug
12
+ state.each_query(recursedepth) do |q|
13
+ rv = pdnsclient.query(q,limit)
14
+ if rv
15
+ rv.each do |r|
16
+ if ["A","AAAA","NS","CNAME","PTR"].index(r.rrtype)
17
+ puts "pdnslookup: #{r.to_s}" if debug
18
+ state.add_result(r)
21
19
  end
22
- else
23
- state.update_query(rv,'failed')
24
20
  end
25
- sleep wait if level < recursedepth
21
+ else
22
+ state.update_query(rv,'failed')
26
23
  end
27
- level += 1
28
- end
29
- state
30
- end
31
-
32
- def printresults(state,format,sep="\t")
33
- case format
34
- when 'text'
35
- puts PassiveDNS::PDNSResult.members.join(sep)
36
- puts state.to_s(sep)
37
- when 'yaml'
38
- puts state.to_yaml
39
- when 'xml'
40
- puts state.to_xml
41
- when 'json'
42
- puts state.to_json
43
- when 'gdf'
44
- puts state.to_gdf
45
- when 'graphviz'
46
- puts state.to_graphviz
47
- when 'graphml'
48
- puts state.to_graphml
24
+ sleep wait if level < recursedepth
49
25
  end
26
+ level += 1
50
27
  end
28
+ state
29
+ end
51
30
 
52
- def usage
53
- puts "Usage: #{$0} [-a|-b|-e|-d|-i|-V] [-c|-x|-y|-j|-t] [-s <sep>] [-f <file>] [-r#|-w#|-l] <ip|domain|cidr>"
54
- puts " -a uses all of the available passive dns databases"
55
- puts " -b only use BFK"
56
- puts " -e only use CERT-EE"
57
- puts " -d only use DNSParse (default)"
58
- puts " -i only use DNSDB (formerly ISC)"
59
- puts " -V only use VirusTotal"
60
- puts ""
61
- puts " -g outputs a link-nodal GDF visualization definition"
62
- puts " -v outputs a link-nodal graphviz visualization definition"
63
- puts " -m output a link-nodal graphml visualization definition"
64
- puts " -c outputs CSV"
65
- puts " -x outputs XML"
66
- puts " -y outputs YAML"
67
- puts " -j outputs JSON"
68
- puts " -t outputs ASCII text (default)"
69
- puts " -s <sep> specifies a field separator for text output, default is tab"
70
- puts ""
71
- puts " -f[file] specifies a sqlite3 database used to read the current state - useful for large result sets and generating graphs of previous runs."
72
- puts " -r# specifies the levels of recursion to pull. **WARNING** This is quite taxing on the pDNS servers, so use judiciously (never more than 3 or so) or find yourself blocked!"
73
- puts " -w# specifies the amount of time to wait, in seconds, between queries (Default: 0)"
74
- puts " -l outputs debugging information"
75
- exit
31
+ def printresults(state,format,sep="\t")
32
+ case format
33
+ when 'text'
34
+ puts PassiveDNS::PDNSResult.members.join(sep)
35
+ puts state.to_s(sep)
36
+ when 'yaml'
37
+ puts state.to_yaml
38
+ when 'xml'
39
+ puts state.to_xml
40
+ when 'json'
41
+ puts state.to_json
42
+ when 'gdf'
43
+ puts state.to_gdf
44
+ when 'graphviz'
45
+ puts state.to_graphviz
46
+ when 'graphml'
47
+ puts state.to_graphml
76
48
  end
49
+ end
50
+
51
+ def usage
52
+ puts "Usage: #{$0} [-d [bedvt]] [-og|-ov|-om|-oc|-ox|-oy|-oj|-ot] [-os <sep>] [-f <file>] [-r#|-w#|-v] [-l <count>] <ip|domain|cidr>"
53
+ puts " -dbedvt uses all of the available passive dns databases"
54
+ puts " -db only use BFK"
55
+ puts " -de only use CERT-EE (default)"
56
+ puts " -dd only use DNSDB (formerly ISC)"
57
+ puts " -dv only use VirusTotal"
58
+ puts " -dt only use TCPIPUtils"
59
+ puts " -dvt uses VirusTotal and TCPIPUtils (for example)"
60
+ puts ""
61
+ puts " -og outputs a link-nodal GDF visualization definition"
62
+ puts " -ov outputs a link-nodal graphviz visualization definition"
63
+ puts " -om output a link-nodal graphml visualization definition"
64
+ puts " -oc outputs CSV"
65
+ puts " -ox outputs XML"
66
+ puts " -oy outputs YAML"
67
+ puts " -oj outputs JSON"
68
+ puts " -ot outputs ASCII text (default)"
69
+ puts " -os <sep> specifies a field separator for text output, default is tab"
70
+ puts ""
71
+ puts " -f[file] specifies a sqlite3 database used to read the current state - useful for large result sets and generating graphs of previous runs."
72
+ puts " -r# specifies the levels of recursion to pull. **WARNING** This is quite taxing on the pDNS servers, so use judiciously (never more than 3 or so) or find yourself blocked!"
73
+ puts " -w# specifies the amount of time to wait, in seconds, between queries (Default: 0)"
74
+ puts " -v outputs debugging information"
75
+ puts " -l <count> limits the number of records returned per passive dns database queried."
76
+ exit
77
+ end
78
+
79
+ opts = GetoptLong.new(
80
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
81
+ [ '--debug', '-z', GetoptLong::NO_ARGUMENT ],
82
+ [ '--database', '-d', GetoptLong::REQUIRED_ARGUMENT ],
77
83
 
78
- opts = GetoptLong.new(
79
- [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
80
- [ '--debug', '-l', GetoptLong::NO_ARGUMENT ],
81
- [ '--all', '-a', GetoptLong::NO_ARGUMENT ],
82
- [ '--bfk', '-b', GetoptLong::NO_ARGUMENT ],
83
- [ '--dnsparse', '-d', GetoptLong::NO_ARGUMENT ],
84
- [ '--certee', '-e', GetoptLong::NO_ARGUMENT ],
85
- [ '--isc', '-i', GetoptLong::NO_ARGUMENT ],
86
- [ '--virustotal', '-V', GetoptLong::NO_ARGUMENT ],
87
-
88
- [ '--gdf', '-g', GetoptLong::NO_ARGUMENT ],
89
- [ '--graphviz', '-v', GetoptLong::NO_ARGUMENT ],
90
- [ '--graphml', '-m', GetoptLong::NO_ARGUMENT ],
91
- [ '--csv', '-c', GetoptLong::NO_ARGUMENT ],
92
- [ '--xml', '-x', GetoptLong::NO_ARGUMENT ],
93
- [ '--yaml', '-y', GetoptLong::NO_ARGUMENT ],
94
- [ '--json', '-j', GetoptLong::NO_ARGUMENT ],
95
- [ '--text', '-t', GetoptLong::NO_ARGUMENT ],
96
- [ '--sep', '-s', GetoptLong::REQUIRED_ARGUMENT ],
97
-
98
- [ '--sqlite3', '-f', GetoptLong::REQUIRED_ARGUMENT ],
99
- [ '--recurse', '-r', GetoptLong::REQUIRED_ARGUMENT ],
100
- [ '--wait', '-w', GetoptLong::REQUIRED_ARGUMENT ]
101
- )
102
-
103
- # sets the default search methods
104
- pdnsdbs = []
105
- format = "text"
106
- sep = "\t"
107
- recursedepth = 1
108
- wait = 0
109
- res = nil
110
- debug = false
111
- sqlitedb = nil
84
+ [ '--gdf', '-g', GetoptLong::NO_ARGUMENT ],
85
+ [ '--graphviz', '-v', GetoptLong::NO_ARGUMENT ],
86
+ [ '--graphml', '-m', GetoptLong::NO_ARGUMENT ],
87
+ [ '--csv', '-c', GetoptLong::NO_ARGUMENT ],
88
+ [ '--xml', '-x', GetoptLong::NO_ARGUMENT ],
89
+ [ '--yaml', '-y', GetoptLong::NO_ARGUMENT ],
90
+ [ '--json', '-j', GetoptLong::NO_ARGUMENT ],
91
+ [ '--text', '-t', GetoptLong::NO_ARGUMENT ],
92
+ [ '--sep', '-s', GetoptLong::REQUIRED_ARGUMENT ],
112
93
 
113
- opts.each do |opt, arg|
114
- case opt
115
- when '--help'
116
- usage
117
- when '--debug'
118
- debug = true
119
- $stderr.puts "Using proxy settings: http_proxy=#{ENV['http_proxy']}, https_proxy=#{ENV['https_proxy']}"
120
- when '--all'
121
- pdnsdbs << "bfk"
122
- pdnsdbs << "certee"
123
- pdnsdbs << "dnsparse"
124
- pdnsdbs << "dnsdb"
125
- pdnsdbs << "virustotal"
126
- when '--bfk'
127
- pdnsdbs << "bfk"
128
- when '--certee'
129
- pdnsdbs << "certee"
130
- when '--dnsparse'
131
- pdnsdbs << "dnsparse"
132
- when '--isc'
133
- pdnsdbs << "dnsdb"
134
- when '--virustotal'
135
- pdnsdbs << "virustotal"
136
- when '--gdf'
137
- format = 'gdf'
138
- when '--graphviz'
139
- format = 'graphviz'
140
- when '--graphml'
141
- format = 'graphml'
142
- when '--csv'
143
- format = 'text'
144
- sep = ','
145
- when '--yaml'
146
- format = 'yaml'
147
- when '--xml'
148
- format = 'xml'
149
- when '--json'
150
- format = 'json'
151
- when '--text'
152
- format = 'text'
153
- when '--sep'
154
- sep = arg
155
- when '--recurse'
156
- recursedepth = arg.to_i
157
- if recursedepth > 3
158
- $stderr.puts "WARNING: a recursedepth of > 3 can be abusive, please reconsider: sleeping 60 seconds for sense to come to you (hint: hit CTRL-C)"
159
- sleep 60
160
- end
161
- when '--wait'
162
- wait = arg.to_i
163
- when '--sqlite3'
164
- sqlitedb = arg
165
- else
166
- usage
94
+ [ '--sqlite3', '-f', GetoptLong::REQUIRED_ARGUMENT ],
95
+ [ '--recurse', '-r', GetoptLong::REQUIRED_ARGUMENT ],
96
+ [ '--wait', '-w', GetoptLong::REQUIRED_ARGUMENT ],
97
+ [ '--limit', '-l', GetoptLong::REQUIRED_ARGUMENT ]
98
+ )
99
+
100
+ # sets the default search methods
101
+ pdnsdbs = []
102
+ format = "text"
103
+ sep = "\t"
104
+ recursedepth = 1
105
+ wait = 0
106
+ res = nil
107
+ debug = false
108
+ sqlitedb = nil
109
+ limit = nil
110
+
111
+ opts.each do |opt, arg|
112
+ case opt
113
+ when '--help'
114
+ usage
115
+ when '--debug'
116
+ debug = true
117
+ when '--database'
118
+ arg.split(//).each do |c|
119
+ case c
120
+ when ','
121
+ when 'b'
122
+ pdnsdbs << "bfk"
123
+ when 'e'
124
+ pdnsdbs << "certee"
125
+ when 'd'
126
+ pdnsdbs << "dnsdb"
127
+ when 'v'
128
+ pdnsdbs << "virustotal"
129
+ when 't'
130
+ pdnsdbs << "tcpiputils"
131
+ else
132
+ raise "Unknown passive DNS database identifier: #{c}"
133
+ end
134
+ end
135
+ when '--gdf'
136
+ format = 'gdf'
137
+ when '--graphviz'
138
+ format = 'graphviz'
139
+ when '--graphml'
140
+ format = 'graphml'
141
+ when '--csv'
142
+ format = 'text'
143
+ sep = ','
144
+ when '--yaml'
145
+ format = 'yaml'
146
+ when '--xml'
147
+ format = 'xml'
148
+ when '--json'
149
+ format = 'json'
150
+ when '--text'
151
+ format = 'text'
152
+ when '--sep'
153
+ sep = arg
154
+ when '--recurse'
155
+ recursedepth = arg.to_i
156
+ if recursedepth > 3
157
+ $stderr.puts "WARNING: a recursedepth of > 3 can be abusive, please reconsider: sleeping 60 seconds for sense to come to you (hint: hit CTRL-C)"
158
+ sleep 60
167
159
  end
168
- end
169
-
170
- if pdnsdbs.length == 0
171
- pdnsdbs << "dnsparse"
172
- end
173
-
174
- if pdnsdbs.index("bfk") and recursedepth > 1 and wait < 60
175
- wait = 60
176
- $stderr.puts "Enforcing a minimal 60 second wait when using BFK for recursive crawling"
177
- end
178
-
179
- state = nil
180
- if sqlitedb
181
- state = PassiveDNS::PDNSToolStateDB.new(sqlitedb)
160
+ when '--wait'
161
+ wait = arg.to_i
162
+ when '--sqlite3'
163
+ sqlitedb = arg
164
+ when '--limit'
165
+ limit = arg.to_i
182
166
  else
183
- state = PassiveDNS::PDNSToolState.new
167
+ usage
184
168
  end
185
- state.debug = true if debug
186
-
187
- pdnsclient = PassiveDNS::Client.new(pdnsdbs)
188
- pdnsclient.debug = debug
189
-
190
- if ARGV.length > 0
191
- ARGV.each do |arg|
192
- state.add_query(arg,'pending',0)
193
- end
194
- else
195
- $stdin.each_line do |l|
196
- state.add_query(l.chomp,'pending',0)
197
- end
169
+ end
170
+
171
+ if pdnsdbs.length == 0
172
+ pdnsdbs << "certee"
173
+ end
174
+
175
+ if pdnsdbs.index("bfk") and recursedepth > 1 and wait < 60
176
+ wait = 60
177
+ $stderr.puts "Enforcing a minimal 60 second wait when using BFK for recursive crawling"
178
+ end
179
+
180
+ if debug
181
+ $stderr.puts "Using the following databases: #{pdnsdbs.join(", ")}"
182
+ $stderr.puts "Recursions: #{recursedepth}, Wait time: #{wait}, Limit: #{limit or 'none'}"
183
+ if format == "text" or format == "csv"
184
+ $stderr.puts "Output format: #{format} (sep=\"#{sep}\")"
185
+ else
186
+ $stderr.puts "Output format: #{format}"
187
+ end
188
+ if ENV['http_proxy']
189
+ $stderr.puts "Using proxy settings: http_proxy=#{ENV['http_proxy']}, https_proxy=#{ENV['https_proxy']}"
190
+ end
191
+ end
192
+
193
+ state = nil
194
+ if sqlitedb
195
+ state = PassiveDNS::PDNSToolStateDB.new(sqlitedb)
196
+ else
197
+ state = PassiveDNS::PDNSToolState.new
198
+ end
199
+ state.debug = true if debug
200
+
201
+ pdnsclient = PassiveDNS::Client.new(pdnsdbs)
202
+ pdnsclient.debug = debug
203
+
204
+ if ARGV.length > 0
205
+ ARGV.each do |arg|
206
+ state.add_query(arg,'pending',0)
207
+ end
208
+ else
209
+ $stdin.each_line do |l|
210
+ state.add_query(l.chomp,'pending',0)
198
211
  end
199
- pdnslookup(state,pdnsclient,recursedepth,wait,debug)
200
- printresults(state,format,sep)
201
- #end
212
+ end
213
+ pdnslookup(state,pdnsclient,recursedepth,wait,debug,limit)
214
+ printresults(state,format,sep)
@@ -38,7 +38,7 @@ module PassiveDNS
38
38
  raise e
39
39
  end
40
40
 
41
- def lookup(label)
41
+ def lookup(label, limit=nil)
42
42
  $stderr.puts "DEBUG: BFKClient.lookup(#{label})" if @debug
43
43
  Timeout::timeout(240) {
44
44
  t1 = Time.now
@@ -48,7 +48,12 @@ module PassiveDNS
48
48
  :http_basic_authentication => [@user,@pass]
49
49
  ) do |f|
50
50
  t2 = Time.now
51
- parse(f.read,t2-t1)
51
+ recs = parse(f.read,t2-t1)
52
+ if limit
53
+ recs[0,limit]
54
+ else
55
+ recs
56
+ end
52
57
  end
53
58
  }
54
59
  rescue Timeout::Error => e
@@ -6,7 +6,7 @@ module PassiveDNS
6
6
  attr_accessor :debug
7
7
  def initialize
8
8
  end
9
- def lookup(label)
9
+ def lookup(label, limit=nil)
10
10
  $stderr.puts "DEBUG: CERTEE.lookup(#{label})" if @debug
11
11
  recs = []
12
12
  begin
@@ -14,6 +14,10 @@ module PassiveDNS
14
14
  s = TCPSocket.new(@@host,43)
15
15
  s.puts(label)
16
16
  s.each_line do |l|
17
+ if l =~ /Traceback \(most recent call last\):/
18
+ # there is a bug in the CERTEE lookup tool
19
+ raise "CERTEE is currently offline"
20
+ end
17
21
  (lbl,ans,fs,ls) = l.chomp.split(/\t/)
18
22
  rrtype = 'A'
19
23
  if ans =~ /^\d+\.\d+\.\d+\.\d+$/
@@ -30,7 +34,11 @@ module PassiveDNS
30
34
  $stderr.puts e
31
35
  end
32
36
  return nil unless recs.length > 0
33
- recs
34
- end
37
+ if limit
38
+ recs = recs[0,limit]
39
+ else
40
+ recs
41
+ end
42
+ end
35
43
  end
36
44
  end
@@ -48,7 +48,7 @@ module PassiveDNS
48
48
  raise e
49
49
  end
50
50
 
51
- def lookup(label)
51
+ def lookup(label, limit=nil)
52
52
  $stderr.puts "DEBUG: DNSDB.lookup(#{label})" if @debug
53
53
  Timeout::timeout(240) {
54
54
  url = nil
@@ -63,14 +63,18 @@ module PassiveDNS
63
63
  http.use_ssl = (url.scheme == 'https')
64
64
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
65
65
  http.verify_depth = 5
66
- request = Net::HTTP::Get.new(url.path)
66
+ path = url.path
67
+ if limit
68
+ path << "?limit=#{limit}"
69
+ end
70
+ request = Net::HTTP::Get.new(path)
67
71
  request.add_field("User-Agent", "Ruby/#{RUBY_VERSION} passivedns-client rubygem v#{PassiveDNS::Client::VERSION}")
68
72
  request.add_field("X-API-Key", @key)
69
73
  request.add_field("Accept", "application/json")
70
74
  t1 = Time.now
71
75
  response = http.request(request)
72
76
  t2 = Time.now
73
- $stderr.puts response if @debug
77
+ $stderr.puts response.body if @debug
74
78
  parse_json(response.body,t2-t1)
75
79
  }
76
80
  rescue Timeout::Error => e
@@ -0,0 +1,80 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'openssl'
4
+ require 'json'
5
+
6
+ # Please read http://www.tcpiputils.com/terms-of-service under automated requests
7
+
8
+ module PassiveDNS
9
+ class TCPIPUtils
10
+ attr_accessor :debug
11
+ def initialize(config="#{ENV['HOME']}/.tcpiputils")
12
+ @debug = false
13
+ if File.exist?(config)
14
+ @apikey = File.open(config).read.split(/\n/)[0]
15
+ $stderr.puts "DEBUG: TCPIPUtils#initialize(#{@apikey})" if @debug
16
+ else
17
+ raise "Error: Configuration file for TCPIPUtils is required for intialization
18
+ Format of configuration file (default: #{ENV['HOME']}/.tcpiputils) is the 64 hex character apikey on one line.
19
+ To obtain an API Key, go to http://www.tcpiputils.com/premium-access and purchase premium API access."
20
+ end
21
+ end
22
+
23
+ def format_recs(reply_data, question, delta)
24
+ recs = []
25
+ reply_data.each do |key, data|
26
+ case key
27
+ when "ipv4"
28
+ data.each do |rec|
29
+ recs << PDNSResult.new("tcpiputils", delta, question, rec["ip"], "A", nil, nil, rec["updatedate"], nil)
30
+ end
31
+ when "ipv6"
32
+ data.each do |rec|
33
+ recs << PDNSResult.new("tcpiputils", delta, question, rec["ip"], "AAAA", nil, nil, rec["updatedate"], nil)
34
+ end
35
+ when "dns"
36
+ data.each do |rec|
37
+ recs << PDNSResult.new("tcpiputils", delta, question, rec["dns"], "NS", nil, nil, rec["updatedate"], nil)
38
+ end
39
+ when "mx"
40
+ data.each do |rec|
41
+ recs << PDNSResult.new("tcpiputils", delta, question, rec["dns"], "MX", nil, nil, rec["updatedate"], nil)
42
+ end
43
+ end
44
+ end
45
+ recs
46
+ end
47
+
48
+ def lookup(label, limit=nil)
49
+ $stderr.puts "DEBUG: TCPIPUtils.lookup(#{label})" if @debug
50
+ url = "https://www.utlsapi.com/api.php?version=1.0&apikey=#{@apikey}&type=domainipdnshistory&q=#{label}"
51
+ recs = []
52
+ Timeout::timeout(240) {
53
+ url = URI.parse url
54
+ http = Net::HTTP.new(url.host, url.port)
55
+ http.use_ssl = (url.scheme == 'https')
56
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
57
+ http.verify_depth = 5
58
+ request = Net::HTTP::Get.new(url.path+"?"+url.query)
59
+ request.add_field("User-Agent", "Ruby/#{RUBY_VERSION} passivedns-client rubygem v#{PassiveDNS::Client::VERSION}")
60
+ t1 = Time.now
61
+ response = http.request(request)
62
+ delta = (Time.now - t1).to_f
63
+ reply = JSON.parse(response.body)
64
+ if reply["status"] and reply["status"] == "succeed"
65
+ question = reply["data"]["question"]
66
+ recs = format_recs(reply["data"], question, delta)
67
+ elsif reply["status"] and reply["status"] == "error"
68
+ raise "TCPIPUtils: error from web API: #{reply["data"]}"
69
+ end
70
+ if limit
71
+ recs[0,limit]
72
+ else
73
+ recs
74
+ end
75
+ }
76
+ rescue Timeout::Error => e
77
+ $stderr.puts "TCPIPUtils lookup timed out: #{label}"
78
+ end
79
+ end
80
+ end
@@ -1,5 +1,5 @@
1
1
  module PassiveDNS
2
2
  class Client
3
- VERSION = "1.1.1"
3
+ VERSION = "1.3.0"
4
4
  end
5
5
  end
@@ -34,7 +34,7 @@ module PassiveDNS
34
34
  raise e
35
35
  end
36
36
 
37
- def lookup(label)
37
+ def lookup(label, limit=nil)
38
38
  $stderr.puts "DEBUG: VirusTotal.lookup(#{label})" if @debug
39
39
  Timeout::timeout(240) {
40
40
  url = nil
@@ -54,7 +54,12 @@ module PassiveDNS
54
54
  t1 = Time.now
55
55
  response = http.request(request)
56
56
  t2 = Time.now
57
- parse_json(response.body, label, t2-t1)
57
+ recs = parse_json(response.body, label, t2-t1)
58
+ if limit
59
+ recs[0,limit]
60
+ else
61
+ recs
62
+ end
58
63
  }
59
64
  rescue Timeout::Error => e
60
65
  $stderr.puts "VirusTotal lookup timed out: #{label}"
@@ -5,9 +5,9 @@ require "passivedns/client/version"
5
5
  # Remember, these passive DNS operators are my friends. I don't want to have a row with them because some asshat used this library to abuse them.
6
6
  require 'passivedns/client/bfk.rb'
7
7
  require 'passivedns/client/certee.rb'
8
- require 'passivedns/client/dnsparse.rb'
9
8
  require 'passivedns/client/dnsdb.rb'
10
9
  require 'passivedns/client/virustotal.rb'
10
+ require 'passivedns/client/tcpiputils.rb'
11
11
  require 'passivedns/client/state.rb'
12
12
 
13
13
  module PassiveDNS
@@ -15,7 +15,7 @@ module PassiveDNS
15
15
  class PDNSResult < Struct.new(:source, :response_time, :query, :answer, :rrtype, :ttl, :firstseen, :lastseen, :count); end
16
16
 
17
17
  class Client
18
- def initialize(pdns=['bfk','certee','dnsparse','dnsdb','virustotal'])
18
+ def initialize(pdns=['bfk','certee','dnsdb','virustotal','tcpiputils'])
19
19
  @pdnsdbs = []
20
20
  pdns.uniq.each do |pd|
21
21
  case pd
@@ -23,14 +23,16 @@ module PassiveDNS
23
23
  @pdnsdbs << PassiveDNS::BFK.new
24
24
  when 'certee'
25
25
  @pdnsdbs << PassiveDNS::CERTEE.new
26
- when 'dnsparse'
27
- @pdnsdbs << PassiveDNS::DNSParse.new
26
+ #when 'dnsparse'
27
+ # @pdnsdbs << PassiveDNS::DNSParse.new
28
28
  when 'dnsdb'
29
29
  @pdnsdbs << PassiveDNS::DNSDB.new
30
30
  when 'isc'
31
31
  @pdnsdbs << PassiveDNS::DNSDB.new
32
32
  when 'virustotal'
33
33
  @pdnsdbs << PassiveDNS::VirusTotal.new
34
+ when 'tcpiputils'
35
+ @pdnsdbs << PassiveDNS::TCPIPUtils.new
34
36
  else
35
37
  raise "Unknown Passive DNS provider: #{pd}"
36
38
  end
@@ -43,11 +45,11 @@ module PassiveDNS
43
45
  end
44
46
  end
45
47
 
46
- def query(item)
48
+ def query(item, limit=nil)
47
49
  threads = []
48
50
  @pdnsdbs.each do |pdnsdb|
49
51
  threads << Thread.new(item) do |q|
50
- pdnsdb.lookup(q)
52
+ pdnsdb.lookup(q, limit)
51
53
  end
52
54
  end
53
55
 
@@ -24,6 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "bundler", "~> 1.3"
25
25
  spec.add_development_dependency "rake"
26
26
 
27
- spec.signing_key = "#{File.dirname(__FILE__)}/../gem-private_key.pem"
28
- spec.cert_chain = ["#{File.dirname(__FILE__)}/../gem-public_cert.pem"]
27
+ #spec.signing_key = "#{File.dirname(__FILE__)}/../gem-private_key.pem"
28
+ #spec.cert_chain = ["#{File.dirname(__FILE__)}/../gem-public_cert.pem"]
29
29
  end
@@ -15,41 +15,6 @@ class TestPassiveDnsQuery < Test::Unit::TestCase
15
15
  end
16
16
  end
17
17
 
18
- def test_instantiate_BFK_Client
19
- assert_not_nil(PassiveDNS::BFK.new)
20
- assert_nothing_raised do
21
- PassiveDNS::Client.new(['bfk'])
22
- end
23
- end
24
-
25
- def test_instantiate_CERTEE_Client
26
- assert_not_nil(PassiveDNS::CERTEE.new)
27
- assert_nothing_raised do
28
- PassiveDNS::Client.new(['certee'])
29
- end
30
- end
31
-
32
- def test_instantiate_DNSParse_Client
33
- assert_not_nil(PassiveDNS::DNSParse.new)
34
- assert_nothing_raised do
35
- PassiveDNS::Client.new(['dnsparse'])
36
- end
37
- end
38
-
39
- def test_instantiate_DNSDB_Client
40
- assert_not_nil(PassiveDNS::DNSDB.new)
41
- assert_nothing_raised do
42
- PassiveDNS::Client.new(['dnsdb'])
43
- end
44
- end
45
-
46
- def test_instantiate_VirusTotal_Client
47
- assert_not_nil(PassiveDNS::VirusTotal.new)
48
- assert_nothing_raised do
49
- PassiveDNS::Client.new(['virustotal'])
50
- end
51
- end
52
-
53
18
  def test_instantiate_All_Clients
54
19
  assert_nothing_raised do
55
20
  PassiveDNS::Client.new()
@@ -70,49 +35,80 @@ class TestPassiveDnsQuery < Test::Unit::TestCase
70
35
  end
71
36
  end
72
37
 
73
- def test_query_BFK
74
- rows = PassiveDNS::BFK.new.lookup("example.org")
38
+ def test_BFK
39
+ assert_not_nil(PassiveDNS::BFK.new)
40
+ assert_nothing_raised do
41
+ PassiveDNS::Client.new(['bfk'])
42
+ end
43
+ rows = PassiveDNS::BFK.new.lookup("example.org",3)
75
44
  assert_not_nil(rows)
76
45
  assert_not_nil(rows.to_s)
77
46
  assert_not_nil(rows.to_xml)
78
47
  assert_not_nil(rows.to_json)
79
48
  assert_not_nil(rows.to_yaml)
49
+ assert_equal(3, rows.length)
80
50
  end
81
51
 
82
- def test_query_CERTEE
83
- rows = PassiveDNS::CERTEE.new.lookup("sim.cert.ee")
84
- assert_not_nil(rows)
85
- assert_not_nil(rows.to_s)
86
- assert_not_nil(rows.to_xml)
87
- assert_not_nil(rows.to_json)
88
- assert_not_nil(rows.to_yaml)
89
- end
90
-
91
- def test_query_DNSParse
92
- rows = PassiveDNS::DNSParse.new.lookup("example.org")
52
+ def test_CERTEE
53
+ assert(false, "CERTEE is still offline")
54
+ assert_not_nil(PassiveDNS::CERTEE.new)
55
+ assert_nothing_raised do
56
+ PassiveDNS::Client.new(['certee'])
57
+ end
58
+ rows = PassiveDNS::CERTEE.new.lookup("sim.cert.ee",3)
93
59
  assert_not_nil(rows)
94
60
  assert_not_nil(rows.to_s)
95
61
  assert_not_nil(rows.to_xml)
96
62
  assert_not_nil(rows.to_json)
97
63
  assert_not_nil(rows.to_yaml)
64
+ assert_equal(3, rows.length)
98
65
  end
99
66
 
100
- def test_query_DNSDB
101
- rows = PassiveDNS::DNSDB.new.lookup("example.org")
67
+ def test_DNSDB
68
+ assert_not_nil(PassiveDNS::DNSDB.new)
69
+ assert_nothing_raised do
70
+ PassiveDNS::Client.new(['dnsdb'])
71
+ end
72
+ rows = PassiveDNS::DNSDB.new.lookup("example.org",3)
102
73
  assert_not_nil(rows)
103
74
  assert_not_nil(rows.to_s)
104
75
  assert_not_nil(rows.to_xml)
105
76
  assert_not_nil(rows.to_json)
106
77
  assert_not_nil(rows.to_yaml)
78
+ assert_equal(3, rows.length) # this will fail since DNSDB has an off by one error
107
79
  end
108
80
 
109
- def test_query_VirusTotal
110
- rows = PassiveDNS::VirusTotal.new.lookup("sim.cert.ee")
81
+ def test_VirusTotal
82
+ assert_not_nil(PassiveDNS::VirusTotal.new)
83
+ assert_nothing_raised do
84
+ PassiveDNS::Client.new(['virustotal'])
85
+ end
86
+ rows = PassiveDNS::VirusTotal.new.lookup("google.com",3)
111
87
  assert_not_nil(rows)
112
88
  assert_not_nil(rows.to_s)
113
89
  assert_not_nil(rows.to_xml)
114
90
  assert_not_nil(rows.to_json)
115
91
  assert_not_nil(rows.to_yaml)
92
+ assert_equal(3, rows.length)
116
93
  end
117
-
94
+
95
+ def test_TCPIPUtils
96
+ assert_not_nil(PassiveDNS::TCPIPUtils.new)
97
+ assert_nothing_raised do
98
+ PassiveDNS::Client.new(['tcpiputils'])
99
+ end
100
+ rows = PassiveDNS::TCPIPUtils.new.lookup("example.org")
101
+ assert_not_nil(rows)
102
+ assert_not_nil(rows.to_s)
103
+ assert_not_nil(rows.to_xml)
104
+ assert_not_nil(rows.to_json)
105
+ assert_not_nil(rows.to_yaml)
106
+ rows = PassiveDNS::TCPIPUtils.new.lookup("example.org",3)
107
+ assert_not_nil(rows)
108
+ assert_not_nil(rows.to_s)
109
+ assert_not_nil(rows.to_xml)
110
+ assert_not_nil(rows.to_json)
111
+ assert_not_nil(rows.to_yaml)
112
+ assert_equal(3, rows.length)
113
+ end
118
114
  end
metadata CHANGED
@@ -1,105 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passivedns-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - chrislee35
8
8
  autorequire:
9
9
  bindir: bin
10
- cert_chain:
11
- - |
12
- -----BEGIN CERTIFICATE-----
13
- MIIDYjCCAkqgAwIBAgIBADANBgkqhkiG9w0BAQUFADBXMREwDwYDVQQDDAhydWJ5
14
- Z2VtczEYMBYGCgmSJomT8ixkARkWCGNocmlzbGVlMRMwEQYKCZImiZPyLGQBGRYD
15
- ZGhzMRMwEQYKCZImiZPyLGQBGRYDb3JnMB4XDTEzMDUyMjEyNTk0N1oXDTE0MDUy
16
- MjEyNTk0N1owVzERMA8GA1UEAwwIcnVieWdlbXMxGDAWBgoJkiaJk/IsZAEZFghj
17
- aHJpc2xlZTETMBEGCgmSJomT8ixkARkWA2RoczETMBEGCgmSJomT8ixkARkWA29y
18
- ZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANcPrx8BZiWIR9xWWG8I
19
- tqR538tS1t+UJ4FZFl+1vrtU9TiuWX3Vj37TwUpa2fFkziK0n5KupVThyEhcem5m
20
- OGRjvgrRFbWQJSSscIKOpwqURHVKRpV9gVz/Hnzk8S+xotUR1Buo3Ugr+I1jHewD
21
- Cgr+y+zgZbtjtHsJtsuujkOcPhEjjUinj68L9Fz9BdeJQt+IacjwAzULix6jWCht
22
- Uc+g+0z8Esryca2G6I1GsrgX6WHw8dykyQDT9dCtS2flCOwSC1R0K5T/xHW54f+5
23
- wcw8mm53KLNe+tmgVC6ZHyME+qJsBnP6uxF0aTEnGA/jDBQDhQNTF0ZP/abzyTsL
24
- zjUCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFO8w
25
- +aeP7T6kVJblCg6eusOII9DfMA0GCSqGSIb3DQEBBQUAA4IBAQBCQyRJLXsBo2Fy
26
- 8W6e/W4RemQRrlAw9DK5O6U71JtedVob2oq+Ob+zmS+PifE2+L+3RiJ2H6VTlOzi
27
- x+A061MUXhGraqVq4J2FC8kt4EQywAD0P0Ta5GU24CGSF08Y3GkJy1Sa4XqTC2YC
28
- o51s7JP+tkCCtpVYSdzJhTllieRAWBpGV1dtaoeUKE6tYPMBkosxSRcVGczk/Sc3
29
- 7eQCpexYy9JlUBI9u3BqIY9E+l+MSn8ihXSPmyK0DgrhaCu+voaSFVOX6Y+B5qbo
30
- jLXMQu2ZgISYwXNjNbGVHehut82U7U9oiHoWcrOGazaRUmGO9TXP+aJLH0gw2dcK
31
- AfMglXPi
32
- -----END CERTIFICATE-----
33
- date: 2013-09-21 00:00:00.000000000 Z
10
+ cert_chain: []
11
+ date: 2014-09-22 00:00:00.000000000 Z
34
12
  dependencies:
35
13
  - !ruby/object:Gem::Dependency
36
14
  name: json
37
15
  requirement: !ruby/object:Gem::Requirement
38
16
  requirements:
39
- - - '>='
17
+ - - ">="
40
18
  - !ruby/object:Gem::Version
41
19
  version: 1.4.3
42
20
  type: :runtime
43
21
  prerelease: false
44
22
  version_requirements: !ruby/object:Gem::Requirement
45
23
  requirements:
46
- - - '>='
24
+ - - ">="
47
25
  - !ruby/object:Gem::Version
48
26
  version: 1.4.3
49
27
  - !ruby/object:Gem::Dependency
50
28
  name: sqlite3
51
29
  requirement: !ruby/object:Gem::Requirement
52
30
  requirements:
53
- - - '>='
31
+ - - ">="
54
32
  - !ruby/object:Gem::Version
55
33
  version: 1.3.3
56
34
  type: :runtime
57
35
  prerelease: false
58
36
  version_requirements: !ruby/object:Gem::Requirement
59
37
  requirements:
60
- - - '>='
38
+ - - ">="
61
39
  - !ruby/object:Gem::Version
62
40
  version: 1.3.3
63
41
  - !ruby/object:Gem::Dependency
64
42
  name: structformatter
65
43
  requirement: !ruby/object:Gem::Requirement
66
44
  requirements:
67
- - - ~>
45
+ - - "~>"
68
46
  - !ruby/object:Gem::Version
69
47
  version: 0.0.1
70
48
  type: :runtime
71
49
  prerelease: false
72
50
  version_requirements: !ruby/object:Gem::Requirement
73
51
  requirements:
74
- - - ~>
52
+ - - "~>"
75
53
  - !ruby/object:Gem::Version
76
54
  version: 0.0.1
77
55
  - !ruby/object:Gem::Dependency
78
56
  name: bundler
79
57
  requirement: !ruby/object:Gem::Requirement
80
58
  requirements:
81
- - - ~>
59
+ - - "~>"
82
60
  - !ruby/object:Gem::Version
83
61
  version: '1.3'
84
62
  type: :development
85
63
  prerelease: false
86
64
  version_requirements: !ruby/object:Gem::Requirement
87
65
  requirements:
88
- - - ~>
66
+ - - "~>"
89
67
  - !ruby/object:Gem::Version
90
68
  version: '1.3'
91
69
  - !ruby/object:Gem::Dependency
92
70
  name: rake
93
71
  requirement: !ruby/object:Gem::Requirement
94
72
  requirements:
95
- - - '>='
73
+ - - ">="
96
74
  - !ruby/object:Gem::Version
97
75
  version: '0'
98
76
  type: :development
99
77
  prerelease: false
100
78
  version_requirements: !ruby/object:Gem::Requirement
101
79
  requirements:
102
- - - '>='
80
+ - - ">="
103
81
  - !ruby/object:Gem::Version
104
82
  version: '0'
105
83
  description: This provides interfaces to various passive DNS databases to do the query
@@ -112,7 +90,7 @@ executables:
112
90
  extensions: []
113
91
  extra_rdoc_files: []
114
92
  files:
115
- - .gitignore
93
+ - ".gitignore"
116
94
  - Gemfile
117
95
  - LICENSE.txt
118
96
  - README.md
@@ -122,8 +100,8 @@ files:
122
100
  - lib/passivedns/client/bfk.rb
123
101
  - lib/passivedns/client/certee.rb
124
102
  - lib/passivedns/client/dnsdb.rb
125
- - lib/passivedns/client/dnsparse.rb
126
103
  - lib/passivedns/client/state.rb
104
+ - lib/passivedns/client/tcpiputils.rb
127
105
  - lib/passivedns/client/version.rb
128
106
  - lib/passivedns/client/virustotal.rb
129
107
  - passivedns-client.gemspec
@@ -139,17 +117,17 @@ require_paths:
139
117
  - lib
140
118
  required_ruby_version: !ruby/object:Gem::Requirement
141
119
  requirements:
142
- - - '>='
120
+ - - ">="
143
121
  - !ruby/object:Gem::Version
144
122
  version: '0'
145
123
  required_rubygems_version: !ruby/object:Gem::Requirement
146
124
  requirements:
147
- - - '>='
125
+ - - ">="
148
126
  - !ruby/object:Gem::Version
149
127
  version: '0'
150
128
  requirements: []
151
129
  rubyforge_project:
152
- rubygems_version: 2.0.3
130
+ rubygems_version: 2.2.2
153
131
  signing_key:
154
132
  specification_version: 4
155
133
  summary: Query passive DNS databases
checksums.yaml.gz.sig DELETED
Binary file
@@ -1,89 +0,0 @@
1
- # DESCRIPTION: this is a module for pdns.rb, primarily used by pdnstool.rb, to query Bojan Zdrnja's passive DNS database, DNSParse
2
- require 'net/http'
3
- require 'net/https'
4
- require 'openssl'
5
-
6
- module PassiveDNS
7
- class DNSParse
8
- attr_accessor :debug
9
- @@dns_rtypes = {1 => 'A', 2 => 'NS', 3 => 'MD', 4 => 'MF', 5 => 'CNAME', 6 => 'SOA', 7 => 'MB', 8 => 'MG', 9 => 'MR', 10 => 'NULL', 11 => 'WKS', 12 => 'PTR', 13 => 'HINFO', 14 => 'MINFO', 15 => 'MX', 16 => 'TXT', 17 => 'RP', 18 => 'AFSDB', 19 => 'X25', 20 => 'ISDN', 21 => 'RT', 22 => 'NSAP', 23 => 'NSAP-PTR', 24 => 'SIG', 25 => 'KEY', 26 => 'PX', 27 => 'GPOS', 28 => 'AAAA', 29 => 'LOC', 30 => 'NXT', 31 => 'EID', 32 => 'NIMLOC', 33 => 'SRV', 34 => 'ATMA', 35 => 'NAPTR', 36 => 'KX', 37 => 'CERT', 38 => 'A6', 39 => 'DNAME', 40 => 'SINK', 41 => 'OPT', 42 => 'APL', 43 => 'DS', 44 => 'SSHFP', 45 => 'IPSECKEY', 46 => 'RRSIG', 47 => 'NSEC', 48 => 'DNSKEY', 49 => 'DHCID', 55 => 'HIP', 99 => 'SPF', 100 => 'UINFO', 101 => 'UID', 102 => 'GID', 103 => 'UNSPEC', 249 => 'TKEY', 250 => 'TSIG', 251 => 'IXFR', 252 => 'AXFR', 253 => 'MAILB', 254 => 'MAILA', 255 => 'ALL'}
10
- def initialize(config="#{ENV['HOME']}/.dnsparse")
11
- if File.exist?(config)
12
- @base,@user,@pass = File.open(config).read.split(/\n/)
13
- $stderr.puts "DEBUG: DNSParse#initialize(#{@base}, #{@user}, #{@pass})" if @debug
14
- else
15
- raise "Configuration file for DNSParse is required for intialization\nFormat of configuration file (default: #{ENV['HOME']}/.dnsparse) is:\n<url>\n<username>\n<password>\n"
16
- end
17
- end
18
-
19
- def parse_json(page,response_time=0)
20
- res = []
21
- # need to remove the json_class tag or the parser will crap itself trying to find a class to align it to
22
- page = page.gsub(/\"json_class\"\:\"PDNSResult\"\,/,'')
23
- recs = JSON.parse(page)
24
- recs.each do |row|
25
- res << PDNSResult.new('DNSParse',response_time,row["query"],row["answer"],@@dns_rtypes[row["rrtype"].to_i],row["ttl"],row["firstseen"],row["lastseen"])
26
- end
27
- res
28
- rescue Exception => e
29
- $stderr.puts "DNSParse Exception: #{e}"
30
- raise e
31
- end
32
-
33
- def parse_html(page,response_time=0)
34
- rows = []
35
- line = page.split(/<table/).grep(/ id=\"dnsparse\"/)
36
- return [] unless line.length > 0
37
- line = line[0].gsub(/[\t\n]/,'').gsub(/<\/table.*/,'')
38
- rows = line.split(/<tr.*?>/)
39
- res = []
40
- rows.collect do |row|
41
- r = row.split(/<td>/).map{|x| x.gsub(/<.*?>/,'').gsub(/\&.*?;/,'').gsub(/[\t\n]/,'')}[1,1000]
42
- if r and r[0] =~ /\w/
43
- #TXT records screw up other functions and don't provide much without a lot of subparshing. Dropping for now.
44
- if r[2]!="TXT" then
45
- res << PDNSResult.new('DNSParse',response_time,r[0],r[1],r[2],r[3],r[4],r[5])
46
- end
47
- end
48
- end
49
- res
50
- rescue Exception => e
51
- $stderr.puts "DNSParse Exception: #{e}"
52
- raise e
53
- end
54
-
55
- def lookup(label)
56
- $stderr.puts "DEBUG: DNSParse.lookup(#{label})" if @debug
57
- Timeout::timeout(240) {
58
- year = Time.now.strftime("%Y").to_i
59
- month = Time.now.strftime("%m").to_i
60
- url = "#{@base}#{label}&year=#{year - 1}"
61
- url = "#{@base}#{label}&year=#{year}" if month > 3
62
- if label =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/
63
- url.gsub!(/query\.php/,'cidr.php')
64
- elsif label =~ /\*$/
65
- url.gsub!(/query\.php/,'wildcard.php')
66
- end
67
- $stderr.puts "DEBUG: DNSParse url = #{url}" if @debug
68
- url = URI.parse url
69
- http = Net::HTTP.new(url.host, url.port)
70
- http.use_ssl = (url.scheme == 'https')
71
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
72
- http.verify_depth = 5
73
- request = Net::HTTP::Get.new(url.path+"?"+url.query)
74
- request.add_field("User-Agent", "Ruby/#{RUBY_VERSION} passivedns-client rubygem v#{PassiveDNS::Client::VERSION}")
75
- request.basic_auth @user, @pass
76
- t1 = Time.now
77
- response = http.request(request)
78
- t2 = Time.now
79
- if @base =~ /format=json/
80
- parse_json(response.body,t2-t1)
81
- else
82
- parse_html(response.body,t2-t1)
83
- end
84
- }
85
- rescue Timeout::Error => e
86
- $stderr.puts "DNSParse lookup timed out: #{label}"
87
- end
88
- end
89
- end
data.tar.gz.sig DELETED
@@ -1 +0,0 @@
1
- ��mѯm��]�y��!���N�I��̡U�媫=1��:�� �1:H�L�s��g�����0g�'$�Za�/dX�e�Dϳ�Y*�k���;���q�`��J|�����Usu4L:�?U�'!*j%@?�{Q [��H#c#u��߱�h��ܚ�d�@!� n v�=K�`Ȩ0�'����А���-"Ê�R�t�]M�i�m��2g�L���澠� rz�ݲ���{���xޢ<I4��P�B�K��"R������
metadata.gz.sig DELETED
@@ -1,2 +0,0 @@
1
- ��$Y��]B�Z�y�d��� ���Z��Uy�H(�D������d4肩���j��߄l�s�:�L"�Bq���`D�������۰��
2
- �𧞇�T_���~k��`�pxc���'��ZjP~c�!E }վMS�A���H&�m�F�� �h�=ig�j�X���xd����-0_Ƣ;a_�?Z��pX�t��2�u�j�@l 2u�x�d�(�(@R����1�Bܳ�81B��C����M���8� �S��