CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/ui/console/command_dispatcher/dns.rb
Views: 1904
1
# -*- coding: binary -*-
2
3
module Msf
4
module Ui
5
module Console
6
module CommandDispatcher
7
8
class DNS
9
10
include Msf::Ui::Console::CommandDispatcher
11
12
ADD_USAGE = 'dns [add] [--index <insertion index>] [--rule <wildcard DNS entry>] [--session <session id>] <resolver> ...'.freeze
13
@@add_opts = Rex::Parser::Arguments.new(
14
['-i', '--index'] => [true, 'Index to insert at'],
15
['-r', '--rule'] => [true, 'Set a DNS wildcard entry to match against'],
16
['-s', '--session'] => [true, 'Force the DNS request to occur over a particular channel (override routing rules)']
17
)
18
19
ADD_STATIC_USAGE = 'dns [add-static] <hostname> <IP address> ...'.freeze
20
21
REMOVE_USAGE = 'dns [remove/del] -i <entry id> [-i <entry id> ...]'.freeze
22
@@remove_opts = Rex::Parser::Arguments.new(
23
['-i', '--index'] => [true, 'Index to remove at']
24
)
25
26
REMOVE_STATIC_USAGE = 'dns [remove-static] <hostname> [<IP address> ...]'.freeze
27
28
RESET_CONFIG_USAGE = 'dns [reset-config] [-y/--yes] [--system]'.freeze
29
@@reset_config_opts = Rex::Parser::Arguments.new(
30
['-y', '--yes'] => [false, 'Assume yes and do not prompt for confirmation before resetting'],
31
['--system'] => [false, 'Include the system resolver']
32
)
33
34
RESOLVE_USAGE = 'dns [resolve] [-f <address family>] <hostname> ...'.freeze
35
@@resolve_opts = Rex::Parser::Arguments.new(
36
# same usage syntax as Rex::Post::Meterpreter::Ui::Console::CommandDispatcher::Stdapi
37
['-f'] => [true, 'Address family - IPv4 or IPv6 (default IPv4)']
38
)
39
40
def initialize(driver)
41
super
42
end
43
44
def name
45
'DNS'
46
end
47
48
def commands
49
commands = {}
50
51
if framework.features.enabled?(Msf::FeatureManager::DNS)
52
commands = {
53
'dns' => "Manage Metasploit's DNS resolving behaviour"
54
}
55
end
56
commands
57
end
58
59
#
60
# Tab completion for the dns command
61
#
62
# @param str [String] the string currently being typed before tab was hit
63
# @param words [Array<String>] the previously completed words on the command line. The array
64
# contains at least one entry when tab completion has reached this stage since the command itself has been completed
65
def cmd_dns_tabs(str, words)
66
return if driver.framework.dns_resolver.nil?
67
68
subcommands = %w[ add add-static delete flush-cache flush-entries flush-static help print query remove remove-static reset-config resolve ]
69
if words.length == 1
70
return subcommands.select { |opt| opt.start_with?(str) }
71
end
72
73
cmd = words[1]
74
case cmd
75
when 'add'
76
# We expect a repeating pattern of tag (e.g. -r) and then a value (e.g. *.metasploit.com)
77
# Once this pattern is violated, we're just specifying DNS servers at that point.
78
tag_is_expected = true
79
if words.length > 2
80
words[2..-1].each do |word|
81
if tag_is_expected && !word.start_with?('-')
82
return
83
end
84
tag_is_expected = !tag_is_expected
85
end
86
end
87
88
case words[-1]
89
when '-r', '--rule'
90
# Hard to auto-complete a rule with any meaningful value; just return
91
return
92
when '-s', '--session'
93
session_ids = driver.framework.sessions.keys.map { |k| k.to_s }
94
return session_ids.select { |id| id.start_with?(str) }
95
when /^-/
96
# Unknown tag
97
return
98
end
99
100
options = @@add_opts.option_keys.select { |opt| opt.start_with?(str) }
101
options << '' # Prevent tab-completion of a dash, given they could provide an IP address at this point
102
return options
103
when 'add-static'
104
if words.length == 2
105
# tab complete existing hostnames because they can have more than one IP address
106
return resolver.static_hostnames.each.select { |hostname,_| hostname.downcase.start_with?(str.downcase) }.map { |hostname,_| hostname }
107
end
108
when 'help'
109
# These commands don't have any arguments
110
return subcommands.select { |sc| sc.start_with?(str) }
111
when 'remove','delete'
112
if words[-1] == '-i'
113
return
114
else
115
return @@remove_opts.option_keys.select { |opt| opt.start_with?(str) }
116
end
117
when 'remove-static'
118
if words.length == 2
119
return resolver.static_hostnames.each.select { |hostname,_| hostname.downcase.start_with?(str.downcase) }.map { |hostname,_| hostname }
120
elsif words.length > 2
121
hostname = words[2]
122
ip_addresses = resolver.static_hostnames.get(hostname, Dnsruby::Types::A) + resolver.static_hostnames.get(hostname, Dnsruby::Types::AAAA)
123
return ip_addresses.map(&:to_s).select { |ip_address| ip_address.start_with?(str) }
124
end
125
when 'reset-config'
126
@@reset_config_opts.option_keys.select { |opt| opt.start_with?(str) }
127
when 'resolve','query'
128
if words[-1] == '-f'
129
families = %w[ IPv4 IPv6 ] # The family argument is case-insensitive
130
return families.select { |family| family.downcase.start_with?(str.downcase) }
131
else
132
@@resolve_opts.option_keys.select { |opt| opt.start_with?(str) }
133
end
134
end
135
end
136
137
def cmd_dns_help(*args)
138
if args.first.present?
139
handler = "#{args.first.gsub('-', '_')}_dns"
140
if respond_to?("#{handler}_help")
141
# if it is a valid command with dedicated help information
142
return send("#{handler}_help")
143
elsif respond_to?(handler)
144
# if it is a valid command without dedicated help information
145
print_error("No help menu is available for #{args.first}")
146
return
147
else
148
print_error("Invalid subcommand: #{args.first}")
149
end
150
end
151
152
print_line "Manage Metasploit's DNS resolution behaviour"
153
print_line
154
print_line "USAGE:"
155
print_line " #{ADD_USAGE}"
156
print_line " #{ADD_STATIC_USAGE}"
157
print_line " #{REMOVE_USAGE}"
158
print_line " #{REMOVE_STATIC_USAGE}"
159
print_line " dns [flush-cache]"
160
print_line " dns [flush-entries]"
161
print_line " dns [flush-static]"
162
print_line " dns [print]"
163
print_line " #{RESET_CONFIG_USAGE}"
164
print_line " #{RESOLVE_USAGE}"
165
print_line " dns [help] [subcommand]"
166
print_line
167
print_line "SUBCOMMANDS:"
168
print_line " add - Add a DNS resolution entry to resolve certain domain names through a particular DNS resolver"
169
print_line " add-static - Add a statically defined hostname"
170
print_line " flush-cache - Remove all cached DNS answers"
171
print_line " flush-entries - Remove all configured DNS resolution entries"
172
print_line " flush-static - Remove all statically defined hostnames"
173
print_line " print - Show all configured DNS resolution entries"
174
print_line " remove - Delete a DNS resolution entry"
175
print_line " remove-static - Delete a statically defined hostname"
176
print_line " reset-config - Reset the DNS configuration"
177
print_line " resolve - Resolve a hostname"
178
print_line
179
print_line "EXAMPLES:"
180
print_line " Display help information for the 'add' subcommand"
181
print_line " dns help add"
182
print_line
183
end
184
185
#
186
# Manage Metasploit's DNS resolution rules
187
#
188
def cmd_dns(*args)
189
if driver.framework.dns_resolver.nil?
190
print_warning("Run the #{Msf::Ui::Tip.highlight("save")} command and restart the console for this feature configuration to take effect.")
191
return
192
end
193
194
args << 'print' if args.length == 0
195
# Short-circuit help
196
if args.delete("-h") || args.delete("--help")
197
subcommand = args.first
198
if subcommand && respond_to?("#{subcommand.gsub('-', '_')}_dns_help")
199
# if it is a valid command with dedicated help information
200
send("#{subcommand.gsub('-', '_')}_dns_help")
201
else
202
# otherwise print the top-level help information
203
cmd_dns_help
204
end
205
return
206
end
207
208
action = args.shift
209
begin
210
case action
211
when "add"
212
add_dns(*args)
213
when "add-static"
214
add_static_dns(*args)
215
when "flush-entries"
216
flush_entries_dns
217
when "flush-cache"
218
flush_cache_dns
219
when "flush-static"
220
flush_static_dns
221
when "help"
222
cmd_dns_help(*args)
223
when "print"
224
print_dns
225
when "remove", "rm", "delete", "del"
226
remove_dns(*args)
227
when "remove-static"
228
remove_static_dns(*args)
229
when "reset-config"
230
reset_config_dns(*args)
231
when "resolve", "query"
232
resolve_dns(*args)
233
else
234
print_error("Invalid command. To view help: dns -h")
235
end
236
rescue ::ArgumentError => e
237
print_error(e.message)
238
end
239
end
240
241
def add_dns(*args)
242
rules = ['*']
243
first_rule = true
244
comm = nil
245
resolvers = []
246
index = -1
247
@@add_opts.parse(args) do |opt, idx, val|
248
unless resolvers.empty? || opt.nil?
249
raise ::ArgumentError.new("Invalid command near #{opt}")
250
end
251
case opt
252
when '-i', '--index'
253
raise ::ArgumentError.new("Not a valid index: #{val}") unless val.to_i > 0
254
255
index = val.to_i - 1
256
when '-r', '--rule'
257
raise ::ArgumentError.new('No rule specified') if val.nil?
258
259
rules.clear if first_rule # if the user defines even one rule, clear the defaults
260
first_rule = false
261
rules << val
262
when '-s', '--session'
263
if val.nil?
264
raise ::ArgumentError.new('No session specified')
265
end
266
267
unless comm.nil?
268
raise ::ArgumentError.new('Only one session can be specified')
269
end
270
271
comm = val
272
when nil
273
val = 'black-hole' if val.casecmp?('blackhole')
274
resolvers << val
275
else
276
raise ::ArgumentError.new("Unknown flag: #{opt}")
277
end
278
end
279
280
# The remaining args should be the DNS servers
281
if resolvers.length < 1
282
raise ::ArgumentError.new('You must specify at least one upstream DNS resolver')
283
end
284
285
resolvers.each do |resolver|
286
unless Rex::Proto::DNS::UpstreamRule.valid_resolver?(resolver)
287
message = "Invalid DNS resolver: #{resolver}."
288
if (suggestions = Rex::Proto::DNS::UpstreamRule.spell_check_resolver(resolver)).present?
289
message << " Did you mean #{suggestions.first}?"
290
end
291
292
raise ::ArgumentError.new(message)
293
end
294
end
295
296
comm_obj = nil
297
298
unless comm.nil?
299
raise ::ArgumentError.new("Not a valid session: #{comm}") unless comm =~ /\A-?[0-9]+\Z/
300
301
comm_obj = driver.framework.sessions.get(comm.to_i)
302
raise ::ArgumentError.new("Session does not exist: #{comm}") unless comm_obj
303
raise ::ArgumentError.new("Socket Comm (Session #{comm}) does not implement Rex::Socket::Comm") unless comm_obj.is_a? ::Rex::Socket::Comm
304
305
if resolvers.any? { |resolver| SPECIAL_RESOLVERS.include?(resolver.downcase) }
306
print_warning("The session argument will be ignored for the system resolver")
307
end
308
end
309
310
rules.each_with_index do |rule, offset|
311
print_warning("DNS rule #{rule} does not contain wildcards, it will not match subdomains") unless rule.include?('*')
312
driver.framework.dns_resolver.add_upstream_rule(
313
resolvers,
314
comm: comm_obj,
315
wildcard: rule,
316
index: (index == -1 ? -1 : offset + index)
317
)
318
end
319
320
print_good("#{rules.length} DNS #{rules.length > 1 ? 'entries' : 'entry'} added")
321
end
322
323
def add_dns_help
324
print_line "USAGE:"
325
print_line " #{ADD_USAGE}"
326
print_line @@add_opts.usage
327
print_line "RESOLVERS:"
328
print_line " ipv4 / ipv6 address - The IP address of an upstream DNS server to resolve from"
329
print_line " #{Rex::Proto::DNS::UpstreamResolver::Type::BLACK_HOLE.to_s.ljust(19)} - Drop all queries"
330
print_line " #{Rex::Proto::DNS::UpstreamResolver::Type::STATIC.to_s.ljust(19) } - Reply with statically configured addresses (only for A/AAAA records)"
331
print_line " #{Rex::Proto::DNS::UpstreamResolver::Type::SYSTEM.to_s.ljust(19) } - Use the host operating systems DNS resolution functionality (only for A/AAAA records)"
332
print_line
333
print_line "EXAMPLES:"
334
print_line " Set the DNS server(s) to be used for *.metasploit.com to 192.168.1.10"
335
print_line " dns add --rule *.metasploit.com 192.168.1.10"
336
print_line
337
print_line " Add multiple entries at once"
338
print_line " dns add --rule *.metasploit.com --rule *.google.com 192.168.1.10 192.168.1.11"
339
print_line
340
print_line " Set the DNS server(s) to be used for *.metasploit.com to 192.168.1.10, but specifically to go through session 2"
341
print_line " dns add --session 2 --rule *.metasploit.com 192.168.1.10"
342
end
343
344
def add_static_dns(*args)
345
if args.length < 2
346
raise ::ArgumentError.new('A hostname and IP address must be provided')
347
end
348
349
hostname = args.shift
350
if !Rex::Proto::DNS::StaticHostnames.is_valid_hostname?(hostname)
351
raise ::ArgumentError.new("Invalid hostname: #{hostname}")
352
end
353
354
ip_addresses = args
355
if (ip_address = ip_addresses.find { |a| !Rex::Socket.is_ip_addr?(a) })
356
raise ::ArgumentError.new("Invalid IP address: #{ip_address}")
357
end
358
359
ip_addresses.each do |ip_address|
360
resolver.static_hostnames.add(hostname, ip_address)
361
print_status("Added static hostname mapping #{hostname} to #{ip_address}")
362
end
363
end
364
365
def add_static_dns_help
366
print_line "USAGE:"
367
print_line " #{ADD_STATIC_USAGE}"
368
print_line
369
print_line "EXAMPLES:"
370
print_line " Define a static entry mapping localhost6 to ::1"
371
print_line " dns add-static localhost6 ::1"
372
end
373
374
#
375
# Query a hostname using the configuration. This is useful for debugging and
376
# inspecting the active settings.
377
#
378
def resolve_dns(*args)
379
names = []
380
query_type = Dnsruby::Types::A
381
382
@@resolve_opts.parse(args) do |opt, idx, val|
383
unless names.empty? || opt.nil?
384
raise ::ArgumentError.new("Invalid command near #{opt}")
385
end
386
case opt
387
when '-f'
388
case val.downcase
389
when 'ipv4'
390
query_type = Dnsruby::Types::A
391
when'ipv6'
392
query_type = Dnsruby::Types::AAAA
393
else
394
raise ::ArgumentError.new("Invalid family: #{val}")
395
end
396
when nil
397
names << val
398
else
399
raise ::ArgumentError.new("Unknown flag: #{opt}")
400
end
401
end
402
403
if names.length < 1
404
raise ::ArgumentError.new('You must specify at least one hostname to resolve')
405
end
406
407
tbl = Table.new(
408
Table::Style::Default,
409
'Header' => 'Host resolutions',
410
'Prefix' => "\n",
411
'Postfix' => "\n",
412
'Columns' => ['Hostname', 'IP Address', 'Rule #', 'Rule', 'Resolver', 'Comm channel'],
413
'ColProps' => { 'Hostname' => { 'Strip' => false } },
414
'SortIndex' => -1,
415
'WordWrap' => false
416
)
417
names.each do |name|
418
upstream_rule = resolver.upstream_rules.find { |ur| ur.matches_name?(name) }
419
if upstream_rule.nil?
420
tbl << [name, '[Failed To Resolve]', '', '', '', '']
421
next
422
end
423
424
upstream_rule_idx = resolver.upstream_rules.index(upstream_rule) + 1
425
426
begin
427
result = resolver.query(name, query_type)
428
rescue NoResponseError
429
tbl = append_resolver_cells!(tbl, upstream_rule, prefix: [name, '[Failed To Resolve]'], index: upstream_rule_idx)
430
else
431
if result.answer.empty?
432
tbl = append_resolver_cells!(tbl, upstream_rule, prefix: [name, '[Failed To Resolve]'], index: upstream_rule_idx)
433
else
434
result.answer.select do |answer|
435
answer.type == query_type
436
end.map(&:address).map(&:to_s).each do |address|
437
tbl = append_resolver_cells!(tbl, upstream_rule, prefix: [name, address], index: upstream_rule_idx)
438
end
439
end
440
end
441
end
442
print(tbl.to_s)
443
end
444
445
def resolve_dns_help
446
print_line "USAGE:"
447
print_line " #{RESOLVE_USAGE}"
448
print_line @@resolve_opts.usage
449
print_line "EXAMPLES:"
450
print_line " Resolve a hostname to an IPv6 address using the current configuration"
451
print_line " dns resolve -f IPv6 www.metasploit.com"
452
print_line
453
end
454
455
#
456
# Remove all matching user-configured DNS entries
457
#
458
def remove_dns(*args)
459
remove_ids = []
460
@@remove_opts.parse(args) do |opt, idx, val|
461
case opt
462
when '-i', '--index'
463
raise ::ArgumentError.new("Not a valid index: #{val}") unless val.to_i > 0
464
465
remove_ids << val.to_i - 1
466
end
467
end
468
469
if remove_ids.empty?
470
raise ::ArgumentError.new('At least one index to remove must be provided')
471
end
472
473
removed = resolver.remove_ids(remove_ids)
474
print_warning('Some entries were not removed') unless removed.length == remove_ids.length
475
if removed.length > 0
476
print_good("#{removed.length} DNS #{removed.length > 1 ? 'entries' : 'entry'} removed")
477
print_dns_set('Deleted entries', removed, ids: [nil] * removed.length)
478
end
479
end
480
481
def remove_dns_help
482
print_line "USAGE:"
483
print_line " #{REMOVE_USAGE}"
484
print_line(@@remove_opts.usage)
485
print_line "EXAMPLES:"
486
print_line " Delete the DNS resolution rule #3"
487
print_line " dns remove -i 3"
488
print_line
489
print_line " Delete multiple rules in one command"
490
print_line " dns remove -i 3 -i 4 -i 5"
491
print_line
492
end
493
494
def remove_static_dns(*args)
495
if args.length < 1
496
raise ::ArgumentError.new('A hostname must be provided')
497
end
498
499
hostname = args.shift
500
if !Rex::Proto::DNS::StaticHostnames.is_valid_hostname?(hostname)
501
raise ::ArgumentError.new("Invalid hostname: #{hostname}")
502
end
503
504
ip_addresses = args
505
if ip_addresses.empty?
506
ip_addresses = resolver.static_hostnames.get(hostname, Dnsruby::Types::A) + resolver.static_hostnames.get(hostname, Dnsruby::Types::AAAA)
507
if ip_addresses.empty?
508
print_status("There are no definitions for hostname: #{hostname}")
509
end
510
elsif (ip_address = ip_addresses.find { |ip| !Rex::Socket.is_ip_addr?(ip) })
511
raise ::ArgumentError.new("Invalid IP address: #{ip_address}")
512
end
513
514
ip_addresses.each do |ip_address|
515
resolver.static_hostnames.delete(hostname, ip_address)
516
print_status("Removed static hostname mapping #{hostname} to #{ip_address}")
517
end
518
end
519
520
def remove_static_dns_help
521
print_line "USAGE:"
522
print_line " #{REMOVE_STATIC_USAGE}"
523
print_line
524
print_line "EXAMPLES:"
525
print_line " Remove all IPv4 and IPv6 addresses for 'localhost'"
526
print_line " dns remove-static localhost"
527
print_line
528
end
529
530
def reset_config_dns(*args)
531
add_system_resolver = false
532
should_confirm = true
533
@@reset_config_opts.parse(args) do |opt, idx, val|
534
case opt
535
when '--system'
536
add_system_resolver = true
537
when '-y', '--yes'
538
should_confirm = false
539
end
540
end
541
542
if should_confirm
543
print("Are you sure you want to reset the DNS configuration? [y/N]: ")
544
response = gets.downcase.chomp
545
return unless response =~ /^y/i
546
end
547
548
resolver.reinit
549
print_status('The DNS configuration has been reset')
550
551
if add_system_resolver
552
# if the user requested that we add the system resolver
553
system_resolver = Rex::Proto::DNS::UpstreamResolver.create_system
554
# first find the default, catch-all rule
555
default_rule = resolver.upstream_rules.find { |ur| ur.matches_all? }
556
if default_rule.nil?
557
resolver.add_upstream_rule([ system_resolver ])
558
else
559
# if the first resolver is for static hostnames, insert after that one
560
if default_rule.resolvers.first&.type == Rex::Proto::DNS::UpstreamResolver::Type::STATIC
561
index = 1
562
else
563
index = 0
564
end
565
default_rule.resolvers.insert(index, system_resolver)
566
end
567
end
568
569
print_dns
570
571
if ENV['PROXYCHAINS_CONF_FILE'] && !add_system_resolver
572
print_warning('Detected proxychains but the system resolver was not added')
573
end
574
end
575
576
def reset_config_dns_help
577
print_line "USAGE:"
578
print_line " #{RESET_CONFIG_USAGE}"
579
print_line @@reset_config_opts.usage
580
print_line "EXAMPLES:"
581
print_line " Reset the configuration without prompting to confirm"
582
print_line " dns reset-config --yes"
583
print_line
584
end
585
586
#
587
# Delete all cached DNS answers
588
#
589
def flush_cache_dns
590
resolver.cache.flush
591
print_good('DNS cache flushed')
592
end
593
594
#
595
# Delete all user-configured DNS settings
596
#
597
def flush_entries_dns
598
resolver.flush
599
print_good('DNS entries flushed')
600
end
601
602
def flush_static_dns
603
resolver.static_hostnames.flush
604
print_good('DNS static hostnames flushed')
605
end
606
607
#
608
# Display the user-configured DNS settings
609
#
610
def print_dns
611
default_domain = 'N/A'
612
if resolver.defname? && resolver.domain.present?
613
default_domain = resolver.domain
614
end
615
print_line("Default search domain: #{default_domain}")
616
617
searchlist = resolver.searchlist
618
case searchlist.length
619
when 0
620
print_line('Default search list: N/A')
621
when 1
622
print_line("Default search list: #{searchlist.first}")
623
else
624
print_line('Default search list:')
625
searchlist.each do |entry|
626
print_line(" * #{entry}")
627
end
628
end
629
print_line("Current cache size: #{resolver.cache.records.length}")
630
631
upstream_rules = resolver.upstream_rules
632
print_dns_set('Resolver rule entries', upstream_rules, ids: (1..upstream_rules.length).to_a)
633
if upstream_rules.empty?
634
print_line
635
print_error('No DNS nameserver entries configured')
636
end
637
638
tbl = Table.new(
639
Table::Style::Default,
640
'Header' => 'Static hostnames',
641
'Prefix' => "\n",
642
'Postfix' => "\n",
643
'Columns' => ['Hostname', 'IPv4 Address', 'IPv6 Address'],
644
'ColProps' => { 'Hostname' => { 'Strip' => false } },
645
'SortIndex' => -1,
646
'WordWrap' => false
647
)
648
resolver.static_hostnames.sort_by { |hostname, _| hostname }.each do |hostname, addresses|
649
ipv4_addresses = addresses.fetch(Dnsruby::Types::A, []).sort_by(&:to_i)
650
ipv6_addresses = addresses.fetch(Dnsruby::Types::AAAA, []).sort_by(&:to_i)
651
if (ipv4_addresses.length <= 1 && ipv6_addresses.length <= 1) && ((ipv4_addresses + ipv6_addresses).length > 0)
652
tbl << [hostname, ipv4_addresses.first, ipv6_addresses.first]
653
else
654
tbl << [hostname, '', '']
655
0.upto([ipv4_addresses.length, ipv6_addresses.length].max - 1) do |idx|
656
tbl << [TABLE_INDENT, ipv4_addresses[idx], ipv6_addresses[idx]]
657
end
658
end
659
end
660
print_line(tbl.to_s)
661
if resolver.static_hostnames.empty?
662
print_line('No static hostname entries are configured')
663
end
664
end
665
666
private
667
668
SPECIAL_RESOLVERS = [
669
Rex::Proto::DNS::UpstreamResolver::Type::BLACK_HOLE.to_s.downcase,
670
Rex::Proto::DNS::UpstreamResolver::Type::SYSTEM.to_s.downcase
671
].freeze
672
673
TABLE_INDENT = " \\_ ".freeze
674
675
#
676
# Get user-friendly text for displaying the session that this entry would go through
677
#
678
def prettify_comm(comm, upstream_resolver)
679
if !Rex::Socket.is_ip_addr?(upstream_resolver.destination)
680
'N/A'
681
elsif comm.nil?
682
channel = Rex::Socket::SwitchBoard.best_comm(upstream_resolver.destination)
683
if channel.nil?
684
nil
685
else
686
"Session #{channel.sid} (route)"
687
end
688
else
689
if comm.alive?
690
"Session #{comm.sid}"
691
else
692
"Closed session (#{comm.sid})"
693
end
694
end
695
end
696
697
def print_dns_set(heading, result_set, ids: [])
698
return if result_set.length == 0
699
columns = ['#', 'Rule', 'Resolver', 'Comm channel']
700
col_props = { 'Rule' => { 'Strip' => false } }
701
702
tbl = Table.new(
703
Table::Style::Default,
704
'Header' => heading,
705
'Prefix' => "\n",
706
'Postfix' => "\n",
707
'Columns' => columns,
708
'ColProps' => col_props,
709
'SortIndex' => -1,
710
'WordWrap' => false
711
)
712
result_set.each_with_index do |entry, index|
713
tbl = append_resolver_cells!(tbl, entry, index: ids[index])
714
end
715
716
print(tbl.to_s) if tbl.rows.length > 0
717
end
718
719
def append_resolver_cells!(tbl, entry, prefix: [], suffix: [], index: nil)
720
alignment_prefix = prefix.empty? ? [] : (['.'] * prefix.length)
721
722
if entry.resolvers.length == 1
723
tbl << prefix + [index.to_s, entry.wildcard, entry.resolvers.first, prettify_comm(entry.comm, entry.resolvers.first)] + suffix
724
elsif entry.resolvers.length > 1
725
tbl << prefix + [index.to_s, entry.wildcard, '', ''] + suffix
726
entry.resolvers.each do |resolver|
727
tbl << alignment_prefix + ['.', TABLE_INDENT, resolver, prettify_comm(entry.comm, resolver)] + ([''] * suffix.length)
728
end
729
end
730
tbl
731
end
732
733
def resolver
734
self.driver.framework.dns_resolver
735
end
736
end
737
738
end
739
end
740
end
741
end
742
743