oneforall-kjl 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. OneForAll/__init__.py +15 -0
  2. OneForAll/brute.py +503 -0
  3. OneForAll/common/check.py +41 -0
  4. OneForAll/common/crawl.py +10 -0
  5. OneForAll/common/database.py +277 -0
  6. OneForAll/common/domain.py +63 -0
  7. OneForAll/common/ipasn.py +42 -0
  8. OneForAll/common/ipreg.py +139 -0
  9. OneForAll/common/lookup.py +28 -0
  10. OneForAll/common/module.py +369 -0
  11. OneForAll/common/query.py +9 -0
  12. OneForAll/common/records.py +363 -0
  13. OneForAll/common/request.py +264 -0
  14. OneForAll/common/resolve.py +173 -0
  15. OneForAll/common/search.py +78 -0
  16. OneForAll/common/similarity.py +138 -0
  17. OneForAll/common/tablib/__init__.py +0 -0
  18. OneForAll/common/tablib/format.py +89 -0
  19. OneForAll/common/tablib/tablib.py +360 -0
  20. OneForAll/common/tldextract.py +240 -0
  21. OneForAll/common/utils.py +789 -0
  22. OneForAll/config/__init__.py +17 -0
  23. OneForAll/config/api.py +94 -0
  24. OneForAll/config/default.py +255 -0
  25. OneForAll/config/log.py +38 -0
  26. OneForAll/config/setting.py +108 -0
  27. OneForAll/export.py +72 -0
  28. OneForAll/modules/altdns.py +216 -0
  29. OneForAll/modules/autotake/github.py +105 -0
  30. OneForAll/modules/certificates/censys_api.py +73 -0
  31. OneForAll/modules/certificates/certspotter.py +48 -0
  32. OneForAll/modules/certificates/crtsh.py +84 -0
  33. OneForAll/modules/certificates/google.py +48 -0
  34. OneForAll/modules/certificates/myssl.py +46 -0
  35. OneForAll/modules/certificates/racent.py +49 -0
  36. OneForAll/modules/check/axfr.py +97 -0
  37. OneForAll/modules/check/cdx.py +44 -0
  38. OneForAll/modules/check/cert.py +58 -0
  39. OneForAll/modules/check/csp.py +94 -0
  40. OneForAll/modules/check/nsec.py +58 -0
  41. OneForAll/modules/check/robots.py +44 -0
  42. OneForAll/modules/check/sitemap.py +44 -0
  43. OneForAll/modules/collect.py +70 -0
  44. OneForAll/modules/crawl/archivecrawl.py +59 -0
  45. OneForAll/modules/crawl/commoncrawl.py +59 -0
  46. OneForAll/modules/datasets/anubis.py +45 -0
  47. OneForAll/modules/datasets/bevigil.py +50 -0
  48. OneForAll/modules/datasets/binaryedge_api.py +50 -0
  49. OneForAll/modules/datasets/cebaidu.py +45 -0
  50. OneForAll/modules/datasets/chinaz.py +45 -0
  51. OneForAll/modules/datasets/chinaz_api.py +49 -0
  52. OneForAll/modules/datasets/circl_api.py +49 -0
  53. OneForAll/modules/datasets/cloudflare_api.py +130 -0
  54. OneForAll/modules/datasets/dnsdb_api.py +51 -0
  55. OneForAll/modules/datasets/dnsdumpster.py +52 -0
  56. OneForAll/modules/datasets/dnsgrep.py +44 -0
  57. OneForAll/modules/datasets/fullhunt.py +48 -0
  58. OneForAll/modules/datasets/hackertarget.py +45 -0
  59. OneForAll/modules/datasets/ip138.py +45 -0
  60. OneForAll/modules/datasets/ipv4info_api.py +73 -0
  61. OneForAll/modules/datasets/netcraft.py +66 -0
  62. OneForAll/modules/datasets/passivedns_api.py +51 -0
  63. OneForAll/modules/datasets/qianxun.py +61 -0
  64. OneForAll/modules/datasets/rapiddns.py +45 -0
  65. OneForAll/modules/datasets/riddler.py +45 -0
  66. OneForAll/modules/datasets/robtex.py +58 -0
  67. OneForAll/modules/datasets/securitytrails_api.py +56 -0
  68. OneForAll/modules/datasets/sitedossier.py +57 -0
  69. OneForAll/modules/datasets/spyse_api.py +62 -0
  70. OneForAll/modules/datasets/sublist3r.py +45 -0
  71. OneForAll/modules/datasets/urlscan.py +45 -0
  72. OneForAll/modules/datasets/windvane.py +92 -0
  73. OneForAll/modules/dnsquery/mx.py +35 -0
  74. OneForAll/modules/dnsquery/ns.py +35 -0
  75. OneForAll/modules/dnsquery/soa.py +35 -0
  76. OneForAll/modules/dnsquery/spf.py +35 -0
  77. OneForAll/modules/dnsquery/txt.py +35 -0
  78. OneForAll/modules/enrich.py +72 -0
  79. OneForAll/modules/finder.py +206 -0
  80. OneForAll/modules/intelligence/alienvault.py +50 -0
  81. OneForAll/modules/intelligence/riskiq_api.py +58 -0
  82. OneForAll/modules/intelligence/threatbook_api.py +50 -0
  83. OneForAll/modules/intelligence/threatminer.py +45 -0
  84. OneForAll/modules/intelligence/virustotal.py +60 -0
  85. OneForAll/modules/intelligence/virustotal_api.py +59 -0
  86. OneForAll/modules/iscdn.py +86 -0
  87. OneForAll/modules/search/ask.py +69 -0
  88. OneForAll/modules/search/baidu.py +96 -0
  89. OneForAll/modules/search/bing.py +79 -0
  90. OneForAll/modules/search/bing_api.py +78 -0
  91. OneForAll/modules/search/fofa_api.py +74 -0
  92. OneForAll/modules/search/gitee.py +71 -0
  93. OneForAll/modules/search/github_api.py +86 -0
  94. OneForAll/modules/search/google.py +83 -0
  95. OneForAll/modules/search/google_api.py +77 -0
  96. OneForAll/modules/search/hunter_api.py +72 -0
  97. OneForAll/modules/search/quake_api.py +72 -0
  98. OneForAll/modules/search/shodan_api.py +53 -0
  99. OneForAll/modules/search/so.py +75 -0
  100. OneForAll/modules/search/sogou.py +72 -0
  101. OneForAll/modules/search/wzsearch.py +68 -0
  102. OneForAll/modules/search/yahoo.py +81 -0
  103. OneForAll/modules/search/yandex.py +80 -0
  104. OneForAll/modules/search/zoomeye_api.py +73 -0
  105. OneForAll/modules/srv.py +75 -0
  106. OneForAll/modules/wildcard.py +319 -0
  107. OneForAll/oneforall.py +275 -0
  108. OneForAll/takeover.py +168 -0
  109. OneForAll/test.py +23 -0
  110. oneforall_kjl-0.1.1.dist-info/METADATA +18 -0
  111. oneforall_kjl-0.1.1.dist-info/RECORD +114 -0
  112. oneforall_kjl-0.1.1.dist-info/WHEEL +5 -0
  113. oneforall_kjl-0.1.1.dist-info/entry_points.txt +2 -0
  114. oneforall_kjl-0.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,363 @@
1
+ import os
2
+ from collections import OrderedDict
3
+ from inspect import isclass
4
+
5
+ from sqlalchemy import create_engine, exc, inspect, text
6
+
7
+ from .tablib import tablib
8
+
9
+ DATABASE_URL = os.environ.get('DATABASE_URL')
10
+
11
+
12
+ def is_exception(obj):
13
+ """Given an object, return a boolean indicating whether it is an instance
14
+ or subclass of :py:class:`Exception`.
15
+ """
16
+ if isinstance(obj, Exception):
17
+ return True
18
+ if isclass(obj) and issubclass(obj, Exception):
19
+ return True
20
+ return False
21
+
22
+
23
+ class Record(object):
24
+ """A row, from a query, from a database."""
25
+ __slots__ = ('_keys', '_values')
26
+
27
+ def __init__(self, keys, values):
28
+ self._keys = keys
29
+ self._values = values
30
+
31
+ # Ensure that lengths match properly.
32
+ assert len(self._keys) == len(self._values)
33
+
34
+ def keys(self):
35
+ """Returns the list of column names from the query."""
36
+ return self._keys
37
+
38
+ def values(self):
39
+ """Returns the list of values from the query."""
40
+ return self._values
41
+
42
+ def __repr__(self):
43
+ return '<Record {}>'.format(self.export('json')[1:-1])
44
+
45
+ def __getitem__(self, key):
46
+ # Support for index-based lookup.
47
+ if isinstance(key, int):
48
+ return self.values()[key]
49
+
50
+ # Support for string-based lookup.
51
+ if key in self.keys():
52
+ i = self.keys().index(key)
53
+ if self.keys().count(key) > 1:
54
+ raise KeyError("Record contains multiple '{}' fields.".format(key))
55
+ return self.values()[i]
56
+
57
+ raise KeyError("Record contains no '{}' field.".format(key))
58
+
59
+ def __getattr__(self, key):
60
+ try:
61
+ return self[key]
62
+ except KeyError as e:
63
+ raise AttributeError(e)
64
+
65
+ def __dir__(self):
66
+ standard = dir(super(Record, self))
67
+ # Merge standard attrs with generated ones (from column names).
68
+ return sorted(standard + [str(k) for k in self.keys()])
69
+
70
+ def get(self, key, default=None):
71
+ """Returns the value for a given key, or default."""
72
+ try:
73
+ return self[key]
74
+ except KeyError:
75
+ return default
76
+
77
+ def as_dict(self, ordered=False):
78
+ """Returns the row as a dictionary, as ordered."""
79
+ items = zip(self.keys(), self.values())
80
+
81
+ return OrderedDict(items) if ordered else dict(items)
82
+
83
+ @property
84
+ def dataset(self):
85
+ """A Tablib Dataset containing the row."""
86
+ data = tablib.Dataset()
87
+ data.headers = self.keys()
88
+
89
+ row = _reduce_datetimes(self.values())
90
+ data.append(row)
91
+
92
+ return data
93
+
94
+ def export(self, format, **kwargs):
95
+ """Exports the row to the given format."""
96
+ return self.dataset.export(format, **kwargs)
97
+
98
+
99
+ class RecordCollection(object):
100
+ """A set of excellent Records from a query."""
101
+
102
+ def __init__(self, rows):
103
+ self._rows = rows
104
+ self._all_rows = []
105
+ self.pending = True
106
+
107
+ def __repr__(self):
108
+ return '<RecordCollection size={} pending={}>'.format(len(self), self.pending)
109
+
110
+ def __iter__(self):
111
+ """Iterate over all rows, consuming the underlying generator
112
+ only when necessary."""
113
+ i = 0
114
+ while True:
115
+ # Other code may have iterated between yields,
116
+ # so always check the cache.
117
+ if i < len(self):
118
+ yield self[i]
119
+ else:
120
+ # Throws StopIteration when done.
121
+ # Prevent StopIteration bubbling from generator,
122
+ # following https://www.python.org/dev/peps/pep-0479/
123
+ try:
124
+ yield next(self)
125
+ except StopIteration:
126
+ return
127
+ i += 1
128
+
129
+ def next(self):
130
+ return self.__next__()
131
+
132
+ def __next__(self):
133
+ try:
134
+ nextrow = next(self._rows)
135
+ self._all_rows.append(nextrow)
136
+ return nextrow
137
+ except StopIteration:
138
+ self.pending = False
139
+ raise StopIteration('RecordCollection contains no more rows.')
140
+
141
+ def __getitem__(self, key):
142
+ is_int = isinstance(key, int)
143
+
144
+ # Convert RecordCollection[1] into slice.
145
+ if is_int:
146
+ key = slice(key, key + 1)
147
+
148
+ while len(self) < key.stop or key.stop is None:
149
+ try:
150
+ next(self)
151
+ except StopIteration:
152
+ break
153
+
154
+ rows = self._all_rows[key]
155
+ if is_int:
156
+ return rows[0]
157
+ else:
158
+ return RecordCollection(iter(rows))
159
+
160
+ def __len__(self):
161
+ return len(self._all_rows)
162
+
163
+ def export(self, format, **kwargs):
164
+ """Export the RecordCollection to a given format (courtesy of Tablib)."""
165
+ return self.dataset.export(format, **kwargs)
166
+
167
+ @property
168
+ def dataset(self):
169
+ """A Tablib Dataset representation of the RecordCollection."""
170
+ # Create a new Tablib Dataset.
171
+ data = tablib.Dataset()
172
+
173
+ # If the RecordCollection is empty, just return the empty set
174
+ # Check number of rows by typecasting to list
175
+ if len(list(self)) == 0:
176
+ return data
177
+
178
+ # Set the column names as headers on Tablib Dataset.
179
+ first = self[0]
180
+
181
+ data.headers = first.keys()
182
+ for row in self.all():
183
+ row = _reduce_datetimes(row.values())
184
+ data.append(row)
185
+
186
+ return data
187
+
188
+ def all(self, as_dict=False, as_ordereddict=False):
189
+ """Returns a list of all rows for the RecordCollection. If they haven't
190
+ been fetched yet, consume the iterator and cache the results."""
191
+
192
+ # By calling list it calls the __iter__ method
193
+ rows = list(self)
194
+
195
+ if as_dict:
196
+ return [r.as_dict() for r in rows]
197
+ elif as_ordereddict:
198
+ return [r.as_dict(ordered=True) for r in rows]
199
+
200
+ return rows
201
+
202
+ def as_dict(self, ordered=False):
203
+ return self.all(as_dict=not (ordered), as_ordereddict=ordered)
204
+
205
+ def first(self, default=None, as_dict=False, as_ordereddict=False):
206
+ """Returns a single record for the RecordCollection, or `default`. If
207
+ `default` is an instance or subclass of Exception, then raise it
208
+ instead of returning it."""
209
+
210
+ # Try to get a record, or return/raise default.
211
+ try:
212
+ record = self[0]
213
+ except IndexError:
214
+ if is_exception(default):
215
+ raise default
216
+ return default
217
+
218
+ # Cast and return.
219
+ if as_dict:
220
+ return record.as_dict()
221
+ elif as_ordereddict:
222
+ return record.as_dict(ordered=True)
223
+ else:
224
+ return record
225
+
226
+ def one(self, default=None, as_dict=False, as_ordereddict=False):
227
+ """Returns a single record for the RecordCollection, ensuring that it
228
+ is the only record, or returns `default`. If `default` is an instance
229
+ or subclass of Exception, then raise it instead of returning it."""
230
+
231
+ # Ensure that we don't have more than one row.
232
+ try:
233
+ return self[1]
234
+ except IndexError:
235
+ return self.first(default=default, as_dict=as_dict,
236
+ as_ordereddict=as_ordereddict)
237
+ else:
238
+ raise ValueError('RecordCollection contained more than one row. '
239
+ 'Expects only one row when using '
240
+ 'RecordCollection.one')
241
+
242
+ def scalar(self, default=None):
243
+ """Returns the first column of the first row, or `default`."""
244
+ row = self.one()
245
+ return row[0] if row else default
246
+
247
+
248
+ class Database(object):
249
+ """A Database. Encapsulates a url and an SQLAlchemy engine with a pool of
250
+ connections.
251
+ """
252
+
253
+ def __init__(self, db_url=None, **kwargs):
254
+ # If no db_url was provided, fallback to $DATABASE_URL.
255
+ self.db_url = db_url or DATABASE_URL
256
+
257
+ if not self.db_url:
258
+ raise ValueError('You must provide a db_url.')
259
+
260
+ # Create an engine.
261
+ self._engine = create_engine(self.db_url, **kwargs)
262
+ self.open = True
263
+
264
+ def close(self):
265
+ """Closes the Database."""
266
+ self._engine.dispose()
267
+ self.open = False
268
+
269
+ def __enter__(self):
270
+ return self
271
+
272
+ def __exit__(self, exc, val, traceback):
273
+ self.close()
274
+
275
+ def __repr__(self):
276
+ return '<Database open={}>'.format(self.open)
277
+
278
+ def get_table_names(self):
279
+ """Returns a list of table names for the connected database."""
280
+
281
+ # Setup SQLAlchemy for Database inspection.
282
+ return inspect(self._engine).get_table_names()
283
+
284
+ def get_connection(self):
285
+ """Get a connection to this Database. Connections are retrieved from a
286
+ pool.
287
+ """
288
+ if not self.open:
289
+ raise exc.ResourceClosedError('Database closed.')
290
+
291
+ return Connection(self._engine.connect())
292
+
293
+ def query(self, query, fetchall=False, **params):
294
+ """Executes the given SQL query against the Database. Parameters can,
295
+ optionally, be provided. Returns a RecordCollection, which can be
296
+ iterated over to get result rows as dictionaries.
297
+ """
298
+ with self.get_connection() as conn:
299
+ return conn.query(query, fetchall, **params)
300
+
301
+ def bulk_query(self, query, *multiparams):
302
+ """Bulk insert or update."""
303
+
304
+ with self.get_connection() as conn:
305
+ conn.bulk_query(query, *multiparams)
306
+
307
+
308
+ class Connection(object):
309
+ """A Database connection."""
310
+
311
+ def __init__(self, connection):
312
+ self._conn = connection
313
+ self.open = not connection.closed
314
+
315
+ def close(self):
316
+ self._conn.close()
317
+ self.open = False
318
+
319
+ def __enter__(self):
320
+ return self
321
+
322
+ def __exit__(self, exc, val, traceback):
323
+ self.close()
324
+
325
+ def __repr__(self):
326
+ return '<Connection open={}>'.format(self.open)
327
+
328
+ def query(self, query, fetchall=False, **params):
329
+ """Executes the given SQL query against the connected Database.
330
+ Parameters can, optionally, be provided. Returns a RecordCollection,
331
+ which can be iterated over to get result rows as dictionaries.
332
+ """
333
+
334
+ # Execute the given query.
335
+ cursor = self._conn.execute(text(query), **params) # TODO: PARAMS GO HERE
336
+
337
+ # Row-by-row Record generator.
338
+ row_gen = (Record(cursor.keys(), row) for row in cursor)
339
+
340
+ # Convert psycopg2 results to RecordCollection.
341
+ results = RecordCollection(row_gen)
342
+
343
+ # Fetch all results if desired.
344
+ if fetchall:
345
+ results.all()
346
+
347
+ return results
348
+
349
+ def bulk_query(self, query, *multiparams):
350
+ """Bulk insert or update."""
351
+
352
+ self._conn.execute(text(query), *multiparams)
353
+
354
+
355
+ def _reduce_datetimes(row):
356
+ """Receives a row, converts datetimes to strings."""
357
+
358
+ row = list(row)
359
+
360
+ for i in range(len(row)):
361
+ if hasattr(row[i], 'isoformat'):
362
+ row[i] = row[i].isoformat()
363
+ return tuple(row)
@@ -0,0 +1,264 @@
1
+ import json
2
+ from threading import Thread
3
+ from queue import Queue
4
+
5
+ import tqdm
6
+ import requests
7
+ from bs4 import BeautifulSoup
8
+
9
+ from common import utils
10
+ from config.log import logger
11
+ from common.database import Database
12
+ from config import settings
13
+
14
+
15
+ def req_thread_count():
16
+ count = settings.request_thread_count
17
+ if isinstance(count, int):
18
+ count = max(16, count)
19
+ else:
20
+ count = utils.get_request_count()
21
+ logger.log('DEBUG', f'Number of request threads {count}')
22
+ return count
23
+
24
+
25
+ def get_port_seq(port):
26
+ logger.log('DEBUG', 'Getting port range')
27
+ ports = set()
28
+ if isinstance(port, (set, list, tuple)):
29
+ ports = port
30
+ elif isinstance(port, int):
31
+ if 0 <= port <= 65535:
32
+ ports = {port}
33
+ elif port in {'small', 'medium', 'large'}:
34
+ logger.log('DEBUG', f'{port} port range')
35
+ ports = settings.ports.get(port)
36
+ if not ports: # 意外情况
37
+ logger.log('ERROR', 'The specified request port range is incorrect')
38
+ ports = {80}
39
+ logger.log('INFOR', f'Port range:{ports}')
40
+ return set(ports)
41
+
42
+
43
+ def gen_req_url(domain, port):
44
+ if str(port).endswith('443'):
45
+ url = f'https://{domain}:{port}'
46
+ if port == 443:
47
+ url = f'https://{domain}'
48
+ return url
49
+ url = f'http://{domain}:{port}'
50
+ if port == 80:
51
+ url = f'http://{domain}'
52
+ return url
53
+
54
+
55
+ def gen_req_data(data, ports):
56
+ logger.log('INFOR', 'Generating request urls')
57
+ req_data = list()
58
+ req_urls = set()
59
+ for info in data:
60
+ resolve = info.get('resolve')
61
+ # 解析不成功的子域不进行http请求探测
62
+ if resolve != 1:
63
+ continue
64
+ subdomain = info.get('subdomain')
65
+ for port in ports:
66
+ tmp_info = info.copy()
67
+ tmp_info['port'] = port
68
+ url = gen_req_url(subdomain, port)
69
+ tmp_info['url'] = url
70
+ req_data.append(tmp_info)
71
+ req_urls.add(url)
72
+ return req_data, req_urls
73
+
74
+
75
+ def get_html_title(markup):
76
+ """
77
+ 获取标题
78
+
79
+ :param markup: html标签
80
+ :return: 标题
81
+ """
82
+ soup = BeautifulSoup(markup, 'html.parser')
83
+
84
+ title = soup.title
85
+ if title:
86
+ return title.text
87
+
88
+ h1 = soup.h1
89
+ if h1:
90
+ return h1.text
91
+
92
+ h2 = soup.h2
93
+ if h2:
94
+ return h2.text
95
+
96
+ h3 = soup.h3
97
+ if h3:
98
+ return h3.text
99
+
100
+ desc = soup.find('meta', attrs={'name': 'description'})
101
+ if desc:
102
+ return desc['content']
103
+
104
+ word = soup.find('meta', attrs={'name': 'keywords'})
105
+ if word:
106
+ return word['content']
107
+
108
+ text = soup.text
109
+ if len(text) <= 200:
110
+ return repr(text)
111
+
112
+ return 'None'
113
+
114
+
115
+ def get_jump_urls(history):
116
+ urls = list()
117
+ for resp in history:
118
+ urls.append(str(resp.url))
119
+ return urls
120
+
121
+
122
+ def get_progress_bar(total):
123
+ bar = tqdm.tqdm()
124
+ bar.total = total
125
+ bar.desc = 'Request Progress'
126
+ bar.ncols = 80
127
+ return bar
128
+
129
+
130
+ def get_resp(url, session):
131
+ timeout = settings.request_timeout_second
132
+ redirect = settings.request_allow_redirect
133
+ proxy = utils.get_proxy()
134
+ try:
135
+ resp = session.get(url, timeout=timeout, allow_redirects=redirect, proxies=proxy)
136
+ except Exception as e:
137
+ logger.log('DEBUG', e.args)
138
+ resp = e
139
+ return resp
140
+
141
+
142
+ def request(urls_queue, resp_queue, session):
143
+ while not urls_queue.empty():
144
+ index, url = urls_queue.get()
145
+ resp = get_resp(url, session)
146
+ resp_queue.put((index, resp))
147
+ urls_queue.task_done()
148
+
149
+
150
+ def progress(bar, total, urls_queue):
151
+ while True:
152
+ remaining = urls_queue.qsize()
153
+ done = total - remaining
154
+ bar.n = done
155
+ bar.update()
156
+ if remaining == 0:
157
+ break
158
+
159
+
160
+ def get_session():
161
+ header = utils.gen_fake_header()
162
+ verify = settings.request_ssl_verify
163
+ redirect_limit = settings.request_redirect_limit
164
+ session = requests.Session()
165
+ session.trust_env = False
166
+ session.headers = header
167
+ session.verify = verify
168
+ session.max_redirects = redirect_limit
169
+ return session
170
+
171
+
172
+ def gen_new_info(info, resp):
173
+ if isinstance(resp, Exception):
174
+ info['reason'] = str(resp.args)
175
+ info['request'] = 0
176
+ info['alive'] = 0
177
+ return info
178
+ info['reason'] = resp.reason
179
+ code = resp.status_code
180
+ info['status'] = code
181
+ info['request'] = 1
182
+ if code == 400 or code >= 500:
183
+ info['alive'] = 0
184
+ else:
185
+ info['alive'] = 1
186
+ headers = resp.headers
187
+ if settings.enable_banner_identify:
188
+ info['banner'] = utils.get_sample_banner(headers)
189
+ info['header'] = json.dumps(dict(headers))
190
+ history = resp.history
191
+ info['history'] = json.dumps(get_jump_urls(history))
192
+ text = utils.decode_resp_text(resp)
193
+ title = get_html_title(text).strip()
194
+ info['title'] = utils.remove_invalid_string(title)
195
+ info['response'] = utils.remove_invalid_string(text)
196
+ return info
197
+
198
+
199
+ def save(name, total, req_data, resp_queue):
200
+ db = Database()
201
+ db.create_table(name)
202
+ i = 0
203
+ while True:
204
+ if not resp_queue.empty():
205
+ i += 1
206
+ index, resp = resp_queue.get()
207
+ old_info = req_data[index]
208
+ new_info = gen_new_info(old_info, resp)
209
+ db.insert_table(name, new_info)
210
+ resp_queue.task_done()
211
+ if i >= total: # 得存入完所有请求结果才能结束
212
+ break
213
+ db.close()
214
+
215
+
216
+ def bulk_request(domain, req_data, ret=False):
217
+ logger.log('INFOR', 'Requesting urls in bulk')
218
+ resp_queue = Queue()
219
+ urls_queue = Queue()
220
+ task_count = len(req_data)
221
+ for index, info in enumerate(req_data):
222
+ url = info.get('url')
223
+ urls_queue.put((index, url))
224
+ session = get_session()
225
+ thread_count = req_thread_count()
226
+ if task_count <= thread_count:
227
+ # 如果请求任务数很小不用创建很多线程了
228
+ thread_count = task_count
229
+ bar = get_progress_bar(task_count)
230
+
231
+ progress_thread = Thread(target=progress, name='ProgressThread',
232
+ args=(bar, task_count, urls_queue), daemon=True)
233
+ progress_thread.start()
234
+
235
+ for i in range(thread_count):
236
+ request_thread = Thread(target=request, name=f'RequestThread-{i}',
237
+ args=(urls_queue, resp_queue, session), daemon=True)
238
+ request_thread.start()
239
+ if ret:
240
+ urls_queue.join()
241
+ return resp_queue
242
+ save_thread = Thread(target=save, name=f'SaveThread',
243
+ args=(domain, task_count, req_data, resp_queue), daemon=True)
244
+ save_thread.start()
245
+ urls_queue.join()
246
+ save_thread.join()
247
+
248
+
249
+ def run_request(domain, data, port):
250
+ """
251
+ HTTP request entrance
252
+
253
+ :param str domain: domain to be requested
254
+ :param list data: subdomains data to be requested
255
+ :param any port: range of ports to be requested
256
+ :return list: result
257
+ """
258
+ logger.log('INFOR', f'Start requesting subdomains of {domain}')
259
+ data = utils.set_id_none(data)
260
+ ports = get_port_seq(port)
261
+ req_data, req_urls = gen_req_data(data, ports)
262
+ bulk_request(domain, req_data)
263
+ count = utils.count_alive(domain)
264
+ logger.log('INFOR', f'Found that {domain} has {count} alive subdomains')