Class: Wmap::HostTracker
- Inherits:
-
Object
- Object
- Wmap::HostTracker
- Includes:
- Singleton, Utils
- Defined in:
- lib/wmap/host_tracker.rb,
lib/wmap/host_tracker/primary_host.rb
Overview
Class to handle the local host data repository file where lists of known hosts from discovery and past assessment efforts are stored
Direct Known Subclasses
Defined Under Namespace
Classes: PrimaryHost
Constant Summary
Constants included from Utils::UrlMagic
Utils::UrlMagic::Max_http_timeout, Utils::UrlMagic::User_agent
Constants included from Utils::DomainRoot
Utils::DomainRoot::File_ccsld, Utils::DomainRoot::File_cctld, Utils::DomainRoot::File_gtld, Utils::DomainRoot::File_tld
Instance Attribute Summary collapse
-
#alias ⇒ Object
readonly
Returns the value of attribute alias.
-
#data_dir ⇒ Object
Returns the value of attribute data_dir.
-
#hosts_file ⇒ Object
Returns the value of attribute hosts_file.
-
#known_hosts ⇒ Object
Returns the value of attribute known_hosts.
-
#max_parallel ⇒ Object
Returns the value of attribute max_parallel.
-
#verbose ⇒ Object
Returns the value of attribute verbose.
Instance Method Summary collapse
-
#add(host) ⇒ Object
Setter to add host entry to the cache once at a time.
-
#bulk_add(list, num = @max_parallel) ⇒ Object
(also: #adds)
Setter to add host entry to the local hosts in batch (from an array).
-
#bulk_delete(list) ⇒ Object
(also: #dels)
‘setter’ to delete host entry to the cache in batch (from an array).
-
#count ⇒ Object
Count numbers of entries in the local host repository.
-
#delete(host) ⇒ Object
‘setter’ to remove entry from the local hosts one at a time.
-
#dump_sub_domains ⇒ Object
(also: #get_sub_domains)
Extract a list of sub-domains from the local host repository @known_hosts.
-
#file_add(file) ⇒ Object
‘setter’ to add host entry to the local hosts in batch (from a file).
-
#file_delete(file) ⇒ Object
Setter to delete host entries in the local hosts in batch (from a file).
-
#get_a_records ⇒ Object
(also: #dump_a_records)
Extract hostname without the root domain part from the @known_hosts.
-
#get_root_domains ⇒ Object
(also: #dump_root_domains)
Extract known root domains from the local host repository @known_hosts.
-
#host_aliases(host) ⇒ Object
(also: #aliases)
Search local host repository and return a list of aliases for the host.
-
#host_known?(host) ⇒ Boolean
(also: #is_known?)
Check if the specific host within @known_hosts table.
-
#initialize(params = {}) ⇒ HostTracker
constructor
Instance default variables.
-
#ip_known?(ip) ⇒ Boolean
(also: #has_a_record?)
Check if the specific IP within @known_hosts table.
-
#is_trusted?(host) ⇒ Boolean
determine if host is part of trusted (known) root domains.
-
#load_known_hosts_from_file(f_hosts = @hosts_file) ⇒ Object
(also: #load)
Setter to load the known hosts from the local hosts file into a class instance.
-
#local_host_2_ip(host) ⇒ Object
Perform DNS lookup on the local host repository.
-
#local_ip_2_host(ip) ⇒ Object
Perform reverse DNS lookup on the local host repository.
-
#print_host(host) ⇒ Object
(also: #print)
Print summary report on the cache.
-
#print_known_hosts ⇒ Object
(also: #print_all)
Print summary report on the cache.
-
#refresh(host) ⇒ Object
Setter to refresh the entry from the cache one at a time.
-
#refresh_all ⇒ Object
Refresh all the entries in the local hosts by querying the Internet.
-
#save_known_hosts_to_file!(f_hosts = @hosts_file) ⇒ Object
(also: #save!)
Save the current local hosts hash table into a (random) data repository file.
-
#search(pattern) ⇒ Object
(also: #find)
Search potential matching sites from the host store by using simple regular expression.
-
#sub_domain_known?(domain) ⇒ Boolean
Based on the current host store, to determine if an entry is a known sub-domain.
-
#top_hostname(num) ⇒ Object
Top hostname - sort out most common host-name in the host store in descendant order.
Methods included from Utils
#cidr_2_ips, #file_2_hash, #file_2_list, #get_nameserver, #get_nameservers, #host_2_ip, #host_2_ips, #is_cidr?, #is_fqdn?, #is_ip?, #list_2_file, #reverse_dns_lookup, #sort_ips, #valid_dns_record?, #zone_transferable?
Methods included from Utils::Logger
Methods included from Utils::UrlMagic
#create_absolute_url_from_base, #create_absolute_url_from_context, #host_2_url, #is_site?, #is_ssl?, #is_url?, #landing_location, #make_absolute, #normalize_url, #open_page, #redirect_location, #response_code, #response_headers, #url_2_host, #url_2_path, #url_2_port, #url_2_site, #urls_on_same_domain?
Methods included from Utils::DomainRoot
#get_domain_root, #get_domain_root_by_ccsld, #get_domain_root_by_cctld, #get_domain_root_by_tlds, #get_sub_domain, #is_domain_root?, #print_ccsld, #print_cctld, #print_gtld
Constructor Details
#initialize(params = {}) ⇒ HostTracker
Instance default variables
21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/wmap/host_tracker.rb', line 21 def initialize (params = {}) @verbose=params.fetch(:verbose, false) @data_dir=params.fetch(:data_dir, File.dirname(__FILE__)+'/../../data/') Dir.mkdir(@data_dir) unless Dir.exist?(@data_dir) # Set default instance variables @hosts_file=params.fetch(:hosts_file, @data_dir + 'hosts') @max_parallel=params.fetch(:max_parallel, 40) # Initialize the instance variables File.new(@hosts_file, "w") unless File.exist?(@hosts_file) @known_hosts=load_known_hosts_from_file(@hosts_file) end |
Instance Attribute Details
#alias ⇒ Object (readonly)
Returns the value of attribute alias.
18 19 20 |
# File 'lib/wmap/host_tracker.rb', line 18 def alias @alias end |
#data_dir ⇒ Object
Returns the value of attribute data_dir.
17 18 19 |
# File 'lib/wmap/host_tracker.rb', line 17 def data_dir @data_dir end |
#hosts_file ⇒ Object
Returns the value of attribute hosts_file.
17 18 19 |
# File 'lib/wmap/host_tracker.rb', line 17 def hosts_file @hosts_file end |
#known_hosts ⇒ Object
Returns the value of attribute known_hosts.
17 18 19 |
# File 'lib/wmap/host_tracker.rb', line 17 def known_hosts @known_hosts end |
#max_parallel ⇒ Object
Returns the value of attribute max_parallel.
17 18 19 |
# File 'lib/wmap/host_tracker.rb', line 17 def max_parallel @max_parallel end |
#verbose ⇒ Object
Returns the value of attribute verbose.
17 18 19 |
# File 'lib/wmap/host_tracker.rb', line 17 def verbose @verbose end |
Instance Method Details
#add(host) ⇒ Object
Setter to add host entry to the cache once at a time
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/wmap/host_tracker.rb', line 119 def add(host) puts "Add entry to the local host repository: #{host}" host=host.strip.downcase unless host.nil? root=get_domain_root(host) unless @known_hosts.key?(host) ip=host_2_ip(host) record=Hash.new if is_ip?(ip) # filter host to known domains only if is_trusted?(host) record[host]=ip record[ip]=host puts "Host data repository entry loaded: #{host} <=> #{ip}" # Replace instance with the class variable to avoid potential race condition under parallel engine # add additional logic to update the sub-domain table as well, 02/10/2014 sub=get_sub_domain(host) if sub!=nil tracker=Wmap::DomainTracker::SubDomain.instance tracker.data_dir=@data_dir tracker.sub_domains_file = tracker.data_dir + "sub_domains" tracker.known_internet_sub_domains=tracker.load_domains_from_file(tracker.sub_domains_file) unless tracker.domain_known?(sub) tracker.add(sub) tracker.save! end tracker=nil end @known_hosts.merge!(record) return record else domain_tracker=nil puts "Error - host #{host} has an untrusted internet root domain: #{root}\nPlease update the trusted domain seeds file first if necessary." end else puts "Problem resolve host #{host} - unknown IP: #{ip}" end else puts "Host is already exist. Skip: #{host}" end #rescue => ee # puts "Exception on method #{__method__}: #{ee}" if @verbose end |
#bulk_add(list, num = @max_parallel) ⇒ Object Also known as: adds
Setter to add host entry to the local hosts in batch (from an array)
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/wmap/host_tracker.rb', line 163 def bulk_add(list, num=@max_parallel) puts "Add entries to the local host repository: #{list}" results=Hash.new if list.size > 0 puts "Start parallel host update processing on:\n #{list}" if @verbose Parallel.map(list, :in_processes => num) { |target| add(target) }.each do |process| if process.nil? next elsif process.empty? #do nothing else results.merge!(process) end end @known_hosts.merge!(results) puts "Done loading host entries." return results else puts "Error: empty list - no entry is loaded. Please check your input list and try again." end return results rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#bulk_delete(list) ⇒ Object Also known as: dels
‘setter’ to delete host entry to the cache in batch (from an array)
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/wmap/host_tracker.rb', line 218 def bulk_delete(list) puts "Delete entries to the local host repository from:\n #{list}" hosts=list changes=Array.new if hosts.size > 0 hosts.map do |x| host=delete(x) changes.push(host) unless host.nil? end puts "Done deleting hosts." return changes else puts "Error: empty list - no entry is loaded. Please check your list and try again." end rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#count ⇒ Object
Count numbers of entries in the local host repository
86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/wmap/host_tracker.rb', line 86 def count puts "Counting number of entries in the local host repository ..." cnt=0 @known_hosts.keys.map do |key| unless is_ip?(key) cnt=cnt+1 end end puts "Current number of entries: #{cnt}" return cnt rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#delete(host) ⇒ Object
‘setter’ to remove entry from the local hosts one at a time
203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/wmap/host_tracker.rb', line 203 def delete(host) puts "Remove entry from the local host repository: #{host} " host=host.strip.downcase if @known_hosts.key?(host) @known_hosts.delete(host) puts "Entry cleared." return host else puts "Entry not fund. Skip: #{host}" end rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#dump_sub_domains ⇒ Object Also known as: get_sub_domains
Extract a list of sub-domains from the local host repository @known_hosts
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/wmap/host_tracker.rb', line 417 def dump_sub_domains puts "Dump out all active sub domains from the local hosts." if @verbose subs=Array.new @known_hosts.keys.each do |hostname| next if is_ip?(hostname) hostname = hostname.strip sub = get_subdomain(hostname) subs.push(sub) unless sub.nil? end subs.uniq!.sort! unless subs.empty? puts "Found sub domains: #{subs}" if @verbose return subs rescue Exception => ee puts "Exception on method #{__method__}: #{ee}" return subs end |
#file_add(file) ⇒ Object
‘setter’ to add host entry to the local hosts in batch (from a file)
192 193 194 195 196 197 198 199 200 |
# File 'lib/wmap/host_tracker.rb', line 192 def file_add(file) puts "Add entries to the local host repository from file: #{file}" raise "File non-exist. Please check your file path and name again: #{file}" unless File.exist?(file) hosts=file_2_list(file) changes=bulk_add(hosts) return changes rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#file_delete(file) ⇒ Object
Setter to delete host entries in the local hosts in batch (from a file)
238 239 240 241 242 243 244 245 246 247 |
# File 'lib/wmap/host_tracker.rb', line 238 def file_delete(file) puts "Delete the local host repository entries from file: #{file}" raise "File non-exist. Please check your file path and name again: #{file}" unless File.exist?(file) hosts=file_2_list(file) changes=bulk_delete(hosts) puts "Delete done." return changes rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#get_a_records ⇒ Object Also known as: dump_a_records
Extract hostname without the root domain part from the @known_hosts. Data can be used for statistics study.
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/wmap/host_tracker.rb', line 318 def get_a_records puts "Dump out all known A records from the local hosts." records=Array.new (@known_hosts.keys-["",nil]).map do |hostname| next if is_ip?(hostname) hostname = hostname.strip root = get_domain_root(hostname) record = hostname.sub('.'+root,'') records.push(record) unless record.nil? end records.sort! return records rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#get_root_domains ⇒ Object Also known as: dump_root_domains
Extract known root domains from the local host repository @known_hosts
301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
# File 'lib/wmap/host_tracker.rb', line 301 def get_root_domains puts "Dump out all active root domains from the cache." zones=Array.new (@known_hosts.keys-["",nil]).map do |hostname| next if is_ip?(hostname) hostname = hostname.strip zone = get_domain_root(hostname) zones.push(zone) unless zone.nil? end zones.uniq!.sort! return zones rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#host_aliases(host) ⇒ Object Also known as: aliases
Search local host repository and return a list of aliases for the host
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/wmap/host_tracker.rb', line 463 def host_aliases (host) puts "Search aliases in the local hosts data repository for host: #{host}" if @verbose host.strip! raise "Unknown method input: #{host} We expect a FQDN host-name string from you. " unless is_fqdn?(host) aliases=Array.new if @known_hosts.key?(host) ip=local_host_2_ip(host) @known_hosts.keys.map do |key| my_ip=local_host_2_ip(key) if ip == my_ip aliases.push(key) end end else raise "Unknown host-name in the local hosts data repository: #{host}" end return aliases-[host] rescue Exception => ee puts "Exception on method #{__method__}: #{ee}" return nil end |
#host_known?(host) ⇒ Boolean Also known as: is_known?
Check if the specific host within @known_hosts table
376 377 378 379 380 381 382 383 384 385 |
# File 'lib/wmap/host_tracker.rb', line 376 def host_known? (host) host=host.strip.downcase unless host.nil? return false if @known_hosts==nil return @known_hosts.key?(host.strip) rescue => ee if @verbose puts "Host Lookup Error: #{ee}" end return false end |
#ip_known?(ip) ⇒ Boolean Also known as: has_a_record?
Check if the specific IP within @known_hosts table
364 365 366 367 368 369 370 371 372 |
# File 'lib/wmap/host_tracker.rb', line 364 def ip_known? (ip) known = false ip=ip.strip unless ip.nil? return false if @known_hosts==nil return @known_hosts.key?(ip.strip) rescue => ee puts "IP Lookup Error: #{ee}" if @verbose return false end |
#is_trusted?(host) ⇒ Boolean
determine if host is part of trusted (known) root domains
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/wmap/host_tracker.rb', line 101 def is_trusted?(host) puts "Determin if host #{host} is part of trusted root domains" if @verbose root=get_domain_root(host) puts "Domain root: #{root}" if @verbose domain_tracker=Wmap::DomainTracker.instance domain_tracker.data_dir=@data_dir domain_tracker.domains_file = domain_tracker.data_dir + "/" + "domains" domain_tracker.load_domains_from_file if domain_tracker.domain_known?(root) domain_tracker=nil return true else domain_tracker=nil return false end end |
#load_known_hosts_from_file(f_hosts = @hosts_file) ⇒ Object Also known as: load
Setter to load the known hosts from the local hosts file into a class instance
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/wmap/host_tracker.rb', line 34 def load_known_hosts_from_file (f_hosts=@hosts_file) puts "Loading local hosts from file: #{f_hosts} ..." if @verbose known_hosts=Hash.new @alias = Hash.new File.new(f_hosts, "w") unless File.exist?(f_hosts) f=File.open(f_hosts, 'r') f.each do |line| next unless line =~ /\d+\.\d+\.\d+\.\d+/ entry=line.chomp.split(%r{\t+|\s+|\,}) key=entry[0].downcase value=entry[1] puts "Loading key value pair: #{key} - #{value}" if @verbose known_hosts[key] = Hash.new unless known_hosts.key?(key) known_hosts[key]= value # For reverse host lookup known_hosts[value] = Hash.new unless known_hosts.key?(value) known_hosts[value] = key # Count the number of alias for the recorded IP if @alias.key?(value) @alias[value]+=1 else @alias[value]=1 end end f.close return known_hosts #rescue => ee # puts "Exception on method #{__method__}: #{ee}" # return known_hosts end |
#local_host_2_ip(host) ⇒ Object
Perform DNS lookup on the local host repository. Not to confuse with the DNS lookup from the Internet
403 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/wmap/host_tracker.rb', line 403 def local_host_2_ip (host) puts "DNS lookup from the local host repository" if @verbose host=host.strip unless host.nil? if @known_hosts.key?(host) return @known_hosts[host] else return nil end rescue => ee puts "Exception on method #{__method__}: #{ee}" return nil end |
#local_ip_2_host(ip) ⇒ Object
Perform reverse DNS lookup on the local host repository. Not to confuse with the reverse DNS lookup from the Internet
389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/wmap/host_tracker.rb', line 389 def local_ip_2_host (ip) puts "Reverse DNS lookup from the local host repository" if @verbose ip=ip.strip unless ip.nil? if @known_hosts.key?(ip) return @known_hosts[ip] else return nil end rescue => ee puts "Exception on method #{__method__}: #{ee}" return nil end |
#print_host(host) ⇒ Object Also known as: print
Print summary report on the cache
348 349 350 351 352 353 354 355 356 357 358 359 360 |
# File 'lib/wmap/host_tracker.rb', line 348 def print_host(host) puts "Local host store entry for #{host}" host.strip! raise "Invalid input: #{host}" unless is_fqdn?(host) if @known_hosts.key?(host) value=@known_hosts[host] puts "#{host}\t#{value}" else puts "Unknown host in the local store: #{host}" end rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#print_known_hosts ⇒ Object Also known as: print_all
Print summary report on the cache
336 337 338 339 340 341 342 343 344 |
# File 'lib/wmap/host_tracker.rb', line 336 def print_known_hosts puts "\nSummary of local hosts Table:" puts "Total entries: #{@known_hosts.size}" (@known_hosts.keys.sort-["",nil]).each do |key| value=@known_hosts[key] puts "#{key}\t#{value}" if is_fqdn?(key) end puts "End of the summary" end |
#refresh(host) ⇒ Object
Setter to refresh the entry from the cache one at a time
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/wmap/host_tracker.rb', line 250 def refresh(host) puts "Refresh the local host repository for host: #{host} " host=host.strip.downcase if @known_hosts.key?(host) old_ip=@known_hosts[host] new_ip=host_2_ip(host) if is_ip?(new_ip) if old_ip==new_ip puts "No change for the host entry: #{host}\t#{old_ip}" return nil else @known_hosts[host]=new_ip @known_hosts[new_ip]=host puts "Entry refreshed: #{host}\t#{@known_hosts[host]}" return host end else puts "Host can no longer be resolved in the Internet. Entry removed: #{host}\t#{@known_hosts[host]}" @known_hosts.delete(host) return host end else puts "Error entry non exist: #{host}" end rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#refresh_all ⇒ Object
Refresh all the entries in the local hosts by querying the Internet
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/wmap/host_tracker.rb', line 279 def refresh_all puts "Refresh all the entries in the local host repository in one shot." changes=Hash.new hosts=@known_hosts.keys @known_hosts=Hash.new changes=bulk_add(hosts) @known_hosts.merge!(changes) #@known_hosts.keys.map do |key| # unless is_ip?(key) # host=refresh(key) # changes.push(host) unless host.nil? # end #end puts "\n#{changes.size} Entries Refreshed:" if changes.size>0 #changes.map { |x| puts x } puts "Done refreshing the local hosts." return changes rescue => ee puts "Exception on method #{__method__}: #{ee}" end |
#save_known_hosts_to_file!(f_hosts = @hosts_file) ⇒ Object Also known as: save!
Save the current local hosts hash table into a (random) data repository file
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/wmap/host_tracker.rb', line 67 def save_known_hosts_to_file!(f_hosts=@hosts_file) puts "Saving the local host repository from memory to file: #{f_hosts} ..." =Time.now f=File.open(f_hosts, 'w') f.write "# local hosts file created by the #{self.class} class #{__method__} method at: #{}" @known_hosts.keys.sort.map do |key| unless key =~ /\d+\.\d+\.\d+\.\d+/ f.write "\n#{key}\t#{@known_hosts[key]}" end end f.write "\n" f.close puts "local host repository is successfully saved to: #{f_hosts}" #rescue => ee # puts "Exception on method #{__method__}: #{ee}" end |
#search(pattern) ⇒ Object Also known as: find
Search potential matching sites from the host store by using simple regular expression. Note that any upper-case char in the search string will be automatically converted into lower case
446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
# File 'lib/wmap/host_tracker.rb', line 446 def search (pattern) puts "Search host store based on the regular expression: #{pattern}" if @verbose pattern=pattern.strip.downcase results=Array.new @known_hosts.keys.map do |key| if key =~ /#{pattern}/i results.push(key) end end return results rescue Exception => ee puts "Exception on method #{__method__}: #{ee}" return nil end |
#sub_domain_known?(domain) ⇒ Boolean
Based on the current host store, to determine if an entry is a known sub-domain
436 437 438 439 440 441 442 443 |
# File 'lib/wmap/host_tracker.rb', line 436 def sub_domain_known?(domain) puts "Validate sub-domain: #{domain}" if @verbose domain=domain.strip.downcase subs=dump_sub_domains return subs.include?(domain) rescue Exception => ee puts "Exception on method #{__method__}: #{ee}" end |
#top_hostname(num) ⇒ Object
Top hostname - sort out most common host-name in the host store in descendant order
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 |
# File 'lib/wmap/host_tracker.rb', line 487 def top_hostname (num) puts "Sort the host store for the most common hostname. " if @verbose h=Hash.new host_store=Hash.new top=Array.new # Build a host table from the host file f=File.open(@hosts_file, 'r') f.each do |line| next unless line =~ /\d+\.\d+\.\d+\.\d+/ # skip the domain roots in the host list next if is_domain_root?(line.chomp) entry=line.chomp.split(%r{\t+|\s+|\,}) key=entry[0].downcase value=entry[1] puts "Loading key value pair: #{key} - #{value}" if @verbose host_store[key] = Hash.new unless known_hosts.key?(key) host_store[key]= value end f.close host_store.keys.map do |key| host=key.split('.') if h.key?(host[0]) h[host[0]]+=1 else h[host[0]]=1 end end result = h.keys.sort { |a,b| h[b] <=> h[a] } # Sort by value descendantly num = result.size if result.size < num for i in 0...num top.push(result[i]) end return top rescue Exception => ee puts "Exception on method #{__method__}: #{ee}" return nil end |