exclude_badExits/exclude_badExits.py

824 lines
31 KiB
Python
Raw Normal View History

2022-11-07 06:40:00 +01:00
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
# https://github.com/nusenu/noContactInfo_Exit_Excluder
# https://github.com/TheSmashy/TorExitRelayExclude
"""
This extends nusenu's basic idea of using the stem library to
dynamically exclude nodes that are likely to be bad by putting them
on the ExcludeNodes or ExcludeExitNodes setting of a running Tor.
* https://github.com/nusenu/noContactInfo_Exit_Excluder
* https://github.com/TheSmashy/TorExitRelayExclude
The basic cut is to exclude Exit nodes that do not have a contact.
That can be extended to nodes that do not have an email in the contact etc.
2022-11-07 12:38:22 +01:00
"""
2022-11-08 15:15:05 +01:00
"""But there's a problem, and your Tor notice.log will tell you about it:
2022-11-07 06:40:00 +01:00
you could exclude the nodes needed to access hidden services or
2022-11-08 15:15:05 +01:00
directorues. So we need to add to the process the concept of a whitelist.
2022-11-07 06:40:00 +01:00
In addition, we may have our own blacklist of nodes we want to exclude,
or use these lists for other applications like selektor.
So we make two files that are structured in YAML:
```
2022-11-08 15:15:05 +01:00
/etc/tor/yaml/torrc-goodnodes.yaml
GoodNodes:
Relays:
IntroductionPoints:
- NODEFINGERPRINT
2022-11-07 06:40:00 +01:00
...
By default all sections of the goodnodes.yaml are used as a whitelist.
2022-11-08 15:15:05 +01:00
/etc/tor/yaml/torrc-badnodes.yaml
BadNodes:
2022-11-07 06:40:00 +01:00
ExcludeExitNodes:
BadExit:
# $0000000000000000000000000000000000000007
```
That part requires [PyYAML](https://pyyaml.org/wiki/PyYAML)
https://github.com/yaml/pyyaml/
Right now only the ExcludeExitNodes section is used by we may add ExcludeNodes
later, and by default all sub-sections of the badnodes.yaml are used as a
ExcludeExitNodes but it can be customized with the lWanted commandline arg.
The original idea has also been extended to add different conditions for
exclusion: the ```--contact``` commandline arg is a comma sep list of conditions:
* Empty - no contact info
* NoEmail - no @ sign in the contact',
More may be added later.
Because you don't want to exclude the introduction points to any onion
2022-11-16 22:00:16 +01:00
you want to connect to, ```--white_onions``` should whitelist the
2022-11-08 15:15:05 +01:00
introduction points to a comma sep list of onions, but is
2022-11-07 06:40:00 +01:00
currently broken in stem 1.8.0: see:
* https://github.com/torproject/stem/issues/96
* https://gitlab.torproject.org/legacy/trac/-/issues/25417
2022-11-08 15:15:05 +01:00
```--torrc_output``` will write the torrc ExcludeNodes configuration to a file.
Now for the final part: we lookup the Contact info of every server
that is currently in our Tor, and check it for its existence.
If it fails to provide the well-know url, we assume its a bogus
relay and add it to a list of nodes that goes on ExcludeNodes -
not just exclude Exit.
2022-11-07 06:40:00 +01:00
2022-11-08 15:15:05 +01:00
If the Contact info is good we add the list of fingerprints to add
to ExitNodes, a whitelist of relays to use as exits.
2022-11-07 06:40:00 +01:00
2022-11-16 23:15:00 +01:00
```--good_contacts``` will write the contact info as a ciiss dictionary
2022-11-07 12:38:22 +01:00
to a YAML file. If the proof is uri-rsa, the well-known file of fingerprints
2022-11-08 15:15:05 +01:00
is downloaded and the fingerprints are added on a 'fps' field we create
of that fingerprint's entry of the YAML dictionary. This file is read at the
2022-11-07 12:38:22 +01:00
beginning of the program to start with a trust database, and only new
2022-11-08 15:15:05 +01:00
contact info from new relays are added to the dictionary.
You can expect it to take an hour or two the first time this is run:
>700 domains.
2022-11-07 12:38:22 +01:00
2022-11-07 06:40:00 +01:00
For usage, do ```python3 exclude_badExits.py --help`
"""
2022-11-17 09:58:45 +01:00
import argparse
2022-11-07 06:40:00 +01:00
import os
2022-11-17 09:58:45 +01:00
import sys
2022-11-07 06:40:00 +01:00
import time
2022-11-07 12:38:22 +01:00
from io import StringIO
2022-11-07 06:40:00 +01:00
2022-11-13 05:37:30 +01:00
import stem
2022-11-17 09:58:45 +01:00
import urllib3
2022-11-08 15:15:05 +01:00
from stem import InvalidRequest
2022-11-07 12:38:22 +01:00
from stem.connection import IncorrectPassword
from stem.util.tor_tools import is_valid_fingerprint
2022-11-17 09:58:45 +01:00
from urllib3.util.ssl_match_hostname import CertificateError
# list(ipaddress._find_address_range(ipaddress.IPv4Network('172.16.0.0/12'))
2022-11-07 06:40:00 +01:00
try:
2022-11-16 22:00:16 +01:00
from ruamel.yaml import YAML
2022-11-16 23:15:00 +01:00
yaml = YAML(typ='rt')
yaml.indent(mapping=2, sequence=2)
2022-11-16 22:00:16 +01:00
safe_load = yaml.load
2022-11-07 06:40:00 +01:00
except:
yaml = None
2022-11-16 22:00:16 +01:00
if yaml is None:
try:
import yaml
safe_load = yaml.safe_load
except:
yaml = None
2022-11-07 06:40:00 +01:00
try:
2022-11-17 09:58:45 +01:00
from unbound import RR_CLASS_IN, RR_TYPE_TXT, ub_ctx
2022-11-08 15:15:05 +01:00
except:
ub_ctx = RR_TYPE_TXT = RR_CLASS_IN = None
2022-11-07 06:40:00 +01:00
global LOG
import logging
2022-11-08 15:15:05 +01:00
import warnings
2022-11-17 09:58:45 +01:00
2022-11-08 15:15:05 +01:00
warnings.filterwarnings('ignore')
2022-11-07 06:40:00 +01:00
LOG = logging.getLogger()
2022-11-17 09:58:45 +01:00
from support_onions import (bAreWeConnected, icheck_torrc, lIntroductionPoints,
oGetStemController, vwait_for_controller,
yKNOWN_NODNS, zResolveDomain)
2022-11-16 16:30:13 +01:00
from support_phantompy import vsetup_logging
2022-11-17 09:58:45 +01:00
from trustor_poc import TrustorError, idns_validate
2022-11-13 21:13:56 +01:00
from trustor_poc import oDownloadUrlUrllib3 as oDownloadUrl
2022-11-17 09:58:45 +01:00
2022-11-13 21:13:56 +01:00
LOG.info("imported HTTPSAdapter")
2022-11-09 06:43:26 +01:00
ETC_DIR = '/etc/tor/yaml'
2022-11-07 12:38:22 +01:00
aTRUST_DB = {}
2022-11-09 10:30:43 +01:00
aTRUST_DB_INDEX = {}
2022-11-13 05:37:30 +01:00
aFP_EMAIL = {}
2022-11-07 06:40:00 +01:00
sDETAILS_URL = "https://metrics.torproject.org/rs.html#details/"
# You can call this while bootstrapping
2022-11-08 15:15:05 +01:00
sEXCLUDE_EXIT_KEY = 'ExcludeNodes'
sINCLUDE_EXIT_KEY = 'ExitNodes'
sINCLUDE_GUARD_KEY = 'EntryNodes'
2022-11-07 06:40:00 +01:00
2022-11-08 15:15:05 +01:00
oBAD_NODES = {}
oBAD_ROOT = 'BadNodes'
2022-11-13 21:13:56 +01:00
oBAD_NODES[oBAD_ROOT] = {}
oBAD_NODES[oBAD_ROOT]['ExcludeNodes'] = {}
lKNOWN_NODNS = []
lMAYBE_NODNS = []
2022-11-08 15:15:05 +01:00
def lYamlBadNodes(sFile,
section=sEXCLUDE_EXIT_KEY,
lWanted=['BadExit']):
global oBAD_NODES
2022-11-13 21:13:56 +01:00
global lKNOWN_NODNS
global lMAYBE_NODNS
2022-11-14 12:59:33 +01:00
2022-11-17 09:58:45 +01:00
if not yaml:
return []
2022-11-07 06:40:00 +01:00
if os.path.exists(sFile):
with open(sFile, 'rt') as oFd:
2022-11-16 22:00:16 +01:00
oBAD_NODES = safe_load(oFd)
2022-11-08 15:15:05 +01:00
2022-11-13 21:13:56 +01:00
# BROKEN
2022-11-09 10:30:43 +01:00
# root = 'ExcludeNodes'
2022-11-08 15:15:05 +01:00
# for elt in o[oBAD_ROOT][root][section].keys():
# if lWanted and elt not in lWanted: continue
# # l += o[oBAD_ROOT][root][section][elt]
2022-11-13 21:13:56 +01:00
l = oBAD_NODES[oBAD_ROOT]['ExcludeNodes']['BadExit']
2022-11-09 10:30:43 +01:00
root = 'ExcludeDomains'
2022-11-09 19:43:54 +01:00
if root not in oBAD_NODES[oBAD_ROOT] or not oBAD_NODES[oBAD_ROOT][root]:
2022-11-16 22:00:16 +01:00
lMAYBE_NODNS = safe_load(StringIO(yKNOWN_NODNS))
2022-11-09 10:30:43 +01:00
else:
2022-11-13 21:13:56 +01:00
lMAYBE_NODNS = oBAD_NODES[oBAD_ROOT][root]
2022-11-07 06:40:00 +01:00
return l
2022-11-08 15:15:05 +01:00
oGOOD_NODES = {}
oGOOD_ROOT = 'GoodNodes'
2022-11-07 06:40:00 +01:00
def lYamlGoodNodes(sFile='/etc/tor/torrc-goodnodes.yaml'):
2022-11-08 15:15:05 +01:00
global oGOOD_NODES
2022-11-07 06:40:00 +01:00
l = []
if not yaml: return l
if os.path.exists(sFile):
with open(sFile, 'rt') as oFd:
2022-11-16 22:00:16 +01:00
o = safe_load(oFd)
2022-11-08 15:15:05 +01:00
oGOOD_NODES = o
2022-11-09 13:31:08 +01:00
if 'GuardNodes' in o[oGOOD_ROOT].keys():
2022-11-13 05:37:30 +01:00
l = o[oGOOD_ROOT]['GuardNodes']
2022-11-07 06:40:00 +01:00
# yq '.Nodes.IntroductionPoints|.[]' < /etc/tor/torrc-goodnodes.yaml
return l
2022-11-14 12:59:33 +01:00
def bdomain_is_bad(domain, fp):
2022-11-13 21:13:56 +01:00
global lKNOWN_NODNS
if domain in lKNOWN_NODNS: return True
if domain in lMAYBE_NODNS:
ip = zResolveDomain(domain)
if ip == '':
LOG.debug(f"{fp} {domain} does not resolve")
lKNOWN_NODNS.append(domain)
lMAYBE_NODNS.remove(domain)
return True
2022-11-14 12:59:33 +01:00
for elt in '@(){}$!':
if elt in domain:
LOG.warn(f"{elt} in domain {domain}")
return True
2022-11-13 21:13:56 +01:00
return False
2022-11-09 10:30:43 +01:00
tBAD_URLS = set()
2022-11-08 15:15:05 +01:00
lATS = ['abuse', 'email']
lINTS = ['ciissversion', 'uplinkbw', 'signingkeylifetime', 'memory']
2022-11-17 09:58:45 +01:00
lBOOLS = ['dnssec', 'dnsqname', 'aesni', 'autoupdate', 'dnslocalrootzone',
'sandbox', 'offlinemasterkey']
2022-11-08 15:15:05 +01:00
def aVerifyContact(a, fp, https_cafile, timeout=20, host='127.0.0.1', port=9050):
2022-11-09 10:30:43 +01:00
global tBAD_URLS
global lKNOWN_NODNS
2022-11-17 09:58:45 +01:00
# cleanups
2022-11-07 12:38:22 +01:00
for elt in lINTS:
if elt in a:
a[elt] = int(a[elt])
for elt in lBOOLS:
if elt in a:
2022-11-17 09:58:45 +01:00
if a[elt] in ['y', 'yes', 'true', 'True']:
2022-11-07 12:38:22 +01:00
a[elt] = True
else:
a[elt] = False
2022-11-08 15:15:05 +01:00
for elt in lATS:
if elt in a:
a[elt] = a[elt].replace('[]', '@')
a.update({'fps': []})
2022-11-09 06:43:26 +01:00
keys = list(a.keys())
2022-11-13 05:37:30 +01:00
if 'email' not in keys:
LOG.warn(f"{fp} 'email' not in {keys}")
a['email'] = ''
if 'ciissversion' not in keys:
aFP_EMAIL[fp] = a['email']
LOG.warn(f"{fp} 'ciissversion' not in {keys}")
a['ciissversion'] = 2
2022-11-07 12:38:22 +01:00
# test the url for fps and add it to the array
2022-11-09 06:43:26 +01:00
if 'proof' not in keys:
2022-11-13 05:37:30 +01:00
aFP_EMAIL[fp] = a['email']
2022-11-09 06:43:26 +01:00
LOG.warn(f"{fp} 'proof' not in {keys}")
2022-11-07 12:38:22 +01:00
return a
2022-11-08 15:15:05 +01:00
2022-11-09 10:30:43 +01:00
if aTRUST_DB_INDEX and fp in aTRUST_DB_INDEX.keys():
aCachedContact = aTRUST_DB_INDEX[fp]
2022-11-09 19:43:54 +01:00
if aCachedContact['email'] == a['email']:
2022-11-13 21:13:56 +01:00
LOG.info(f"{fp} in aTRUST_DB_INDEX")
2022-11-09 10:30:43 +01:00
return aCachedContact
2022-11-14 12:59:33 +01:00
2022-11-09 06:43:26 +01:00
if 'url' not in keys:
if 'uri' not in keys:
2022-11-08 15:15:05 +01:00
a['url'] = ''
2022-11-13 05:37:30 +01:00
aFP_EMAIL[fp] = a['email']
2022-11-09 06:43:26 +01:00
LOG.warn(f"{fp} url and uri not in {keys}")
2022-11-08 15:15:05 +01:00
return a
a['url'] = a['uri']
2022-11-13 05:37:30 +01:00
aFP_EMAIL[fp] = a['email']
2022-11-09 06:43:26 +01:00
LOG.debug(f"{fp} 'uri' but not 'url' in {keys}")
2022-11-08 15:15:05 +01:00
# drop through
2022-11-13 21:13:56 +01:00
c = a['url'].lstrip('https://').lstrip('http://').strip('/')
a['url'] = 'https://' +c
2022-11-14 12:59:33 +01:00
2022-11-09 06:43:26 +01:00
# domain should be a unique key for contacts
2022-11-13 21:13:56 +01:00
domain = a['url'][8:]
2022-11-14 12:59:33 +01:00
if bdomain_is_bad(domain, fp):
2022-11-13 21:13:56 +01:00
LOG.warn(f"{domain} is bad from {a['url']}")
2022-11-14 12:59:33 +01:00
LOG.debug(f"{fp} is bad from {a}")
2022-11-13 21:13:56 +01:00
return a
2022-11-16 22:00:16 +01:00
2022-11-13 21:13:56 +01:00
ip = zResolveDomain(domain)
2022-11-09 19:43:54 +01:00
if ip == '':
2022-11-13 05:37:30 +01:00
aFP_EMAIL[fp] = a['email']
LOG.debug(f"{fp} {domain} does not resolve")
lKNOWN_NODNS.append(domain)
return {}
2022-11-14 12:59:33 +01:00
2022-11-08 15:15:05 +01:00
if a['proof'] not in ['uri-rsa']:
# only support uri for now
if False and ub_ctx:
2022-11-17 09:58:45 +01:00
fp_domain = fp + '.' + domain
2022-11-08 15:15:05 +01:00
if idns_validate(fp_domain,
libunbound_resolv_file='resolv.conf',
dnssec_DS_file='dnssec-root-trust',
) == 0:
pass
LOG.warn(f"{fp} proof={a['proof']} not supported yet")
return a
2022-11-14 12:59:33 +01:00
2022-11-09 06:43:26 +01:00
LOG.debug(f"{len(keys)} contact fields for {fp}")
2022-11-17 09:58:45 +01:00
url = f"https://{domain}/.well-known/tor-relay/rsa-fingerprint.txt"
2022-11-07 12:38:22 +01:00
try:
2022-11-08 15:15:05 +01:00
LOG.debug(f"Downloading from {domain} for {fp}")
2022-11-13 21:13:56 +01:00
o = oDownloadUrl(url, https_cafile,
2022-11-09 06:43:26 +01:00
timeout=timeout, host=host, port=port)
# requests response: text "reason", "status_code"
except AttributeError as e:
LOG.exception(f"AttributeError downloading from {domain} {e}")
except CertificateError as e:
LOG.warn(f"CertificateError downloading from {domain} {e}")
2022-11-09 10:30:43 +01:00
tBAD_URLS.add(a['url'])
2022-11-09 06:43:26 +01:00
except TrustorError as e:
2022-11-13 05:37:30 +01:00
if e.args == "HTTP Errorcode 404":
aFP_EMAIL[fp] = a['email']
LOG.warn(f"TrustorError 404 from {domain} {e.args}")
else:
LOG.warn(f"TrustorError downloading from {domain} {e.args}")
2022-11-09 10:30:43 +01:00
tBAD_URLS.add(a['url'])
2022-11-17 09:58:45 +01:00
except urllib3.exceptions.MaxRetryError as e: # noqa
# maybe offline - not bad
LOG.warn(f"MaxRetryError downloading from {domain} {e}")
except (BaseException) as e:
2022-11-09 06:43:26 +01:00
LOG.error(f"Exception {type(e)} downloading from {domain} {e}")
2022-11-07 12:38:22 +01:00
else:
2022-11-13 21:13:56 +01:00
if hasattr(o, 'status'):
status_code = o.status
else:
status_code = o.status_code
if status_code >= 300:
2022-11-13 05:37:30 +01:00
aFP_EMAIL[fp] = a['email']
2022-11-13 21:13:56 +01:00
LOG.warn(f"Error from {domain} {status_code} {o.reason}")
2022-11-09 06:43:26 +01:00
# any reason retry?
2022-11-09 10:30:43 +01:00
tBAD_URLS.add(a['url'])
2022-11-09 06:43:26 +01:00
return a
2022-11-14 12:59:33 +01:00
2022-11-13 21:13:56 +01:00
if hasattr(o, 'text'):
data = o.text
else:
data = str(o.data, 'UTF-8')
l = data.upper().strip().split('\n')
LOG.debug(f"Downloaded from {domain} {len(l)} lines {len(data)} bytes")
a['modified'] = int(time.time())
2022-11-07 12:38:22 +01:00
if not l:
2022-11-09 06:43:26 +01:00
LOG.warn(f"Downloading from {domain} empty for {fp}")
2022-11-07 12:38:22 +01:00
else:
2022-11-17 09:58:45 +01:00
a['fps'] = [elt for elt in l if elt and len(elt) == 40 \
2022-11-08 15:15:05 +01:00
and not elt.startswith('#')]
2022-11-13 21:13:56 +01:00
LOG.info(f"Downloaded from {domain} {len(a['fps'])} FPs")
return a
def aParseContactYaml(contact, fp):
"""
2022-11-14 12:59:33 +01:00
See the Tor ContactInfo Information Sharing Specification v2
2022-11-13 21:13:56 +01:00
https://nusenu.github.io/ContactInfo-Information-Sharing-Specification/
"""
lelts = contact.split()
a = {}
if len(lelts) % 1 != 0:
LOG.warn(f"bad contact for {fp} odd number of components")
LOG.debug(f"{fp} {a}")
return a
key = ''
2022-11-14 12:59:33 +01:00
for elt in lelts:
2022-11-13 21:13:56 +01:00
if key == '':
key = elt
continue
a[key] = elt
key = ''
LOG.debug(f"{fp} {len(a.keys())} fields")
2022-11-07 12:38:22 +01:00
return a
def aParseContact(contact, fp):
2022-11-08 15:15:05 +01:00
"""
2022-11-14 12:59:33 +01:00
See the Tor ContactInfo Information Sharing Specification v2
2022-11-08 15:15:05 +01:00
https://nusenu.github.io/ContactInfo-Information-Sharing-Specification/
"""
2022-11-07 12:38:22 +01:00
l = [line for line in contact.strip().replace('"', '').split(' ')
if ':' in line]
LOG.debug(f"{fp} {len(l)} fields")
s = f'"{fp}":\n'
2022-11-17 09:58:45 +01:00
s += '\n'.join([f" {line}\"".replace(':', ': \"', 1)
2022-11-07 12:38:22 +01:00
for line in l])
oFd = StringIO(s)
2022-11-16 22:00:16 +01:00
a = safe_load(oFd)
2022-11-07 12:38:22 +01:00
return a
2022-11-07 06:40:00 +01:00
def oMainArgparser(_=None):
2022-11-08 15:15:05 +01:00
try:
from OpenSSL import SSL
lCAfs = SSL._CERTIFICATE_FILE_LOCATIONS
except:
lCAfs = []
CAfs = []
for elt in lCAfs:
if os.path.exists(elt):
CAfs.append(elt)
if not CAfs:
CAfs = ['']
2022-11-07 06:40:00 +01:00
2022-11-07 12:38:22 +01:00
parser = argparse.ArgumentParser(add_help=True,
epilog=__doc__)
2022-11-08 15:15:05 +01:00
parser.add_argument('--https_cafile', type=str,
help="Certificate Authority file (in PEM)",
default=CAfs[0])
2022-11-07 06:40:00 +01:00
parser.add_argument('--proxy_host', '--proxy-host', type=str,
default='127.0.0.1',
help='proxy host')
2022-11-07 12:38:22 +01:00
parser.add_argument('--proxy_port', '--proxy-port', default=9050, type=int,
2022-11-07 06:40:00 +01:00
help='proxy control port')
parser.add_argument('--proxy_ctl', '--proxy-ctl',
2022-11-09 06:43:26 +01:00
default='/run/tor/control' if os.path.exists('/run/tor/control') else 9051,
2022-11-07 12:38:22 +01:00
type=str,
help='control socket - or port')
2022-11-08 15:15:05 +01:00
parser.add_argument('--torrc',
2022-11-09 06:43:26 +01:00
default='/etc/tor/torrc-defaults',
2022-11-08 15:15:05 +01:00
type=str,
help='torrc to check for suggestions')
2022-11-09 06:43:26 +01:00
parser.add_argument('--timeout', default=60, type=int,
2022-11-08 15:15:05 +01:00
help='proxy download connect timeout')
2022-11-07 12:38:22 +01:00
2022-11-07 06:40:00 +01:00
parser.add_argument('--good_nodes', type=str,
2022-11-16 22:00:16 +01:00
default=os.path.join(ETC_DIR, 'goodnodes.yaml'),
2022-11-13 21:13:56 +01:00
help="Yaml file of good info that should not be excluded")
2022-11-07 06:40:00 +01:00
parser.add_argument('--bad_nodes', type=str,
2022-11-16 22:00:16 +01:00
default=os.path.join(ETC_DIR, 'badnodes.yaml'),
2022-11-07 06:40:00 +01:00
help="Yaml file of bad nodes that should also be excluded")
parser.add_argument('--contact', type=str, default='Empty,NoEmail',
help="comma sep list of conditions - Empty,NoEmail")
2022-11-08 15:15:05 +01:00
parser.add_argument('--bad_contacts', type=str,
2022-11-09 13:31:08 +01:00
default=os.path.join(ETC_DIR, 'badcontacts.yaml'),
2022-11-08 15:15:05 +01:00
help="Yaml file of bad contacts that bad FPs are using")
2022-11-14 12:59:33 +01:00
2022-11-17 09:58:45 +01:00
parser.add_argument('--strict_nodes', type=int, default=0, choices=[0, 1],
2022-11-13 05:37:30 +01:00
help="Set StrictNodes: 1 is less anonymous but more secure, although some sites may be unreachable")
2022-11-07 06:40:00 +01:00
parser.add_argument('--wait_boot', type=int, default=120,
help="Seconds to wait for Tor to booststrap")
2022-11-13 21:13:56 +01:00
parser.add_argument('--points_timeout', type=int, default=0,
help="Timeout for getting introduction points - must be long >120sec. 0 means disabled looking for IPs")
2022-11-14 12:59:33 +01:00
parser.add_argument('--log_level', type=int, default=20,
2022-11-07 06:40:00 +01:00
help="10=debug 20=info 30=warn 40=error")
parser.add_argument('--bad_sections', type=str,
2022-11-09 13:31:08 +01:00
default='MyBadExit',
2022-11-08 15:15:05 +01:00
help="sections of the badnodes.yaml to use, comma separated, '' BROKEN")
2022-11-16 22:00:16 +01:00
parser.add_argument('--white_onions', type=str,
2022-11-07 06:40:00 +01:00
default='',
help="comma sep. list of onions to whitelist their introduction points - BROKEN")
2022-11-16 16:30:13 +01:00
parser.add_argument('--torrc_output', type=str,
default=os.path.join(ETC_DIR, 'torrc.new'),
2022-11-07 06:40:00 +01:00
help="Write the torrc configuration to a file")
2022-11-16 23:15:00 +01:00
parser.add_argument('--good_contacts', type=str, default=os.path.join(ETC_DIR, 'goodcontacts.yaml'),
2022-11-07 12:38:22 +01:00
help="Write the proof data of the included nodes to a YAML file")
2022-11-07 06:40:00 +01:00
return parser
2022-11-17 09:58:45 +01:00
def vwrite_badnodes(oargs, oBAD_NODES, slen):
if oargs.bad_nodes:
tmp = oargs.bad_nodes +'.tmp'
bak = oargs.bad_nodes +'.bak'
2022-11-08 15:15:05 +01:00
with open(tmp, 'wt') as oFYaml:
2022-11-16 23:15:00 +01:00
yaml.dump(oBAD_NODES, oFYaml)
2022-11-17 09:58:45 +01:00
LOG.info(f"Wrote {slen} to {oargs.bad_nodes}")
2022-11-08 15:15:05 +01:00
oFYaml.close()
2022-11-17 09:58:45 +01:00
if os.path.exists(oargs.bad_nodes):
os.rename(oargs.bad_nodes, bak)
os.rename(tmp, oargs.bad_nodes)
def vwrite_goodnodes(oargs, oGOOD_NODES, ilen):
if oargs.good_nodes:
tmp = oargs.good_nodes +'.tmp'
bak = oargs.good_nodes +'.bak'
2022-11-08 15:15:05 +01:00
with open(tmp, 'wt') as oFYaml:
2022-11-16 23:15:00 +01:00
yaml.dump(oGOOD_NODES, oFYaml)
2022-11-17 09:58:45 +01:00
LOG.info(f"Wrote {ilen} good relays to {oargs.good_nodes}")
2022-11-08 15:15:05 +01:00
oFYaml.close()
2022-11-17 09:58:45 +01:00
if os.path.exists(oargs.good_nodes):
os.rename(oargs.good_nodes, bak)
os.rename(tmp, oargs.good_nodes)
2022-11-14 12:59:33 +01:00
2022-11-07 06:40:00 +01:00
def iMain(lArgs):
2022-11-09 19:43:54 +01:00
global aTRUST_DB
global aTRUST_DB_INDEX
2022-11-13 05:37:30 +01:00
global oBAD_NODES
2022-11-14 12:59:33 +01:00
global oGOOD_NODES
2022-11-09 19:43:54 +01:00
global lKNOWN_NODNS
2022-11-07 06:40:00 +01:00
parser = oMainArgparser()
2022-11-17 09:58:45 +01:00
oargs = parser.parse_args(lArgs)
2022-11-07 06:40:00 +01:00
2022-11-17 09:58:45 +01:00
vsetup_logging(oargs.log_level)
2022-11-08 15:15:05 +01:00
if bAreWeConnected() is False:
raise SystemExit("we are not connected")
2022-11-17 09:58:45 +01:00
sFile = oargs.torrc
2022-11-08 15:15:05 +01:00
if sFile and os.path.exists(sFile):
2022-11-17 09:58:45 +01:00
icheck_torrc(sFile, oargs)
2022-11-07 12:38:22 +01:00
2022-11-13 05:37:30 +01:00
twhitelist_set = set()
2022-11-17 09:58:45 +01:00
sFile = oargs.good_contacts
2022-11-07 12:38:22 +01:00
if sFile and os.path.exists(sFile):
2022-11-08 15:15:05 +01:00
try:
with open(sFile, 'rt') as oFd:
2022-11-16 22:00:16 +01:00
aTRUST_DB = safe_load(oFd)
2022-11-13 05:37:30 +01:00
LOG.info(f"{len(aTRUST_DB.keys())} trusted contacts from {sFile}")
2022-11-09 10:30:43 +01:00
# reverse lookup of fps to contacts
# but...
2022-11-17 09:58:45 +01:00
for (k, v,) in aTRUST_DB.items():
2022-11-13 05:37:30 +01:00
if 'modified' not in v.keys():
2022-11-13 21:13:56 +01:00
v['modified'] = int(time.time())
2022-11-09 10:30:43 +01:00
aTRUST_DB_INDEX[k] = v
if 'fps' in aTRUST_DB[k].keys():
for fp in aTRUST_DB[k]['fps']:
2022-11-13 05:37:30 +01:00
if fp in aTRUST_DB_INDEX:
continue
2022-11-09 10:30:43 +01:00
aTRUST_DB_INDEX[fp] = v
2022-11-13 05:37:30 +01:00
LOG.info(f"{len(aTRUST_DB_INDEX.keys())} good relays from {sFile}")
2022-11-14 12:59:33 +01:00
2022-11-09 10:30:43 +01:00
except Exception as e:
2022-11-09 19:43:54 +01:00
LOG.exception(f"Error reading YAML TrustDB {sFile} {e}")
2022-11-07 12:38:22 +01:00
2022-11-17 09:58:45 +01:00
if os.path.exists(oargs.proxy_ctl):
controller = oGetStemController(log_level=oargs.log_level, sock_or_pair=oargs.proxy_ctl)
2022-11-07 12:38:22 +01:00
else:
2022-11-17 09:58:45 +01:00
port =int(oargs.proxy_ctl)
controller = oGetStemController(port=port)
2022-11-08 15:15:05 +01:00
2022-11-17 09:58:45 +01:00
vwait_for_controller(controller, oargs.wait_boot)
2022-11-08 15:15:05 +01:00
2022-11-17 09:58:45 +01:00
if oargs.good_contacts:
good_contacts_tmp = oargs.good_contacts + '.tmp'
2022-11-08 15:15:05 +01:00
2022-11-07 06:40:00 +01:00
elt = controller.get_conf('UseMicrodescriptors')
2022-11-17 09:58:45 +01:00
if elt != '0':
2022-11-08 15:15:05 +01:00
LOG.error('"UseMicrodescriptors 0" is required in your /etc/tor/torrc. Exiting.')
2022-11-07 12:38:22 +01:00
controller.set_conf('UseMicrodescriptors', 0)
# does it work dynamically?
2022-11-08 15:15:05 +01:00
return 2
elt = controller.get_conf(sEXCLUDE_EXIT_KEY)
2022-11-07 06:40:00 +01:00
if elt and elt != '{??}':
2022-11-08 15:15:05 +01:00
LOG.warn(f"{sEXCLUDE_EXIT_KEY} is in use already")
2022-11-07 06:40:00 +01:00
2022-11-17 09:58:45 +01:00
twhitelist_set.update(set(lYamlGoodNodes(oargs.good_nodes)))
LOG.info(f"lYamlGoodNodes {len(twhitelist_set)} GuardNodes from {oargs.good_nodes}")
2022-11-08 15:15:05 +01:00
2022-11-13 05:37:30 +01:00
global oGOOD_NODES
t = set()
if 'IntroductionPoints' in oGOOD_NODES[oGOOD_ROOT]['Relays'].keys():
t = set(oGOOD_NODES[oGOOD_ROOT]['Relays']['IntroductionPoints'])
2022-11-16 22:00:16 +01:00
w = set()
if 'Services' in oGOOD_NODES[oGOOD_ROOT].keys():
# 'Onions' can I use the IntroductionPoints for Services too?
# w = set(oGOOD_NODES[oGOOD_ROOT]['Services'])
pass
if 'Onions' in oGOOD_NODES[oGOOD_ROOT].keys():
# Provides the descriptor for a hidden service. The **address** is the
2022-11-17 09:58:45 +01:00
# '.onion' address of the hidden service
2022-11-16 22:00:16 +01:00
w = set(oGOOD_NODES[oGOOD_ROOT]['Onions'])
2022-11-17 09:58:45 +01:00
if oargs.white_onions:
w.update(oargs.white_onions.split(','))
if oargs.points_timeout > 0:
2022-11-13 21:13:56 +01:00
LOG.info(f"{len(w)} services will be checked from IntroductionPoints")
2022-11-17 09:58:45 +01:00
t.update(lIntroductionPoints(controller, w, itimeout=oargs.points_timeout))
2022-11-13 05:37:30 +01:00
if len(t) > 0:
2022-11-13 21:13:56 +01:00
LOG.info(f"IntroductionPoints {len(t)} relays from {len(w)} services")
2022-11-13 05:37:30 +01:00
twhitelist_set.update(t)
texclude_set = set()
2022-11-17 09:58:45 +01:00
if oargs.bad_nodes and os.path.exists(oargs.bad_nodes):
if False and oargs.bad_sections:
2022-11-08 15:15:05 +01:00
# BROKEN
2022-11-17 09:58:45 +01:00
sections = oargs.bad_sections.split(',')
texclude_set = set(lYamlBadNodes(oargs.bad_nodes,
2022-11-08 15:15:05 +01:00
lWanted=sections,
2022-11-13 05:37:30 +01:00
section=sEXCLUDE_EXIT_KEY))
2022-11-14 12:59:33 +01:00
LOG.info(f"Preloaded {len(texclude_set)} bad fps")
2022-11-07 06:40:00 +01:00
2022-11-13 05:37:30 +01:00
ttrust_db_index = aTRUST_DB_INDEX.keys()
2022-11-14 12:59:33 +01:00
tdns_urls = set()
2022-11-08 15:15:05 +01:00
iFakeContact = 0
2022-11-13 21:13:56 +01:00
iTotalContacts = 0
2022-11-08 15:15:05 +01:00
aBadContacts = {}
2022-11-14 12:59:33 +01:00
2022-11-17 09:58:45 +01:00
lConds = oargs.contact.split(',')
2022-11-09 06:43:26 +01:00
iR = 0
2022-11-14 12:59:33 +01:00
2022-11-09 19:43:54 +01:00
relays = controller.get_server_descriptors()
2022-11-07 06:40:00 +01:00
for relay in relays:
2022-11-09 06:43:26 +01:00
iR += 1
2022-11-07 12:38:22 +01:00
if not is_valid_fingerprint(relay.fingerprint):
LOG.warn('Invalid Fingerprint: %s' % relay.fingerprint)
continue
2022-11-08 15:15:05 +01:00
relay.fingerprint = relay.fingerprint.upper()
2022-11-14 12:59:33 +01:00
sofar = f"G:{len(aTRUST_DB.keys())} U:{len(tdns_urls)} F:{iFakeContact} BF:{len(texclude_set)} GF:{len(ttrust_db_index)} TC:{iTotalContacts} #{iR}"
2022-11-08 15:15:05 +01:00
if not relay.exit_policy.is_exiting_allowed():
if sEXCLUDE_EXIT_KEY == 'ExcludeNodes':
2022-11-13 21:13:56 +01:00
pass # LOG.debug(f"{relay.fingerprint} not an exit {sofar}")
2022-11-08 15:15:05 +01:00
else:
2022-11-13 21:13:56 +01:00
pass # LOG.warn(f"{relay.fingerprint} not an exit {sofar}")
2022-11-08 15:15:05 +01:00
# continue
2022-11-14 12:59:33 +01:00
2022-11-13 05:37:30 +01:00
# great contact had good fps and we are in them
if relay.fingerprint in aTRUST_DB_INDEX.keys():
# a cached entry
2022-11-08 15:15:05 +01:00
continue
2022-11-09 19:43:54 +01:00
if type(relay.contact) == bytes:
# dunno
2022-11-09 06:43:26 +01:00
relay.contact = str(relay.contact, 'UTF-8')
2022-11-14 12:59:33 +01:00
2022-11-09 19:43:54 +01:00
if ('Empty' in lConds and not relay.contact) or \
2022-11-17 09:58:45 +01:00
('NoEmail' in lConds and relay.contact and 'email:' not in relay.contact):
2022-11-13 05:37:30 +01:00
texclude_set.add(relay.fingerprint)
2022-11-07 12:38:22 +01:00
continue
2022-11-14 12:59:33 +01:00
2022-11-17 09:58:45 +01:00
if not relay.contact or 'ciissversion:' not in relay.contact:
2022-11-09 19:43:54 +01:00
# should be unreached 'Empty' should always be in lConds
continue
2022-11-13 21:13:56 +01:00
iTotalContacts += 1
2022-11-14 12:59:33 +01:00
fp = relay.fingerprint
2022-11-17 09:58:45 +01:00
if relay.contact and 'url:' not in relay.contact:
2022-11-14 12:59:33 +01:00
LOG.info(f"{fp} skipping bad contact - no url: {sofar}")
LOG.debug(f"{fp} {relay.contact} {sofar}")
texclude_set.add(fp)
2022-11-13 21:13:56 +01:00
continue
2022-11-14 12:59:33 +01:00
2022-11-09 19:43:54 +01:00
c = relay.contact.lower()
2022-11-13 05:37:30 +01:00
# first rough cut
2022-11-09 19:43:54 +01:00
i = c.find('url:')
2022-11-13 21:13:56 +01:00
if i >=0:
2022-11-17 09:58:45 +01:00
c = c[i + 4:]
2022-11-09 19:43:54 +01:00
i = c.find(' ')
if i >=0: c = c[:i]
2022-11-13 21:13:56 +01:00
c = c.lstrip('https://').lstrip('http://').strip('/')
2022-11-13 05:37:30 +01:00
i = c.find('/')
if i >=0: c = c[:i]
domain = c
2022-11-14 12:59:33 +01:00
if domain and bdomain_is_bad(domain, fp):
LOG.info(f"{fp} skipping bad {domain} {sofar}")
LOG.debug(f"{fp} {relay.contact} {sofar}")
texclude_set.add(fp)
2022-11-13 05:37:30 +01:00
continue
if domain:
2022-11-13 21:13:56 +01:00
ip = zResolveDomain(domain)
2022-11-13 05:37:30 +01:00
if not ip:
2022-11-14 12:59:33 +01:00
LOG.warn(f"{fp} {domain} did not resolve {sofar}")
texclude_set.add(fp)
2022-11-13 21:13:56 +01:00
lKNOWN_NODNS.append(domain)
2022-11-13 05:37:30 +01:00
iFakeContact += 1
continue
2022-11-14 12:59:33 +01:00
if 'dns-rsa' in relay.contact.lower():
target = f"{relay.fingerprint}.{domain}"
2022-11-13 05:37:30 +01:00
LOG.info(f"skipping 'dns-rsa' {target} {sofar}")
2022-11-14 12:59:33 +01:00
tdns_urls.add(target)
2022-11-09 19:43:54 +01:00
elif 'proof:uri-rsa' in relay.contact.lower():
2022-11-07 12:38:22 +01:00
a = aParseContact(relay.contact, relay.fingerprint)
2022-11-08 15:15:05 +01:00
if not a:
LOG.warn(f"{relay.fingerprint} did not parse {sofar}")
2022-11-13 05:37:30 +01:00
texclude_set.add(relay.fingerprint)
2022-11-08 15:15:05 +01:00
continue
2022-11-09 10:30:43 +01:00
if 'url' in a and a['url']:
if a['url'] in tBAD_URLS:
# The fp is using a contact with a URL we know is bad
LOG.info(f"{relay.fingerprint} skipping in tBAD_URLS {a['url']} {sofar}")
2022-11-13 21:13:56 +01:00
LOG.debug(f"{relay.fingerprint} {a} {sofar}")
2022-11-13 05:37:30 +01:00
iFakeContact += 1
texclude_set.add(relay.fingerprint)
2022-11-09 10:30:43 +01:00
continue
domain = a['url'].replace('https://', '').replace('http://', '')
if domain in lKNOWN_NODNS:
# The fp is using a contact with a URL we know is bogus
LOG.info(f"{relay.fingerprint} skipping in lKNOWN_NODNS {a['url']} {sofar}")
2022-11-13 21:13:56 +01:00
LOG.debug(f"{relay.fingerprint} {a} {sofar}")
2022-11-13 05:37:30 +01:00
iFakeContact += 1
texclude_set.add(relay.fingerprint)
2022-11-09 10:30:43 +01:00
continue
2022-11-14 12:59:33 +01:00
2022-11-08 15:15:05 +01:00
b = aVerifyContact(list(a.values())[0],
relay.fingerprint,
2022-11-17 09:58:45 +01:00
oargs.https_cafile,
timeout=oargs.timeout,
host=oargs.proxy_host,
port=oargs.proxy_port)
# need to skip urllib3.exceptions.MaxRetryError
if not b or 'fps' not in b or not b['fps'] or not b['url']:
2022-11-09 06:43:26 +01:00
LOG.warn(f"{relay.fingerprint} did NOT VERIFY {sofar}")
2022-11-13 21:13:56 +01:00
LOG.debug(f"{relay.fingerprint} {b} {sofar}")
2022-11-08 15:15:05 +01:00
# If it's giving contact info that doesnt check out
# it could be a bad exit with fake contact info
2022-11-13 05:37:30 +01:00
texclude_set.add(relay.fingerprint)
2022-11-08 15:15:05 +01:00
aBadContacts[relay.fingerprint] = b
2022-11-07 12:38:22 +01:00
continue
2022-11-14 12:59:33 +01:00
2022-11-08 15:15:05 +01:00
if relay.fingerprint not in b['fps']:
2022-11-09 06:43:26 +01:00
LOG.warn(f"{relay.fingerprint} the FP IS NOT in the list of fps {sofar}")
2022-11-08 15:15:05 +01:00
# assume a fp is using a bogus contact
2022-11-13 05:37:30 +01:00
texclude_set.add(relay.fingerprint)
2022-11-08 15:15:05 +01:00
iFakeContact += 1
aBadContacts[relay.fingerprint] = b
continue
2022-11-09 10:30:43 +01:00
2022-11-08 15:15:05 +01:00
LOG.info(f"{relay.fingerprint} verified {b['url']} {sofar}")
# add our contact info to the trustdb
2022-11-13 05:37:30 +01:00
aTRUST_DB[relay.fingerprint] = b
for elt in b['fps']:
aTRUST_DB_INDEX[elt] = b
2022-11-17 09:58:45 +01:00
if oargs.good_contacts and oargs.log_level <= 20:
2022-11-08 15:15:05 +01:00
# as we go along then clobber
2022-11-16 23:15:00 +01:00
with open(good_contacts_tmp, 'wt') as oFYaml:
yaml.dump(aTRUST_DB, oFYaml)
2022-11-08 15:15:05 +01:00
oFYaml.close()
2022-11-14 12:59:33 +01:00
2022-11-13 21:13:56 +01:00
LOG.info(f"Filtered {len(twhitelist_set)} whitelisted relays")
2022-11-13 05:37:30 +01:00
texclude_set = texclude_set.difference(twhitelist_set)
2022-11-14 12:59:33 +01:00
# accept the dns-rsa urls for now until we test them
texclude_set = texclude_set.difference(tdns_urls)
2022-11-13 21:13:56 +01:00
LOG.info(f"{len(list(aTRUST_DB.keys()))} good contacts out of {iTotalContacts}")
2022-11-08 15:15:05 +01:00
2022-11-17 09:58:45 +01:00
if oargs.torrc_output and texclude_set:
with open(oargs.torrc_output, 'wt') as oFTorrc:
2022-11-13 05:37:30 +01:00
oFTorrc.write(f"{sEXCLUDE_EXIT_KEY} {','.join(texclude_set)}\n")
oFTorrc.write(f"{sINCLUDE_EXIT_KEY} {','.join(aTRUST_DB_INDEX.keys())}\n")
2022-11-14 12:59:33 +01:00
oFTorrc.write(f"{sINCLUDE_GUARD_KEY} {','.join(oGOOD_NODES[oGOOD_ROOT]['GuardNodes'])}\n")
2022-11-17 09:58:45 +01:00
LOG.info(f"Wrote tor configuration to {oargs.torrc_output}")
2022-11-08 15:15:05 +01:00
oFTorrc.close()
2022-11-17 09:58:45 +01:00
if oargs.bad_contacts and aBadContacts:
2022-11-08 15:15:05 +01:00
# for later analysis
2022-11-17 09:58:45 +01:00
with open(oargs.bad_contacts, 'wt') as oFYaml:
2022-11-16 23:15:00 +01:00
yaml.dump(aBadContacts, oFYaml)
oFYaml.close()
2022-11-17 09:58:45 +01:00
if oargs.good_contacts != '' and aTRUST_DB:
2022-11-16 23:15:00 +01:00
with open(good_contacts_tmp, 'wt') as oFYaml:
yaml.dump(aTRUST_DB, oFYaml)
2022-11-08 15:15:05 +01:00
oFYaml.close()
2022-11-17 09:58:45 +01:00
if os.path.exists(oargs.good_contacts):
bak = oargs.good_contacts +'.bak'
os.rename(oargs.good_contacts, bak)
os.rename(good_contacts_tmp, oargs.good_contacts)
LOG.info(f"Wrote {len(list(aTRUST_DB.keys()))} good contact details to {oargs.good_contacts}")
2022-11-08 15:15:05 +01:00
2022-11-13 05:37:30 +01:00
oBAD_NODES[oBAD_ROOT]['ExcludeNodes']['BadExit'] = list(texclude_set)
2022-11-09 13:31:08 +01:00
oBAD_NODES[oBAD_ROOT]['ExcludeDomains'] = lKNOWN_NODNS
2022-11-17 09:58:45 +01:00
vwrite_badnodes(oargs, oBAD_NODES, str(len(texclude_set)))
2022-11-14 12:59:33 +01:00
2022-11-13 05:37:30 +01:00
oGOOD_NODES['GoodNodes']['Relays']['ExitNodes'] = list(aTRUST_DB_INDEX.keys())
# GuardNodes are readonl
2022-11-17 09:58:45 +01:00
vwrite_goodnodes(oargs, oGOOD_NODES, len(aTRUST_DB_INDEX.keys()))
2022-11-08 15:15:05 +01:00
retval = 0
try:
logging.getLogger('stem').setLevel(30)
2022-11-09 06:43:26 +01:00
try:
2022-11-13 05:37:30 +01:00
if texclude_set:
LOG.info(f"{sEXCLUDE_EXIT_KEY} {len(texclude_set)} net bad exit relays")
controller.set_conf(sEXCLUDE_EXIT_KEY, texclude_set)
2022-11-09 06:43:26 +01:00
2022-11-17 09:58:45 +01:00
except stem.SocketClosed as e: # noqa
2022-11-13 05:37:30 +01:00
LOG.error(f"Failed setting {sEXCLUDE_EXIT_KEY} bad exit relays in Tor")
2022-11-09 06:43:26 +01:00
retval += 1
try:
2022-11-13 05:37:30 +01:00
if aTRUST_DB_INDEX.keys():
LOG.info(f"{sINCLUDE_EXIT_KEY} {len(aTRUST_DB_INDEX.keys())} good relays")
controller.set_conf(sINCLUDE_EXIT_KEY, aTRUST_DB_INDEX.keys())
2022-11-17 09:58:45 +01:00
except stem.SocketClosed as e: # noqa
2022-11-09 06:43:26 +01:00
LOG.error(f"Failed setting {sINCLUDE_EXIT_KEY} good exit nodes in Tor")
retval += 1
try:
2022-11-13 05:37:30 +01:00
if 'GuardNodes' in oGOOD_NODES[oGOOD_ROOT].keys():
LOG.info(f"{sINCLUDE_GUARD_KEY} {len(oGOOD_NODES[oGOOD_ROOT]['GuardNodes'])} guard nodes")
# FixMe for now override StrictNodes it may be unusable otherwise
controller.set_conf(sINCLUDE_GUARD_KEY,
oGOOD_NODES[oGOOD_ROOT]['GuardNodes'])
cur = controller.get_conf('StrictNodes')
2022-11-17 09:58:45 +01:00
if oargs.strict_nodes and int(cur) != oargs.strict_nodes:
LOG.info(f"OVERRIDING StrictNodes to {oargs.strict_nodes}")
controller.set_conf('StrictNodes', oargs.strict_nodes)
2022-11-13 05:37:30 +01:00
else:
LOG.info(f"StrictNodes is set to {cur}")
2022-11-17 09:58:45 +01:00
except stem.SocketClosed as e: # noqa
2022-11-09 06:43:26 +01:00
LOG.errro(f"Failed setting {sINCLUDE_EXIT_KEY} good exit nodes in Tor")
retval += 1
2022-11-09 10:30:43 +01:00
2022-11-08 15:15:05 +01:00
except InvalidRequest as e:
# Unacceptable option value: Invalid router list.
LOG.error(str(e))
retval = 1
return retval
except KeyboardInterrupt:
return 0
except Exception as e:
LOG.exception(str(e))
retval = 2
return retval
finally:
# wierd we are getting stem errors during the final return
# with a traceback that doesnt correspond to any real flow
# File "/usr/lib/python3.9/site-packages/stem/control.py", line 2474, in set_conf
# self.set_options({param: value}, False)
logging.getLogger('stem').setLevel(40)
try:
for elt in controller._event_listeners:
controller.remove_event_listener(elt)
controller.close()
except Exception as e:
LOG.warn(str(e))
2022-11-17 09:58:45 +01:00
2022-11-16 23:15:00 +01:00
sys.stdout.write("dns-rsa domains:\n" +'\n'.join(tdns_urls) +'\n')
return retval
2022-11-07 06:40:00 +01:00
if __name__ == '__main__':
try:
i = iMain(sys.argv[1:])
2022-11-07 12:38:22 +01:00
except IncorrectPassword as e:
LOG.error(e)
i = 1
2022-11-08 15:15:05 +01:00
except KeyboardInterrupt:
i = 0
2022-11-07 06:40:00 +01:00
except Exception as e:
LOG.exception(e)
2022-11-08 15:15:05 +01:00
i = 2
2022-11-07 06:40:00 +01:00
sys.exit(i)