Skip to content

Instantly share code, notes, and snippets.

@phracker
Forked from andresperezl/trackers-resolvs.rb
Last active October 21, 2024 20:26
Show Gist options
  • Save phracker/77ab27038172bce4b2b6f3670ac5f3b4 to your computer and use it in GitHub Desktop.
Save phracker/77ab27038172bce4b2b6f3670ac5f3b4 to your computer and use it in GitHub Desktop.
Given a list of trackers, this script will eliminate duplicates and unreachables.
#!/usr/bin/env ruby
require 'uri'
require 'resolv'
require 'optparse'
require 'net/http'
require 'socket'
require 'uri'
require 'timeout'
# progress bar snippet
class ProgressBar
def initialize(units=60)
@units = units.to_f
end
def print(completed, total)
norm = 1.0 / (total / @units)
progress = (completed * norm).ceil
pending = @units - progress
Kernel.print "[#{'=' * progress }#{' ' * ( pending )}] #{percentage(completed, total)}%\r"
end
def percentage(completed, total)
( ( completed / total.to_f ) * 100 ).round
end
end
# build option parser
options = {}
optparse = OptionParser.new do |opts|
opts.banner = "Usage: opentracker-tester.rb -l <LIST> [ -o <OUTPUT> ]"
opts.set_summary_indent " "
opts.set_summary_width 24
opts.on("-l", "--list=LIST", String, "File containing list of tracker uris.") do |val|
options[:list] = val || nil
end
opts.on("-o", "--output=OUTPUT", String, "Write working trackers to this file.") do |val|
options[:output] = val || nil
end
end
# parse options and raise exceptions as necessary
begin
optparse.parse!
# specify mandatory options
mandatory = [:list]
missing = mandatory.select{ |param| options[param].nil? }
# raise exception if necessary args are missing
raise OptionParser::MissingArgument, missing.join(", ") unless missing.empty?
rescue OptionParser::MissingArgument => e
puts "Missing required argument(s): #{e.args.join(", ")}"
puts optparse
exit
end
# read tracker list, split by line
text = File.open(options[:list]).read.split(/\n|(\r\n)/)
hosts = text.map{ |uri| [URI(uri).host, uri] }.to_h
resolvs = {}
hosts.each do |h, uri|
if /\d+\.\d+\.\d+\.\d+/.match(h)
resolvs[h] = h
else
begin
ip = Resolv.getaddress h
points = resolvs[ip]
if points.nil? || /\d+\.\d+\.\d+\.\d+/.match(points)
resolvs[ip] = h
end
rescue
# Could not resolve address
end
end
end
schemes = ["udp", "http", "https"]
ips = { "udp" => [], "http" => [], "https" => [] }
groups = {}
total_trackers = 0
resolvs.each do |ip, h|
scheme = URI(hosts[h]).scheme
if ip == h
ips[scheme] << hosts[h]
total_trackers += 1
else
domain = h.split(/\./)
domain = domain.size > 2 ? domain[1] : domain[0]
if groups[domain].nil?
groups[domain] = { "udp" => [], "http" => [], "https" => [] }
end
groups[domain][scheme] << hosts[h]
total_trackers += 1
end
end
bar = ProgressBar.new
# create empty array for text output
output_text = Array.new
current_tracker = 0
groups.each do |group, uris|
schemes.each do |scheme|
uris[scheme].each do |uri|
current_tracker += 1
bar.print(current_tracker,total_trackers)
# parse each uri into components
u = URI.parse(uri)
begin
timeout(15) do
case u.scheme
# UDP Test
when "udp"
udp_sock = UDPSocket.new
host = u.host.to_s
port = u.port.to_i
path = u.path.to_s
udp_r = udp_sock.send "announce", 0, host, port
# puts "UDP: " << udp_r.to_s
output_text.push(uri)
# HTTP(S) Test
when "http", "https"
h = Net::HTTP.new(u.host, u.port)
h.read_timeout = 15
http_r = h.request_get(u.path)
if http_r
# puts "HTTP: " << http_r.to_s
output_text.push(uri)
end
end
end
# Handle exceptions
rescue Errno::ECONNREFUSED
# puts "Error: Connection refused. " << e.inspect
rescue Errno::ETIMEDOUT
# puts "Error: Timeout. " << e.inspect
rescue Exception
# puts "Error: " << e.inspect
rescue Timeout::Error => e
# puts "Timeout: " << e.inspect
end
end
end
end
schemes.each do |scheme|
ips[scheme].each do |ip|
# parse each uri into components
u = URI.parse(ip)
begin
timeout(15) do
case u.scheme
# UDP Test
when "udp"
udp_sock = UDPSocket.new
host = u.host.to_s
port = u.port.to_i
path = u.path.to_s
udp_r = udp_sock.send "announce", 0, host, port
# puts "UDP: " << udp_r.to_s
output_text.push(ip)
# HTTP(S) Test
when "http", "https"
h = Net::HTTP.new(u.host, u.port)
h.read_timeout = 10
http_r = h.request_get(u.path)
if http_r
# puts "HTTP: " << http_r.to_s
output_text.push(ip)
end
end
end
# Handle exceptions
rescue Errno::ECONNREFUSED
# puts "Error: Connection refused. " << e.inspect
rescue Errno::ETIMEDOUT
# puts "Error: Timeout. " << e.inspect
rescue Exception
# puts "Error: " << e.inspect
rescue Timeout::Error => e
# puts "Timeout: " << e.inspect
end
end
end
# sort output array
output_text.sort!
puts ""
# print output to file if specified, otherwise print to stdout
if options[:output_file]
output = File.open(options[:output_file], "w+")
output_text.each { |line| output.puts line }
else
output_text.each { |line| puts line }
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment