passivedns-client 2.1.6 → 2.1.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +0 -0
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +3 -0
- data/lib/passivedns/client.rb +77 -37
- data/lib/passivedns/client/cli.rb +149 -141
- data/lib/passivedns/client/passivedb.rb +0 -0
- data/lib/passivedns/client/provider/bfk.rb +56 -52
- data/lib/passivedns/client/provider/circl.rb +55 -39
- data/lib/passivedns/client/provider/cn360.rb +44 -33
- data/lib/passivedns/client/provider/dnsdb.rb +57 -56
- data/lib/passivedns/client/provider/mnemonic.rb +63 -55
- data/lib/passivedns/client/provider/passivetotal.rb +48 -43
- data/lib/passivedns/client/provider/riskiq.rb +59 -43
- data/lib/passivedns/client/provider/tcpiputils.rb +20 -19
- data/lib/passivedns/client/provider/virustotal.rb +60 -46
- data/lib/passivedns/client/state.rb +237 -236
- data/lib/passivedns/client/version.rb +1 -1
- data/passivedns-client.gemspec +6 -6
- data/test/helper.rb +0 -0
- data/test/test_cli.rb +34 -5
- data/test/test_passivedns-client.rb +153 -146
- metadata +23 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d8ea3feed82ed9f049ef8d7cd6ac78c087b94a630449cdbb3560fc28364ffe41
|
4
|
+
data.tar.gz: 37056ad39c2d721a3b5e6ee9f0347c5f1f79686f75eb15da99e2f155fc7b22d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 61ca76c52991e1a9e131d51412f0a855301b9555cbd0d1d6e5fe9c0071c1bc2d0b1539926148c090ad31c7db1aaff544565537c4db2f59bfd449e6747786d655
|
7
|
+
data.tar.gz: 4bae5d3265ca1723c9170dc3e1ec726a6d855ff2324b4744692f0a9a486444751723fa907ce50ef9960900fa4236b53fdfb1484f04fc4a91c7dc1e6d61889756
|
data/.gitignore
CHANGED
File without changes
|
data/Gemfile
CHANGED
File without changes
|
data/LICENSE.txt
CHANGED
File without changes
|
data/README.md
CHANGED
@@ -115,6 +115,9 @@ Or use the included tool...
|
|
115
115
|
-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!
|
116
116
|
-w# specifies the amount of time to wait, in seconds, between queries (Default: 0)
|
117
117
|
-l <count> limits the number of records returned per passive dns database queried.
|
118
|
+
|
119
|
+
Specifying a Configuration File
|
120
|
+
--config <file> specifies a config file. default: #{ENV['HOME']}/.passivedns-client
|
118
121
|
|
119
122
|
Getting Help
|
120
123
|
-v debugging information
|
data/lib/passivedns/client.rb
CHANGED
@@ -12,22 +12,56 @@ provider_path = File.dirname(__FILE__)+"/client/provider/*.rb"
|
|
12
12
|
Dir.glob(provider_path).each do |provider|
|
13
13
|
name = File.basename(provider, '.rb')
|
14
14
|
require "passivedns/client/provider/#{name}.rb"
|
15
|
-
|
15
|
+
if name != 'bfk'
|
16
|
+
$passivedns_providers << name
|
17
|
+
end
|
16
18
|
end
|
17
19
|
|
18
20
|
require 'configparser'
|
19
21
|
|
20
22
|
module PassiveDNS # :nodoc:
|
23
|
+
|
24
|
+
class SecurityControl
|
25
|
+
def allow(user_level)
|
26
|
+
raise "unimplemented"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class TLPSecurityControl < SecurityControl
|
31
|
+
LEVELS = ['white','green','yellow','red']
|
32
|
+
|
33
|
+
def initialize(tlp)
|
34
|
+
if tlp =~ /(white|green|yellow|red)/i
|
35
|
+
@tlp = tlp.downcase
|
36
|
+
@tlp_level = LEVELS.index(@tlp)
|
37
|
+
else
|
38
|
+
raise "Unknown TLP setting, #{tlp}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def allow(user_level)
|
43
|
+
user_level = LEVELS.index(user_level.downcase)
|
44
|
+
if user_level == nil
|
45
|
+
raise "Invalid user level, #{user_level}"
|
46
|
+
end
|
47
|
+
return(user_level >= @tlp_level)
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s()
|
51
|
+
@tlp
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
21
55
|
# struct to contain the results from a PassiveDNS lookup
|
22
|
-
|
56
|
+
class PDNSResult < Struct.new(:source, :response_time, :query, :answer, :rrtype, :ttl, :firstseen, :lastseen, :count, :security); end
|
23
57
|
|
24
58
|
# coodinates the lookups accross all configured PassiveDNS providers
|
25
|
-
|
59
|
+
class Client
|
26
60
|
|
27
61
|
# instantiate and configure all specified PassiveDNS providers
|
28
62
|
# pdns array of passivedns provider names, e.g., ["dnsdb","virustotal"]
|
29
63
|
# configfile filename of the passivedns-client configuration (this should probably be abstracted)
|
30
|
-
|
64
|
+
def initialize(pdns=$passivedns_providers, configfile="#{ENV['HOME']}/.passivedns-client")
|
31
65
|
cp = {}
|
32
66
|
if File.exist?(configfile)
|
33
67
|
cp = ConfigParser.new(configfile)
|
@@ -42,7 +76,7 @@ module PassiveDNS # :nodoc:
|
|
42
76
|
end
|
43
77
|
end
|
44
78
|
|
45
|
-
|
79
|
+
@pdnsdbs = []
|
46
80
|
pdns.uniq.each do |pd|
|
47
81
|
if class_map[pd]
|
48
82
|
@pdnsdbs << class_map[pd].new(cp[pd] || {})
|
@@ -51,38 +85,44 @@ module PassiveDNS # :nodoc:
|
|
51
85
|
end
|
52
86
|
end
|
53
87
|
|
54
|
-
|
55
|
-
|
88
|
+
end #initialize
|
89
|
+
|
56
90
|
# set the debug flag
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
91
|
+
def debug=(d)
|
92
|
+
@pdnsdbs.each do |pdnsdb|
|
93
|
+
pdnsdb.debug = d
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def timeout=(t)
|
98
|
+
@pdnsdbs.each do |pdnsdb|
|
99
|
+
pdnsdb.timeout = t
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
63
103
|
# perform the query lookup accross all configured PassiveDNS providers
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
104
|
+
def query(item, limit=nil)
|
105
|
+
threads = []
|
106
|
+
@pdnsdbs.each do |pdnsdb|
|
107
|
+
threads << Thread.new(item) do |q|
|
108
|
+
pdnsdb.lookup(q, limit)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
results = []
|
113
|
+
threads.each do |thr|
|
114
|
+
rv = thr.join.value
|
115
|
+
if rv
|
116
|
+
rv.each do |r|
|
117
|
+
if ["A","AAAA","NS","CNAME","PTR"].index(r.rrtype)
|
118
|
+
results << r
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
return results
|
125
|
+
end #query
|
126
|
+
|
127
|
+
end # Client
|
88
128
|
end # PassiveDNS
|
@@ -7,7 +7,7 @@ require 'pp'
|
|
7
7
|
module PassiveDNS # :nodoc:
|
8
8
|
# Handles all the command-line parsing, state tracking, and dispatching queries to the PassiveDNS::Client instance
|
9
9
|
# CLInterface is aliased by CLI
|
10
|
-
|
10
|
+
class CLInterface
|
11
11
|
# generates a mapping between the option letter for each PassiveDNS provider and the class
|
12
12
|
def self.get_letter_map
|
13
13
|
letter_map = {}
|
@@ -41,24 +41,25 @@ module PassiveDNS # :nodoc:
|
|
41
41
|
origARGV = ARGV.dup
|
42
42
|
ARGV.replace(args)
|
43
43
|
opts = GetoptLong.new(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
44
|
+
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
|
45
|
+
[ '--debug', '-v', GetoptLong::NO_ARGUMENT ],
|
46
|
+
[ '--database', '-d', GetoptLong::REQUIRED_ARGUMENT ],
|
47
|
+
|
48
|
+
[ '--gdf', '-g', GetoptLong::NO_ARGUMENT ],
|
49
|
+
[ '--graphviz', '-z', GetoptLong::NO_ARGUMENT ],
|
50
|
+
[ '--graphml', '-m', GetoptLong::NO_ARGUMENT ],
|
51
|
+
[ '--csv', '-c', GetoptLong::NO_ARGUMENT ],
|
52
|
+
[ '--xml', '-x', GetoptLong::NO_ARGUMENT ],
|
53
|
+
[ '--yaml', '-y', GetoptLong::NO_ARGUMENT ],
|
54
|
+
[ '--json', '-j', GetoptLong::NO_ARGUMENT ],
|
55
|
+
[ '--text', '-t', GetoptLong::NO_ARGUMENT ],
|
56
|
+
[ '--sep', '-s', GetoptLong::REQUIRED_ARGUMENT ],
|
57
|
+
|
58
|
+
[ '--sqlite3', '-f', GetoptLong::REQUIRED_ARGUMENT ],
|
59
|
+
[ '--recurse', '-r', GetoptLong::REQUIRED_ARGUMENT ],
|
60
|
+
[ '--wait', '-w', GetoptLong::REQUIRED_ARGUMENT ],
|
61
|
+
[ '--limit', '-l', GetoptLong::REQUIRED_ARGUMENT ],
|
62
|
+
[ '--config', GetoptLong::REQUIRED_ARGUMENT ]
|
62
63
|
)
|
63
64
|
|
64
65
|
letter_map = get_letter_map
|
@@ -74,17 +75,18 @@ module PassiveDNS # :nodoc:
|
|
74
75
|
:debug => false,
|
75
76
|
:sqlitedb => nil,
|
76
77
|
:limit => nil,
|
77
|
-
:help => false
|
78
|
+
:help => false,
|
79
|
+
:configfile => "#{ENV['HOME']}/.passivedns-client"
|
78
80
|
}
|
79
81
|
|
80
82
|
opts.each do |opt, arg|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
83
|
+
case opt
|
84
|
+
when '--help'
|
85
|
+
options[:help] = true
|
86
|
+
when '--debug'
|
87
|
+
options[:debug] = true
|
88
|
+
when '--database'
|
89
|
+
arg.split(//).each do |c|
|
88
90
|
if c == ','
|
89
91
|
next
|
90
92
|
elsif letter_map[c]
|
@@ -94,60 +96,62 @@ module PassiveDNS # :nodoc:
|
|
94
96
|
usage(letter_map)
|
95
97
|
end
|
96
98
|
end
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
99
|
+
when '--gdf'
|
100
|
+
options[:format] = 'gdf'
|
101
|
+
when '--graphviz'
|
102
|
+
options[:format] = 'graphviz'
|
103
|
+
when '--graphml'
|
104
|
+
options[:format] = 'graphml'
|
105
|
+
when '--csv'
|
106
|
+
options[:format] = 'text'
|
107
|
+
options[:sep] = ','
|
108
|
+
when '--yaml'
|
109
|
+
options[:format] = 'yaml'
|
110
|
+
when '--xml'
|
111
|
+
options[:format] = 'xml'
|
112
|
+
when '--json'
|
113
|
+
options[:format] = 'json'
|
114
|
+
when '--text'
|
115
|
+
options[:format] = 'text'
|
116
|
+
when '--sep'
|
117
|
+
options[:sep] = arg
|
118
|
+
when '--recurse'
|
119
|
+
options[:recursedepth] = arg.to_i
|
120
|
+
when '--wait'
|
121
|
+
options[:wait] = arg.to_i
|
122
|
+
when '--sqlite3'
|
123
|
+
options[:sqlitedb] = arg
|
124
|
+
when '--limit'
|
125
|
+
options[:limit] = arg.to_i
|
126
|
+
when '--config'
|
127
|
+
options[:configfile] = arg
|
128
|
+
else
|
129
|
+
options[:help] = true
|
130
|
+
end
|
127
131
|
end
|
128
132
|
args = ARGV.dup
|
129
133
|
ARGV.replace(origARGV)
|
130
134
|
|
131
135
|
if options[:pdnsdbs].length == 0
|
132
|
-
|
136
|
+
options[:pdnsdbs] << "bfk"
|
133
137
|
end
|
134
138
|
|
135
139
|
if options[:pdnsdbs].index("bfk") and options[:recursedepth] > 1 and options[:wait] < 60
|
136
|
-
|
137
|
-
|
140
|
+
options[:wait] = 60
|
141
|
+
$stderr.puts "Enforcing a minimal 60 second wait when using BFK for recursive crawling"
|
138
142
|
end
|
139
143
|
|
140
144
|
if options[:debug]
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
145
|
+
$stderr.puts "Using the following databases: #{options[:pdnsdbs].join(", ")}"
|
146
|
+
$stderr.puts "Recursions: #{options[:recursedepth]}, Wait time: #{options[:wait]}, Limit: #{options[:limit] or 'none'}"
|
147
|
+
if options[:format] == "text" or options[:format] == "csv"
|
148
|
+
$stderr.puts "Output format: #{options[:format]} (sep=\"#{options[:sep]}\")"
|
149
|
+
else
|
150
|
+
$stderr.puts "Output format: #{options[:format]}"
|
151
|
+
end
|
152
|
+
if ENV['http_proxy']
|
153
|
+
$stderr.puts "Using proxy settings: http_proxy=#{ENV['http_proxy']}, https_proxy=#{ENV['https_proxy']}"
|
154
|
+
end
|
151
155
|
end
|
152
156
|
|
153
157
|
[options, args]
|
@@ -158,36 +162,39 @@ module PassiveDNS # :nodoc:
|
|
158
162
|
def self.usage(letter_map)
|
159
163
|
databases = letter_map.keys.sort.join("")
|
160
164
|
help_text = ""
|
161
|
-
|
165
|
+
help_text << "Usage: #{$0} [-d [#{databases}]] [-g|-v|-m|-c|-x|-y|-j|-t] [-os <sep>] [-f <file>] [-r#|-w#|-v] [-l <count>] [--config <file>] <ip|domain|cidr>\n"
|
162
166
|
help_text << "Passive DNS Providers\n"
|
163
|
-
|
167
|
+
help_text << " -d#{databases} uses all of the available passive dns database\n"
|
164
168
|
letter_map.keys.sort.each do |l|
|
165
169
|
help_text << " -d#{l} use #{letter_map[l][0]}\n"
|
166
170
|
end
|
167
|
-
|
168
|
-
|
171
|
+
help_text << " -dvt uses VirusTotal and TCPIPUtils (for example)\n"
|
172
|
+
help_text << "\n"
|
169
173
|
help_text << "Output Formatting\n"
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
174
|
+
help_text << " -g link-nodal GDF visualization definition\n"
|
175
|
+
help_text << " -z link-nodal graphviz visualization definition\n"
|
176
|
+
help_text << " -m link-nodal graphml visualization definition\n"
|
177
|
+
help_text << " -c CSV\n"
|
178
|
+
help_text << " -x XML\n"
|
179
|
+
help_text << " -y YAML\n"
|
180
|
+
help_text << " -j JSON\n"
|
181
|
+
help_text << " -t ASCII text (default)\n"
|
182
|
+
help_text << " -s <sep> specifies a field separator for text output, default is tab\n"
|
183
|
+
help_text << "\n"
|
180
184
|
help_text << "State and Recursion\n"
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
185
|
+
help_text << " -f[file] specifies a sqlite3 database used to read the current state - useful for large result sets and generating graphs of previous runs.\n"
|
186
|
+
help_text << " -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!\n"
|
187
|
+
help_text << " -w# specifies the amount of time to wait, in seconds, between queries (Default: 0)\n"
|
188
|
+
help_text << " -l <count> limits the number of records returned per passive dns database queried.\n"
|
189
|
+
help_text << "\n"
|
190
|
+
help_text << "Specifying a Configuration File\n"
|
191
|
+
help_text << " --config <file> specifies a config file. default: #{ENV['HOME']}/.passivedns-client\n"
|
192
|
+
help_text << "\n"
|
186
193
|
help_text << "Getting Help\n"
|
187
194
|
help_text << " -h hello there. This option produces this helpful help information on how to access help.\n"
|
188
|
-
|
195
|
+
help_text << " -v debugging information\n"
|
189
196
|
|
190
|
-
|
197
|
+
help_text
|
191
198
|
end
|
192
199
|
|
193
200
|
# performs a stateful, recursive (if desired) passive DNS lookup against all specified providers
|
@@ -196,59 +203,60 @@ module PassiveDNS # :nodoc:
|
|
196
203
|
wait = options[:wait]
|
197
204
|
debug = options[:debug]
|
198
205
|
limit = options[:limit]
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
206
|
+
puts "pdnslookup: #{state.level} #{recursedepth}" if debug
|
207
|
+
level = 0
|
208
|
+
while level < recursedepth
|
209
|
+
puts "pdnslookup: #{level} < #{recursedepth}" if debug
|
210
|
+
state.each_query(recursedepth) do |q|
|
211
|
+
rv = pdnsclient.query(q,limit)
|
212
|
+
if rv
|
213
|
+
rv.each do |r|
|
214
|
+
if ["A","AAAA","NS","CNAME","PTR"].index(r.rrtype)
|
215
|
+
puts "pdnslookup: #{r.to_s}" if debug
|
216
|
+
state.add_result(r)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
else
|
220
|
+
state.update_query(rv,'failed')
|
221
|
+
end
|
222
|
+
sleep wait if level < recursedepth
|
223
|
+
end
|
224
|
+
level += 1
|
225
|
+
end
|
226
|
+
state
|
220
227
|
end
|
221
228
|
|
222
229
|
# returns a string transforming all the PassiveDNS::PDNSResult stored in the state object into text/xml/json/etc.
|
223
230
|
def self.results_to_s(state,options)
|
224
231
|
format = options[:format]
|
225
232
|
sep = options[:sep]
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
233
|
+
case format
|
234
|
+
when 'text'
|
235
|
+
PassiveDNS::PDNSResult.members.join(sep)+"\n"+state.to_s(sep)
|
236
|
+
when 'yaml'
|
237
|
+
state.to_yaml
|
238
|
+
when 'xml'
|
239
|
+
state.to_xml
|
240
|
+
when 'json'
|
241
|
+
state.to_json
|
242
|
+
when 'gdf'
|
243
|
+
state.to_gdf
|
244
|
+
when 'graphviz'
|
245
|
+
state.to_graphviz
|
246
|
+
when 'graphml'
|
247
|
+
state.to_graphml
|
248
|
+
end
|
242
249
|
end
|
243
250
|
|
244
251
|
# create a state instance
|
245
252
|
def self.create_state(sqlitedb=nil)
|
246
253
|
state = nil
|
247
254
|
if sqlitedb
|
248
|
-
|
255
|
+
state = PassiveDNS::PDNSToolStateDB.new(sqlitedb)
|
249
256
|
else
|
250
|
-
|
257
|
+
state = PassiveDNS::PDNSToolState.new
|
251
258
|
end
|
259
|
+
state
|
252
260
|
end
|
253
261
|
|
254
262
|
# main method, takes command-line arguments and performs the desired queries and outputs
|
@@ -257,24 +265,24 @@ module PassiveDNS # :nodoc:
|
|
257
265
|
if options[:help]
|
258
266
|
return usage(get_letter_map)
|
259
267
|
end
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
268
|
+
if options[:recursedepth] > 3
|
269
|
+
$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)"
|
270
|
+
sleep 60
|
271
|
+
end
|
264
272
|
state = create_state(options[:sqlitedb])
|
265
273
|
state.debug = options[:debug]
|
266
274
|
|
267
|
-
pdnsclient = PassiveDNS::Client.new(options[:pdnsdbs])
|
275
|
+
pdnsclient = PassiveDNS::Client.new(options[:pdnsdbs], options[:configfile])
|
268
276
|
pdnsclient.debug = options[:debug]
|
269
277
|
|
270
278
|
if items.length > 0
|
271
|
-
|
272
|
-
|
273
|
-
|
279
|
+
items.each do |arg|
|
280
|
+
state.add_query(arg,'pending',0)
|
281
|
+
end
|
274
282
|
else
|
275
|
-
|
276
|
-
|
277
|
-
|
283
|
+
$stdin.each_line do |l|
|
284
|
+
state.add_query(l.chomp,'pending',0)
|
285
|
+
end
|
278
286
|
end
|
279
287
|
pdnslookup(state,pdnsclient,options)
|
280
288
|
results_to_s(state,options)
|