aboutsummaryrefslogtreecommitdiff
path: root/cli/onionshare_cli/meek.py
diff options
context:
space:
mode:
authorMiguel Jacq <mig@mig5.net>2021-10-19 11:36:03 +1100
committerMiguel Jacq <mig@mig5.net>2021-10-19 11:36:03 +1100
commit5b4d77c3634c0c13ae8ab1f27be540260027d0a8 (patch)
treeb17433aa310e2da928fc7c40d236c39cbcf40623 /cli/onionshare_cli/meek.py
parentbcf697574e8c3265bd0d9173da1b91f298770280 (diff)
downloadonionshare-5b4d77c3634c0c13ae8ab1f27be540260027d0a8.tar.gz
onionshare-5b4d77c3634c0c13ae8ab1f27be540260027d0a8.zip
Refactor to CensorshipCircumvention and Meek classes. Use Meek domain fronting when requesting bridges in frontend
Diffstat (limited to 'cli/onionshare_cli/meek.py')
-rw-r--r--cli/onionshare_cli/meek.py144
1 files changed, 144 insertions, 0 deletions
diff --git a/cli/onionshare_cli/meek.py b/cli/onionshare_cli/meek.py
new file mode 100644
index 00000000..4fc42756
--- /dev/null
+++ b/cli/onionshare_cli/meek.py
@@ -0,0 +1,144 @@
+# -*- coding: utf-8 -*-
+"""
+OnionShare | https://onionshare.org/
+
+Copyright (C) 2014-2021 Micah Lee, et al. <micah@micahflee.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+import subprocess
+from queue import Queue, Empty
+from threading import Thread
+
+
+class Meek(object):
+ """
+ The Meek object starts the meek-client as a subprocess.
+ This process is used to do domain-fronting to connect to
+ the Tor APIs for censorship circumvention and retrieving
+ bridges, before connecting to Tor.
+ """
+
+ def __init__(self, common):
+ """
+ Set up the Meek object
+ """
+
+ self.common = common
+ self.common.log("Meek", "__init__")
+
+ get_tor_paths = self.common.get_tor_paths
+ (
+ self.tor_path,
+ self.tor_geo_ip_file_path,
+ self.tor_geo_ipv6_file_path,
+ self.obfs4proxy_file_path,
+ self.snowflake_file_path,
+ self.meek_client_file_path,
+ ) = get_tor_paths()
+
+ self.meek_proxies = {}
+ self.meek_url = "https://moat.torproject.org.global.prod.fastly.net/"
+ self.meek_front = "cdn.sstatic.net"
+ self.meek_env = {
+ "TOR_PT_MANAGED_TRANSPORT_VER": "1",
+ "TOR_PT_CLIENT_TRANSPORTS": "meek",
+ }
+ self.meek_host = "127.0.0.1"
+ self.meek_port = None
+
+ def start(self):
+ """
+ Start the Meek Client and populate the SOCKS proxies dict
+ for use with requests to the Tor Moat API.
+ """
+ # Small method to read stdout from the subprocess.
+ # We use this to obtain the random port that Meek
+ # started on
+ def enqueue_output(out, queue):
+ for line in iter(out.readline, b""):
+ queue.put(line)
+ out.close()
+
+ # Start the Meek Client as a subprocess.
+
+ if self.common.platform == "Windows":
+ # In Windows, hide console window when opening tor.exe subprocess
+ startupinfo = subprocess.STARTUPINFO()
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+ self.meek_proc = subprocess.Popen(
+ [
+ self.meek_client_file_path,
+ "--url",
+ self.meek_url,
+ "--front",
+ self.meek_front,
+ ],
+ stdout=subprocess.PIPE,
+ startupinfo=startupinfo,
+ bufsize=1,
+ env=self.meek_env,
+ text=True,
+ )
+ else:
+ self.meek_proc = subprocess.Popen(
+ [
+ self.meek_client_file_path,
+ "--url",
+ self.meek_url,
+ "--front",
+ self.meek_front,
+ ],
+ stdout=subprocess.PIPE,
+ bufsize=1,
+ env=self.meek_env,
+ text=True,
+ )
+
+ # Queue up the stdout from the subprocess for polling later
+ q = Queue()
+ t = Thread(target=enqueue_output, args=(self.meek_proc.stdout, q))
+ t.daemon = True # thread dies with the program
+ t.start()
+
+ while True:
+ # read stdout without blocking
+ try:
+ line = q.get_nowait()
+ except Empty:
+ # no stdout yet?
+ pass
+ else: # we got stdout
+ if "CMETHOD meek socks5" in line:
+ self.meek_host = line.split(" ")[3].split(":")[0]
+ self.meek_port = line.split(" ")[3].split(":")[1]
+ self.common.log("Meek", "start", f"Meek host is {self.meek_host}")
+ self.common.log("Meek", "start", f"Meek port is {self.meek_port}")
+ break
+
+ if self.meek_port:
+ self.meek_proxies = {
+ "http": f"socks5h://{self.meek_host}:{self.meek_port}",
+ "https": f"socks5h://{self.meek_host}:{self.meek_port}",
+ }
+ else:
+ self.common.log("Meek", "start", "Could not obtain the meek port")
+ raise MeekNotRunning()
+
+
+class MeekNotRunning(Exception):
+ """
+ We were unable to start Meek or obtain the port
+ number it started on, in order to do domain fronting.
+ """