diff --git a/floodbl.py b/floodbl.py
@@ -1,48 +1,16 @@
#!/usr/bin/env python
# FloodBL - Developed by acidvegas in Python (https://git.acid.vegas/proxytools)
-'''
-Notes for future improvement:
-
-To query an IPv6 address, you must expand it, then reverse it into "nibble" format.
- e.g. if the IP was 2001:db8::1, you expand it to 2001:0db8:0000:0000:0000:0000:0000:0001 and reverse it.
- In nibble format it is 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2 and add on the dns blacklist you require.
-
- e.g. 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.tor.dan.me.uk
-
- If the IP has a match, the DNS server will respond with an "A" record of 127.0.0.100.
- It will also respond with a "TXT" record with extra information as per below:
-
- N:<nodename>/P:<port1[,port2]>/F:<flags>
-
- port1 is the OR (onion router) port, port2 (if specified) is the DR (directory) port.
- Flags are defined as follows:
- E Exit
- X Hidden Exit
- A Authority
- B BadExit
- C NoEdConsensus
- D V2Dir
- F Fast
- G Guard
- H HSDir
- N Named
- R Running
- S Stable
- U Unnamed
- V Valid
-'''
-
import argparse
import concurrent.futures
+import ipaddress
import os
import re
-import socket
try:
import dns.resolver
except ImportError:
- raise SystemExit('error: missing required \'dnspython\' library (https://pypi.org/project/dnspython/)')
+ raise SystemExit('missing required \'dnspython\' library (https://pypi.org/project/dnspython/)')
# Globals
good = list()
@@ -67,36 +35,22 @@ blackholes = {
'15' : 'Compromised router / gateway',
'16' : 'Autorooting worms',
'17' : 'Automatically determined botnet IPs (experimental)',
- '18' : 'DNS/MX type'
+ '18' : 'DNS/MX type',
+ '19' : 'Abused VPN Service',
+ '255': 'Uncategorzied threat class'
},
-# 'rbl.efnetrbl.org': { # NOTE: Most IRC networks use DroneBL, un-comment this section to check the EFnetRBL
-# '1' : "Open Proxy",
-# '2' : "spamtrap666",
-# '3' : "spamtrap50",
-# '4' : "TOR",
-# '5' : "Drones / Flooding"
-# },
-# 'torexit.dan.me.uk': { # TODO: The require a TXT lookup, although IRC daemons do numeric replies...will look into this
-# 'E' : 'Exit',
-# 'X' : 'Hidden Exit',
-# 'A' : 'Authority',
-# 'B' : 'BadExit',
-# 'C' : 'NoEdConsensus',
-# 'D' : 'V2Dir',
-# 'F' : 'Fast',
-# 'G' : 'Guard',
-# 'H' : 'HSDir',
-# 'N' : 'Named',
-# 'R' : 'Running',
-# 'S' : 'Stable',
-# 'U' : 'Unnamed',
-# 'V' : 'Valid'
-# }
+ 'rbl.efnetrbl.org': {
+ '1' : "Open Proxy",
+ '2' : "spamtrap666",
+ '3' : "spamtrap50",
+ '4' : "TOR",
+ '5' : "Drones / Flooding"
+ }
}
def check(proxy):
proxy_ip = proxy.split(':')[0]
- formatted_ip = '.'.join(proxy_ip.split('.')[::-1])
+ formatted_ip = ipaddress.ip_address(proxy_ip).reverse_pointer
for blackhole in blackholes:
try:
results = dns.resolver.resolve(f'{formatted_ip}.{blackhole}', 'A')
@@ -112,7 +66,7 @@ def check(proxy):
unknown.append(proxy)
else:
print(f'{proxy_ip.ljust(15)} \033[1;30m|\033[0m {blackhole.ljust(17)} \033[1;30m|\033[0m Error (No results)')
- unkown.append(proxy)
+ unknown.append(proxy)
except Exception as ex:
print(f'{proxy_ip.ljust(15)} \033[1;30m|\033[0m {blackhole.ljust(17)} \033[1;30m|\033[0m \033[1;32mGOOD\033[0m')
if proxy not in bad:
@@ -134,7 +88,7 @@ args = parser.parse_args()
if not os.path.isfile(args.input):
raise SystemExit('no such input file')
initial = len(open(args.input).readlines())
-proxies = set([proxy.split(':')[0] for proxy in re.findall('[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+', open(args.input).read(), re.MULTILINE)])
+proxies = set([proxy.split(':')[0] for proxy in re.findall('[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+', open(args.input).read(), re.MULTILINE)]) # TODO: handle IPv6 better
if not proxies:
raise SystemExit('no proxies found from input file')
with concurrent.futures.ThreadPoolExecutor(max_workers=args.threads) as executor:
diff --git a/shellscrape b/shellscrape
@@ -0,0 +1,20 @@
+#!/bin/env bash
+# shellscrape - developed by acidvegas (https://git.acid.vegas/proxytools)
+
+URLS=(
+ "https://raw.githubusercontent.com/ALIILAPRO/Proxy/main/socks4.txt"
+ "https://raw.githubusercontent.com/ALIILAPRO/Proxy/main/socks5.txt"
+ "https://raw.githubusercontent.com/B4RC0DE-TM/proxy-list/main/SOCKS4.txt"
+ "https://raw.githubusercontent.com/B4RC0DE-TM/proxy-list/main/SOCKS5.txt"
+ "https://raw.githubusercontent.com/hookzof/socks5_list/master/proxy.txt"
+ "https://raw.githubusercontent.com/HyperBeats/proxy-list/main/socks4.txt"
+ "https://raw.githubusercontent.com/HyperBeats/proxy-list/main/socks5.txt"
+)
+
+[ -f proxies.txt ] >proxies.txt
+for URL in "${URLS[@]}"; do
+ echo "Downloading from $URL"
+ curl -s $URL >> proxies.txt &
+done
+sort -u -o proxies.txt proxies.txt
+echo "done"
diff --git a/sockhub.py b/sockhub.py
@@ -1,23 +1,15 @@
#!/usr/bin/env python
-# SockSpot Proxy Scraper - Developed by acidvegas in Python (https://git.acid.vegas/proxytools)
+# SockHub Proxy Scraper - Developed by acidvegas in Python (https://git.acid.vegas/proxytools)
-'''
-
-Scrap IP:PORT proxies from a URL list
-
-'''
-
-import concurrent.futures
import os
import re
-import time
import urllib.request
# Can be any URL containing a list of IP:PORT proxies (does not have to be socks5)
# The current list contains proxy sources that are updated frequently with new proxies
# Almost all of the Github repos pull from the same place & contain duplicates (which are removed)
urls = set((
- 'https://api.openproxylist.xyz/socks4.txt'
+ 'https://api.openproxylist.xyz/socks4.txt',
'https://api.openproxylist.xyz/socks5.txt',
'https://api.proxyscrape.com/?request=displayproxies&proxytype=socks4',
'https://api.proxyscrape.com/v2/?request=displayproxies&protocol=socks4',
@@ -76,10 +68,11 @@ urls = set((
'https://spys.one/en/socks-proxy-list/'
))
-def get_source(url):
+def get_source(url: str) -> str:
+ ''' Get the source of a URL using a Googlebot user-agent. '''
req = urllib.request.Request(url)
req.add_header('User-Agent', 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)')
- source = urllib.request.urlopen(req, timeout=10)
+ source = urllib.request.urlopen(req, timeout=15)
return source.read().decode()
# Main
diff --git a/sockspot.py b/sockspot.py
@@ -1,131 +0,0 @@
-#!/usr/bin/env python
-# SockSpot - Developed by acidvegas in Python (https://git.acid.vegas/proxytools)
-
-'''
-
-This script will scan popular blogspots that posts freesh proxies daily
-
-
-Edit: It seems Blogspots for proxies in 2023 is no longer a reliable source.
-This code is old & will possibly be updated again in the future.
-
-'''
-
-import datetime
-import json
-import base64
-import os
-import re
-import threading
-import time
-import urllib.request
-
-# Blogspot URLs
-blogspot_list = (
- 'live-socks.net',
- 'newfreshproxies-24.blogspot.sg',
- 'proxyserverlist-24.blogspot.sg',
- 'socks24.org',
- 'sock5us.blogspot.com',
- 'sockproxy.blogspot.com',
- 'socksproxylist24.blogspot.com',
- 'newsocks.info',
- 'socksecurelist.ca',
- 'canada-socks247.com',
- 'sock5us.blogspot.com',
- 'socks24.org',
- 'sslproxies24.blogspot.com',
- 'vip-socks24.blogspot.com'
-)
-
-# Settings
-max_results = 100 # Maximum number of results per-page.
-post_depth = 1 # How many days back from the current date to pull posts from. (1 = Today Only)
-timeout = 30 # Timeout for HTTP requests.
-
-# Globals
-proxy_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'proxies.txt')
-proxy_list = list()
-threads = dict()
-
-def debug(msg):
- print(f'{get_time()} | [~] - {msg}')
-
-def error(msg, reason):
- print(f'{get_time()} | [!] - {msg} ({reason})')
-
-def get_time():
- return time.strftime('%I:%M:%S')
-
-def get_date():
- date = datetime.datetime.today()
- return '{0}-{1:02d}-{2:02d}'.format(date.year, date.month, date.day)
-
-def get_date_range():
- date_range = datetime.datetime.today() - datetime.timedelta(days=post_depth)
- return '{0}-{1:02d}-{2:02d}'.format(date_range.year, date_range.month, date_range.day)
-
-def get_source(url):
- req = urllib.request.Request(url)
- req.add_header('User-Agent', 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)')
- source = urllib.request.urlopen(req, timeout=timeout)
- charset = source.headers.get_content_charset()
- if charset:
- return source.read().decode(charset)
- else:
- return source.read().decode()
-
-def parse_blogspot(url):
- global proxy_list
- try:
- source = json.loads(get_source(f'http://{url}/feeds/posts/default?max-results={max_results}&alt=json&updated-min={get_date_range()}T00:00:00&updated-max={get_date()}T23:59:59&orderby=updated'))
- found = []
- if source['feed'].get('entry'):
- for item in source['feed']['entry']:
- data = item['content']['$t']
- proxies = re.findall('[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+', data, re.MULTILINE)
- if proxies:
- found += proxies
- proxy_list += proxies
- debug('Found {0} proxies on {1}'.format(format(len(found), ',d'), url))
- else:
- error('No posts found on page!', url)
- except Exception as ex:
- proxy_value = ex
-
-def scan_blogspots():
- for url in blogspot_list:
- threads[url] = threading.Thread(target=parse_blogspot, args=(url,))
- for thread in threads:
- threads[thread].start()
- time.sleep(10)
- for thread in threads:
- threads[thread].join()
- debug('Found {0} total proxies!'.format(format(len(proxy_list), ',d')))
- with open (proxy_file, 'w') as proxy__file:
- for proxy in proxy_list:
- proxy__file.write(proxy + '\n')
-
-# Main
-print('#'*56)
-print('#{0}#'.format(''.center(54)))
-print('#{0}#'.format('SockSpot Proxy Scraper'.center(54)))
-print('#{0}#'.format('Developed by acidvegas in Python'.center(54)))
-print('#{0}#'.format('https://git.acid.vegas/proxytools'.center(54)))
-print('#{0}#'.format(''.center(54)))
-print('#'*56)
-debug(f'Scanning {len(blogspot_list)} URLs from list...')
-threading.Thread(target=scan_blogspots).start()
-for url in blogspot_list:
- threads[url] = threading.Thread(target=parse_blogspot, args=(url,))
-for thread in threads:
- threads[thread].start()
- time.sleep(10)
-for thread in threads:
- threads[thread].join()
-if proxy_value == 0:
- error('no socks found')
-debug('Found {0} total proxies!'.format(format(len(proxy_list), ',d')))
-with open (proxy_file, 'w') as proxy__file:
- for proxy in proxy_list:
- proxy__file.write(proxy + '\n')
diff --git a/tor_network_test.py b/tor_network_test.py
@@ -1,76 +0,0 @@
-import StringIO
-import time
-
-import pycurl
-
-import stem.control
-
-# https://metrics.torproject.org/rs.html#details/379FB450010D17078B3766C2273303C358C3A442
-
-EXIT_FINGERPRINT = '379FB450010D17078B3766C2273303C358C3A442'
-
-SOCKS_PORT = 9050
-CONNECTION_TIMEOUT = 30 # timeout before we give up on a circuit
-
-def query(url):
- """
- Uses pycurl to fetch a site using the proxy on the SOCKS_PORT.
- """
-
- output = StringIO.StringIO()
-
- query = pycurl.Curl()
- query.setopt(pycurl.URL, url)
- query.setopt(pycurl.PROXY, 'localhost')
- query.setopt(pycurl.PROXYPORT, SOCKS_PORT)
- query.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_SOCKS5_HOSTNAME)
- query.setopt(pycurl.CONNECTTIMEOUT, CONNECTION_TIMEOUT)
- query.setopt(pycurl.WRITEFUNCTION, output.write)
-
- try:
- query.perform()
- return output.getvalue()
- except pycurl.error as exc:
- raise ValueError("Unable to reach %s (%s)" % (url, exc))
-
-
-def scan(controller, path):
- """
- Fetch check.torproject.org through the given path of relays, providing back
- the time it took.
- """
-
- circuit_id = controller.new_circuit(path, await_build = True)
-
- def attach_stream(stream):
- if stream.status == 'NEW':
- controller.attach_stream(stream.id, circuit_id)
-
- controller.add_event_listener(attach_stream, stem.control.EventType.STREAM)
-
- try:
- controller.set_conf('__LeaveStreamsUnattached', '1') # leave stream management to us
- start_time = time.time()
-
- check_page = query('https://check.torproject.org/')
-
- if 'Congratulations. This browser is configured to use Tor.' not in check_page:
- raise ValueError("Request didn't have the right content")
-
- return time.time() - start_time
- finally:
- controller.remove_event_listener(attach_stream)
- controller.reset_conf('__LeaveStreamsUnattached')
-
-
-with stem.control.Controller.from_port() as controller:
- controller.authenticate()
-
- relay_fingerprints = [desc.fingerprint for desc in controller.get_network_statuses()]
-
- for fingerprint in relay_fingerprints:
- try:
- time_taken = scan(controller, [fingerprint, EXIT_FINGERPRINT])
- print('%s => %0.2f seconds' % (fingerprint, time_taken))
- except Exception as exc:
- print('%s => %s' % (fingerprint, exc))
-\ No newline at end of file
diff --git a/torglass.py b/torglass.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+# Tor Glass - Developed by acidvegas in Python (https://git.acid.vegas/proxytools)
+
+import json
+
+try:
+ import stem.descriptor.remote
+except ImportError:
+ raise SystemExit('missing required library \'stem\' (https://pypi.org/project/stem/)')
+
+def get_descriptors() -> dict:
+ ''' Generate a json database of all Tor relays & exit nodes '''
+ tor_map = {'relay':list(),'exit':list()}
+ for relay in stem.descriptor.remote.get_server_descriptors():
+ data = {
+ 'nickname' : relay.nickname,
+ 'fingerprint' : relay.fingerprint,
+ 'published' : str(relay.published) if relay.published else None,
+ 'address' : relay.address,
+ 'or_port' : relay.or_port,
+ 'socks_port' : relay.socks_port,
+ 'dir_port' : relay.dir_port,
+ 'platform' : str(relay.platform) if relay.platform else None,
+ 'tor_version' : str(relay.tor_version),
+ 'operating_system' : relay.operating_system,
+ 'uptime' : relay.uptime,
+ 'contact' : str(relay.contact) if relay.contact else None,
+ 'exit_policy' : str(relay.exit_policy) if relay.exit_policy else None,
+ 'exit_policy_v6' : str(relay.exit_policy_v6) if relay.exit_policy_v6 else None,
+ 'bridge_distribution' : relay.bridge_distribution,
+ 'family' : list(relay.family) if relay.family else None,
+ 'average_bandwidth' : relay.average_bandwidth,
+ 'burst_bandwidth' : relay.burst_bandwidth,
+ 'observed_bandwidth' : relay.observed_bandwidth,
+ 'link_protocols' : relay.link_protocols,
+ 'circuit_protocols' : relay.circuit_protocols,
+ 'is_hidden_service_dir' : relay.is_hidden_service_dir,
+ 'hibernating' : relay.hibernating,
+ 'allow_single_hop_exits' : relay.allow_single_hop_exits,
+ 'allow_tunneled_dir_requests' : relay.allow_tunneled_dir_requests,
+ 'extra_info_cache' : relay.extra_info_cache,
+ 'extra_info_digest' : relay.extra_info_digest,
+ 'extra_info_sha256_digest' : relay.extra_info_sha256_digest,
+ 'eventdns' : relay.eventdns,
+ 'ntor_onion_key' : relay.ntor_onion_key,
+ 'or_addresses' : relay.or_addresses,
+ 'protocols' : relay.protocols
+ }
+ if relay.exit_policy.is_exiting_allowed():
+ tor_map['exit'].append(data)
+ else:
+ tor_map['relay'].append(data)
+ return tor_map
+
+if __name__ == '__main__':
+ print('loading Tor descriptors... (this could take a while)')
+ tor_data = get_descriptors()
+ with open('tor.json', 'w') as fd:
+ json.dump(tor_data['relay'], fd)
+ with open('tor.exit.json', 'w') as fd:
+ json.dump(tor_data['exit'], fd)
+ print('Relays: {0:,}'.foramt(len(tor_data['relay'])))
+ print('Exits : {0:,}'.format(len(tor_data['exit'])))
+ try:
+ import ipinfo
+ except ImportError:
+ print('missing optional library \'ipinfo\' (https://pypi.org/project/ipinfo/) for map visualization')
+ else:
+ try:
+ handler = ipinfo.getHandler('changeme') # put your ipinfo.io API key here
+ print('Relay Map: ' + handler.getMap([ip['address'] for ip in tor_data['relay']]))
+ print('Exit Map: ' + handler.getMap([ip['address'] for ip in tor_data['exit']]))
+ except ipinfo.errors.AuthorizationError:
+ print('error: invalid ipinfo.io API key (https://ipinfo.io/signup)')
+ except Exception as ex:
+ print(f'error generating ipinfo map ({ex})')
+\ No newline at end of file
diff --git a/torscan.py b/torscan.py
@@ -1,61 +0,0 @@
-#!/usr/bin/env python
-# Tor Scan - Developed by acidvegas in Python (https://git.acid.vegas/proxytools)
-
-'''
-
-PUll a list of information about all Tor relays & exit relays into a json database
-
-'''
-
-import json
-
-try:
- import stem.descriptor.remote
-except ImportError:
- raise SystemExit('missing required library \'stem\' (https://pypi.org/project/stem/)')
-
-tor_map = {'relay':list(),'exit':list()}
-
-for relay in stem.descriptor.remote.get_server_descriptors().run():
- _map = {
- 'nickname' : relay.nickname,
- 'fingerprint' : relay.fingerprint,
- 'published' : str(relay.published) if relay.published else None,
- 'address' : relay.address,
- 'or_port' : relay.or_port,
- 'socks_port' : relay.socks_port,
- 'dir_port' : relay.dir_port,
- 'platform' : str(relay.platform) if relay.platform else None,
- 'tor_version' : str(relay.tor_version),
- 'operating_system' : relay.operating_system,
- 'uptime' : relay.uptime,
- 'contact' : str(relay.contact) if relay.contact else None,
- 'exit_policy' : str(relay.exit_policy) if relay.exit_policy else None,
- 'exit_policy_v6' : str(relay.exit_policy_v6) if relay.exit_policy_v6 else None,
- 'bridge_distribution' : relay.bridge_distribution,
- 'family' : list(relay.family) if relay.family else None,
- 'average_bandwidth' : relay.average_bandwidth,
- 'burst_bandwidth' : relay.burst_bandwidth,
- 'observed_bandwidth' : relay.observed_bandwidth,
- 'link_protocols' : relay.link_protocols,
- 'circuit_protocols' : relay.circuit_protocols,
- 'is_hidden_service_dir' : relay.is_hidden_service_dir,
- 'hibernating' : relay.hibernating,
- 'allow_single_hop_exits' : relay.allow_single_hop_exits,
- 'allow_tunneled_dir_requests' : relay.allow_tunneled_dir_requests,
- 'extra_info_cache' : relay.extra_info_cache,
- 'extra_info_digest' : relay.extra_info_digest,
- 'extra_info_sha256_digest' : relay.extra_info_sha256_digest,
- 'eventdns' : relay.eventdns,
- 'ntor_onion_key' : relay.ntor_onion_key,
- 'or_addresses' : relay.or_addresses,
- 'protocols' : relay.protocols
- }
- if relay.exit_policy.is_exiting_allowed():
- tor_map['exit'].append(_map)
- else:
- tor_map['relay'].append(_map)
-with open('tor.out', 'w') as fd:
- json.dump(tor_map['relay'], fd)
-with open('tor.exit.out', 'w') as fd:
- json.dump(tor_map['exit'], fd)
-\ No newline at end of file
diff --git a/tortest.py b/tortest.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# Tor Test - Developed by acidvegas in Python (https://git.acid.vegas/proxytools)
+
+import io
+import time
+
+try:
+ import pycurl
+except ImportError:
+ raise Exception('missing required library \'pycurl\' (https://pypi.org/project/pycurl/)')
+
+try:
+ import stem.control
+except ImportError:
+ raise Exception('missing required library \'stem\' (https://pypi.org/project/stem/)')
+
+# Globals
+EXIT_FINGERPRINT = '379FB450010D17078B3766C2273303C358C3A442' # https://metrics.torproject.org/rs.html#details/379FB450010D17078B3766C2273303C358C3A442
+SOCKS_PORT = 9050
+CONNECTION_TIMEOUT = 30 # timeout before we give up on a circuit
+
+def query(url):
+ ''' Uses pycurl to fetch a site using the proxy on the SOCKS_PORT. '''
+ output = io.StringIO.StringIO()
+ query = pycurl.Curl()
+ query.setopt(pycurl.URL, url)
+ query.setopt(pycurl.PROXY, 'localhost')
+ query.setopt(pycurl.PROXYPORT, SOCKS_PORT)
+ query.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_SOCKS5_HOSTNAME)
+ query.setopt(pycurl.CONNECTTIMEOUT, CONNECTION_TIMEOUT)
+ query.setopt(pycurl.WRITEFUNCTION, output.write)
+ try:
+ query.perform()
+ return output.getvalue()
+ except pycurl.error as exc:
+ raise ValueError("Unable to reach %s (%s)" % (url, exc))
+
+def scan(controller, path):
+ ''' Test the connection to a website through the given path of relays using the given controller '''
+ circuit_id = controller.new_circuit(path, await_build = True)
+ def attach_stream(stream):
+ if stream.status == 'NEW':
+ controller.attach_stream(stream.id, circuit_id)
+ controller.add_event_listener(attach_stream, stem.control.EventType.STREAM)
+ try:
+ controller.set_conf('__LeaveStreamsUnattached', '1') # leave stream management to us
+ start_time = time.time()
+ check_page = query('https://check.torproject.org/')
+ if 'Congratulations. This browser is configured to use Tor.' not in check_page:
+ raise ValueError("Request didn't have the right content")
+ return time.time() - start_time
+ finally:
+ controller.remove_event_listener(attach_stream)
+ controller.reset_conf('__LeaveStreamsUnattached')
+
+# Main
+with stem.control.Controller.from_port(port=9056) as controller:
+ controller.authenticate('loldongs')
+ relay_fingerprints = [desc.fingerprint for desc in controller.get_network_statuses()]
+ for fingerprint in relay_fingerprints:
+ try:
+ time_taken = scan(controller, [fingerprint, EXIT_FINGERPRINT])
+ print('%s => %0.2f seconds' % (fingerprint, time_taken))
+ except Exception as exc:
+ print('%s => %s' % (fingerprint, exc))
+\ No newline at end of file
| | | | | | | |