Move the ability to use Tor vs Meek into the CensorshipCircumvention class so that we can use those endpoints over Tor elsewhere later

This commit is contained in:
Miguel Jacq 2021-11-27 10:35:25 +11:00
parent 06a3599fe1
commit 55c8ada6ef
No known key found for this signature in database
GPG key ID: EEA4341C6D97A0B6
2 changed files with 60 additions and 57 deletions

View file

@ -25,21 +25,46 @@ from .meek import MeekNotRunning
class CensorshipCircumvention(object): class CensorshipCircumvention(object):
""" """
Connect to the Tor Moat APIs to retrieve censorship Connect to the Tor Moat APIs to retrieve censorship
circumvention recommendations, over the Meek client. circumvention recommendations or the latest bridges.
We support reaching this API over Tor, or Meek
(domain fronting) if Tor is not connected.
""" """
def __init__(self, common, meek, domain_fronting=True): def __init__(self, common, meek=None, onion=None):
""" """
Set up the CensorshipCircumvention object to hold Set up the CensorshipCircumvention object to hold
common and meek objects. common and meek objects.
""" """
self.common = common self.common = common
self.meek = meek
self.common.log("CensorshipCircumvention", "__init__") self.common.log("CensorshipCircumvention", "__init__")
self.api_proxies = {}
# Bail out if we requested domain fronting but we can't use meek if meek:
if domain_fronting and not self.meek.meek_proxies: self.meek = meek
raise MeekNotRunning() if not self.meek.meek_proxies:
raise MeekNotRunning()
else:
self.common.log(
"CensorshipCircumvention",
"__init__",
"Using Meek with CensorShipCircumvention API",
)
self.api_proxies = self.meek.meek_proxies
if onion:
self.onion = onion
if not self.onion.is_authenticated:
return False
else:
self.common.log(
"CensorshipCircumvention",
"__init__",
"Using Tor with CensorShipCircumvention API",
)
(socks_address, socks_port) = self.onion.get_tor_socks_port()
self.api_proxies = {
"http": f"socks5h://{socks_address}:{socks_port}",
"https": f"socks5h://{socks_address}:{socks_port}",
}
def request_map(self, country=False): def request_map(self, country=False):
""" """
@ -52,6 +77,8 @@ class CensorshipCircumvention(object):
Note that this API endpoint doesn't return actual bridges, Note that this API endpoint doesn't return actual bridges,
it just returns the recommended bridge type countries. it just returns the recommended bridge type countries.
""" """
if not self.api_proxies:
return False
endpoint = "https://bridges.torproject.org/moat/circumvention/map" endpoint = "https://bridges.torproject.org/moat/circumvention/map"
data = {} data = {}
if country: if country:
@ -61,7 +88,7 @@ class CensorshipCircumvention(object):
endpoint, endpoint,
json=data, json=data,
headers={"Content-Type": "application/vnd.api+json"}, headers={"Content-Type": "application/vnd.api+json"},
proxies=self.meek.meek_proxies, proxies=self.api_proxies,
) )
if r.status_code != 200: if r.status_code != 200:
self.common.log( self.common.log(
@ -95,6 +122,8 @@ class CensorshipCircumvention(object):
Optionally, a list of transports can be specified in order to Optionally, a list of transports can be specified in order to
return recommended settings for just that transport type. return recommended settings for just that transport type.
""" """
if not self.api_proxies:
return False
endpoint = "https://bridges.torproject.org/moat/circumvention/settings" endpoint = "https://bridges.torproject.org/moat/circumvention/settings"
data = {} data = {}
if country: if country:
@ -105,7 +134,7 @@ class CensorshipCircumvention(object):
endpoint, endpoint,
json=data, json=data,
headers={"Content-Type": "application/vnd.api+json"}, headers={"Content-Type": "application/vnd.api+json"},
proxies=self.meek.meek_proxies, proxies=self.api_proxies,
) )
if r.status_code != 200: if r.status_code != 200:
self.common.log( self.common.log(
@ -142,11 +171,13 @@ class CensorshipCircumvention(object):
""" """
Retrieves the list of built-in bridges from the Tor Project. Retrieves the list of built-in bridges from the Tor Project.
""" """
if not self.api_proxies:
return False
endpoint = "https://bridges.torproject.org/moat/circumvention/builtin" endpoint = "https://bridges.torproject.org/moat/circumvention/builtin"
r = requests.post( r = requests.post(
endpoint, endpoint,
headers={"Content-Type": "application/vnd.api+json"}, headers={"Content-Type": "application/vnd.api+json"},
proxies=self.meek.meek_proxies, proxies=self.api_proxies,
) )
if r.status_code != 200: if r.status_code != 200:
self.common.log( self.common.log(

View file

@ -914,7 +914,8 @@ class Onion(object):
Use the CensorshipCircumvention API to fetch the latest built-in bridges Use the CensorshipCircumvention API to fetch the latest built-in bridges
and update them in settings. and update them in settings.
""" """
got_builtin_bridges = False builtin_bridges = False
meek = None
# Try obtaining bridges over Tor, if we're connected to it. # Try obtaining bridges over Tor, if we're connected to it.
if self.is_authenticated: if self.is_authenticated:
self.common.log( self.common.log(
@ -922,43 +923,14 @@ class Onion(object):
"update_builtin_bridges", "update_builtin_bridges",
"Updating the built-in bridges. Trying over Tor first", "Updating the built-in bridges. Trying over Tor first",
) )
(socks_address, socks_port) = self.get_tor_socks_port() self.censorship_circumvention = CensorshipCircumvention(
tor_proxies = { self.common, None, self
"http": f"socks5h://{socks_address}:{socks_port}",
"https": f"socks5h://{socks_address}:{socks_port}",
}
# Request a bridge
r = requests.post(
"https://bridges.torproject.org/moat/circumvention/builtin",
headers={"Content-Type": "application/vnd.api+json"},
proxies=tor_proxies,
) )
if r.status_code != 200: builtin_bridges = self.censorship_circumvention.request_builtin_bridges()
self.common.log(
"Onion",
"update_builtin_bridges",
f"Trying over Tor failed: status_code={r.status_code}",
)
try: if not builtin_bridges:
builtin_bridges = r.json() # Tor was not running or it failed to hit the Tor API.
if "errors" in builtin_bridges: # Fall back to using Meek (domain-fronting).
self.common.log(
"Onion",
"update_builtin_bridges",
f"Trying over Tor failed: errors={builtin_bridges['errors']}",
)
else:
got_builtin_bridges = builtin_bridges
except Exception as e:
self.common.log(
"Onion",
"update_builtin_bridges",
f"Hit exception when trying over Tor: {e}",
)
if not got_builtin_bridges:
# Fall back to using Meek, without Tor
self.common.log( self.common.log(
"Onion", "Onion",
"update_builtin_bridges", "update_builtin_bridges",
@ -966,35 +938,35 @@ class Onion(object):
) )
meek = Meek(self.common) meek = Meek(self.common)
meek.start() meek.start()
self.censorship_circumvention = CensorshipCircumvention(self.common, meek) self.censorship_circumvention = CensorshipCircumvention(
got_builtin_bridges = ( self.common, meek, None
self.censorship_circumvention.request_builtin_bridges()
) )
builtin_bridges = self.censorship_circumvention.request_builtin_bridges()
meek.cleanup() meek.cleanup()
# If we got to this point, we have bridges if builtin_bridges:
if got_builtin_bridges: # If we got to this point, we have bridges
self.common.log( self.common.log(
"Onion", "Onion",
"update_builtin_bridges", "update_builtin_bridges",
f"Obtained bridges: {got_builtin_bridges}", f"Obtained bridges: {builtin_bridges}",
) )
if got_builtin_bridges["meek"]: if builtin_bridges["meek"]:
# Meek bridge needs to be defined as "meek_lite", not "meek", # Meek bridge needs to be defined as "meek_lite", not "meek",
# for it to work with obfs4proxy. # for it to work with obfs4proxy.
# We also refer to this bridge type as 'meek-azure' in our settings. # We also refer to this bridge type as 'meek-azure' in our settings.
# So first, rename the key in the dict # So first, rename the key in the dict
got_builtin_bridges["meek-azure"] = got_builtin_bridges.pop("meek") builtin_bridges["meek-azure"] = builtin_bridges.pop("meek")
new_meek_bridges = [] new_meek_bridges = []
# Now replace the values. They also need the url/front params appended # Now replace the values. They also need the url/front params appended
for item in got_builtin_bridges["meek-azure"]: for item in builtin_bridges["meek-azure"]:
newline = item.replace("meek", "meek_lite") newline = item.replace("meek", "meek_lite")
new_meek_bridges.append( new_meek_bridges.append(
f"{newline} url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com" f"{newline} url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com"
) )
got_builtin_bridges["meek-azure"] = new_meek_bridges builtin_bridges["meek-azure"] = new_meek_bridges
# Save the new settings # Save the new settings
self.settings.set("bridges_builtin", got_builtin_bridges) self.settings.set("bridges_builtin", builtin_bridges)
self.settings.save() self.settings.save()
else: else:
self.common.log( self.common.log(