-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrwf.py
executable file
·219 lines (200 loc) · 8.35 KB
/
rwf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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
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
161
162
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/usr/bin/env python
#-*- coding: utf-8 -*-
from time import time
from collections import deque
from threading import RLock, Thread
from random import Random
from httplib import HTTPConnection, responses
from argparse import ArgumentParser
class RWF:
def __init__(self, num_threads=30, conn_timeout=1,
good_statuses=responses.keys(), bad_statuses=[],
sec_limit=None, min_limit=None, hour_limit=None,
site_limit=None, addrs_limit=None):
self.num_threads = num_threads
self.conn_timeout = conn_timeout
self.status_codes = list_diff(good_statuses, bad_statuses)
self.start_time = None
self.limits = {}
self.limits["user"] = False
self.limits["time"] = {"sec": sec_limit, "min": min_limit,
"hour": hour_limit}
self.limits["time"]["total"] = self.time_limit()
self.limits["site"] = site_limit
self.limits["addrs"] = addrs_limit
self.num_addrs_tested = 0
self.num_addrs_tested_lock = RLock()
self.num_sites_found = 0
self.num_sites_found_lock = RLock()
self.threads = []
self.sites = deque()
def time_limit(self):
total = 0
multiples = {"sec": 1, "min": 60, "hour": 60 ** 2}
for key, value in self.limits["time"].iteritems():
if value is not None:
total += (value * multiples[key])
if total == 0:
return None
else:
return total
@classmethod
def from_cmdline(cls):
finder_parser = ArgumentParser(
description="rwf - a random website finder")
finder_parser.add_argument(
"-th", "--num-threads",
type=int, help="set the number of website finding threads")
finder_parser.add_argument(
"-ct", "--conn-timeout", type=int,
help=("set the maximum amount of seconds "
"to attempt to connect to addresses"))
finder_parser.add_argument(
"-gs", "--good-statuses", type=int, nargs="+",
help="http status codes that will cause a site to be added")
finder_parser.add_argument(
"-bs", "--bad-statuses", type=int, nargs="+",
help="http status codes that will not cause a site to be added")
finder_parser.add_argument(
"-sec", "--sec-limit", type=int,
help="set an amount of seconds to add to the runtime")
finder_parser.add_argument(
"-min", "--min-limit", type=int,
help="set an amount of minutes to add to the runtime")
finder_parser.add_argument(
"-hr", "--hour-limit", type=int,
help="set an amount of hours to add to the runtime")
finder_parser.add_argument(
"-e", "--site-limit", type=int,
help=("set an amount of sites, that when "
"found will cause finding to stop"))
finder_parser.add_argument(
"-a", "--addrs-limit", type=int,
help="set an amount of addresses tested to limit the runtime")
finder_parser.set_defaults(
num_threads=30, conn_timeout=1,
good_statuses=responses.keys(), bad_statuses=[])
args = finder_parser.parse_args()
if not (args.sec_limit or args.min_limit or args.hour_limit or
args.addrs_limit or args.site_limit):
finder_parser.error(
("At least one of --sec-limit, --min-limit, --hour-limit, \n"
"--addrs-limit, or site-limit arguments must be given"))
args_dict = vars(finder_parser.parse_args())
return cls(**args_dict)
def start_finding(self):
self.limits["user"] = False
self.start_time = time()
for thread in range(self.num_threads):
thread = Thread(target=self.find_sites)
thread.daemon=True
thread.start()
self.threads.append(thread)
def find_sites(self):
while not self.limited():
addrs = self.random_addrs()
if self.is_site(addrs):
with self.num_sites_found_lock:
self.num_sites_found += 1
self.sites.append(addrs)
def limited(self):
time_limited = self.limits["time"]["total"] is not None
site_limited = self.limits["site"] is not None
addrs_limited = self.limits["addrs"] is not None
with self.num_addrs_tested_lock:
at_addrs_tested_lim = self.num_addrs_tested >= self.limits["addrs"]
if ((time_limited and self.remaining_time() <= 0) or
(site_limited and self.num_sites_found >= self.limits["site"]) or
(addrs_limited and at_addrs_tested_lim) or
(self.limits["user"])):
return True
else:
return False
def remaining_time(self):
current_time = time()
running_time = current_time - self.start_time
remaining_time = self.limits["time"]["total"] - running_time
return remaining_time
def random_addrs(self):
addrs = []
total_range = xrange(0, 256)
# addresses not routed on the public internet
# http://en.wikipedia.org/wiki/Reserved_IP_addresses
# the key is the address element previously generated and
# the value is a range of values the next element can't be
# "all" refers to all element values in that location
options = {"all": [0, 10, 127] + range(224, 256),
100: {"all": xrange(64, 128)},
169: {"all": [254]},
172: {"all": xrange(16, 32)},
192: {"all": [168],
0: {"all": [2],
0: xrange(0, 8)}
},
198: {"all": [18, 20],
51: [100]
},
203: {"all": [],
0: [113]
}
}
self.use_options(options, addrs, total_range)
return "{}.{}.{}.{}".format(*addrs)
def use_options(self, location, addrs, total_range):
rand = Random()
if len(addrs) < 4:
# get list of the next part of the addrs that is not routed
# based on the location(addrs part number and previous addrses)
if type(location) == dict:
not_routed = location["all"]
elif type(location) == list:
not_routed = location
# perform set difference on the list and total_range to
# get an addrs element, then append
addrs.append(rand.choice(list_diff(total_range, not_routed)))
# if location is a dict, change location to the value for the
# key in the dict that equals the last element of addrs
if type(location) == dict:
if addrs[-1] in location.keys():
location = location[addrs[-1]]
# if location is a list, there are no more nested dicts, so choose
# next addrs element from whole range
elif type(location) == list:
addrs.append(rand.choice(total_range))
# calls use_options again until there are four elements
self.use_options(location, addrs, total_range)
# four elements, addrs completed
else:
pass
def is_site(self, addrs):
with self.num_addrs_tested_lock:
self.num_addrs_tested += 1
try:
conn = HTTPConnection(addrs, timeout=self.conn_timeout)
conn.request("GET", "/")
status = conn.getresponse().status
if status in self.status_codes:
return True
else:
return False
except:
return False
def finding(self):
for thread in self.threads:
if thread.isAlive():
return True
return False
def stop_finding(self):
self.limits["user"] = True
def list_diff(minuend, subtrahend):
return list(set(minuend) - set(subtrahend))
def statuses_starting_with(num):
return filter(lambda status: str(status)[0] == str(num), responses.keys())
def main():
finder = RWF.from_cmdline()
finder.start_finding()
while finder.finding():
if len(finder.sites) > 0:
print finder.sites.pop()
if __name__ == "__main__":
main()