-
-
Save phracker/77ab27038172bce4b2b6f3670ac5f3b4 to your computer and use it in GitHub Desktop.
Given a list of trackers, this script will eliminate duplicates and unreachables.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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