Ejemplo n.º 1
0
def gen_controller():
    connect_method = os.environ.get('connectmethod', 'port')

    if connect_method == 'port':
        return Controller.from_port(port=os.environ.get('port', 9051))
    elif connect_method == 'socket':
        return Controller.from_socket_file(path=os.environ.get('socket', '/var/run/tor/control'))
    else:
        print("env.connectmethod contains an invalid value. Please specify either 'port' or 'socket'.", file=sys.stderr)
        sys.exit(-1)
Ejemplo n.º 2
0
 def _on_connect(self):
     try:
         self._control = Controller.from_port()
     except SocketError:
         try:
             self._control = Controller.from_socket_file()
         except SocketError:
             self._status_icon.set_tooltip_text('Failed to initialize stem')
             return True
     self._control.add_status_listener(self._on_status)
     self._status_icon.set_tooltip_text('Stem: Authenticating')
     GLib.timeout_add_seconds(1, self._on_auth)
     return False
Ejemplo n.º 3
0
 def handleKey(self, key):
   isKeystrokeConsumed = True
   
   if key in (ord('n'), ord('N')) and torTools.getConn().isNewnymAvailable():
     self.sendNewnym()
   elif key in (ord('r'), ord('R')) and not self._isTorConnected:
     controller = None
     allowPortConnection, allowSocketConnection, _ = starter.allowConnectionTypes()
     
     if os.path.exists(self._config["startup.interface.socket"]) and allowSocketConnection:
       try:
         # TODO: um... what about passwords?
         controller = Controller.from_socket_file(self._config["startup.interface.socket"])
         controller.authenticate()
       except (IOError, stem.SocketError), exc:
         controller = None
         
         if not allowPortConnection:
           cli.popups.showMsg("Unable to reconnect (%s)" % exc, 3)
     elif not allowPortConnection:
       cli.popups.showMsg("Unable to reconnect (socket '%s' doesn't exist)" % self._config["startup.interface.socket"], 3)
     
     if not controller and allowPortConnection:
       # TODO: This has diverged from starter.py's connection, for instance it
       # doesn't account for relative cookie paths or multiple authentication
       # methods. We can't use the starter.py's connection function directly
       # due to password prompts, but we could certainly make this mess more
       # manageable.
       
       try:
         ctlAddr, ctlPort = self._config["startup.interface.ipAddress"], self._config["startup.interface.port"]
         controller = Controller.from_port(ctlAddr, ctlPort)
         
         try:
           controller.authenticate()
         except stem.connection.MissingPassword:
           controller.authenticate(authValue) # already got the password above
       except Exception, exc:
         controller = None
Ejemplo n.º 4
0
   sys.exit()
 
 # By default attempts to connect using the control socket if it exists. This
 # skips attempting to connect by socket or port if the user has given
 # arguments for connecting to the other.
 
 controller = None
 allowPortConnection, allowSocketConnection, allowDetachedStart = allowConnectionTypes()
 
 socketPath = param["startup.interface.socket"]
 if os.path.exists(socketPath) and allowSocketConnection:
   try:
     # TODO: um... what about passwords?
     # https://trac.torproject.org/6881
     
     controller = Controller.from_socket_file(socketPath)
     controller.authenticate()
   except IOError, exc:
     if not allowPortConnection:
       print "Unable to use socket '%s': %s" % (socketPath, exc)
 elif not allowPortConnection:
   print "Socket '%s' doesn't exist" % socketPath
 
 if not controller and allowPortConnection:
   # sets up stem connection, prompting for the passphrase if necessary and
   # sending problems to stdout if they arise
   authPassword = config.get("startup.controlPassword", CONFIG["startup.controlPassword"])
   incorrectPasswordMsg = "Password found in '%s' was incorrect" % configPath
   controller = _getController(controlAddr, controlPort, authPassword, incorrectPasswordMsg, not allowDetachedStart)
   
   # removing references to the controller password so the memory can be freed
Ejemplo n.º 5
0
    def connect(self, settings=False, config=False, tor_status_update_func=None):
        common.log('Onion', 'connect')

        # Either use settings that are passed in, or load them from disk
        if settings:
            self.settings = settings
        else:
            self.settings = Settings(config)
            self.settings.load()

        # The Tor controller
        self.c = None

        if self.settings.get('connection_type') == 'bundled':
            if not self.bundle_tor_supported:
                raise BundledTorNotSupported(strings._('settings_error_bundled_tor_not_supported'))

            # Create a torrc for this session
            self.tor_data_directory = tempfile.TemporaryDirectory()

            if self.system == 'Windows':
                # Windows needs to use network ports, doesn't support unix sockets
                torrc_template = open(common.get_resource_path('torrc_template-windows')).read()
                try:
                    self.tor_control_port = common.get_available_port(1000, 65535)
                except:
                    raise OSError(strings._('no_available_port'))
                self.tor_control_socket = None
                self.tor_cookie_auth_file = os.path.join(self.tor_data_directory.name, 'cookie')
                try:
                    self.tor_socks_port = common.get_available_port(1000, 65535)
                except:
                    raise OSError(strings._('no_available_port'))
                self.tor_torrc = os.path.join(self.tor_data_directory.name, 'torrc')
            else:
                # Linux, Mac and BSD can use unix sockets
                with open(common.get_resource_path('torrc_template')) as f:
                    torrc_template = f.read()
                self.tor_control_port = None
                self.tor_control_socket = os.path.join(self.tor_data_directory.name, 'control_socket')
                self.tor_cookie_auth_file = os.path.join(self.tor_data_directory.name, 'cookie')
                try:
                    self.tor_socks_port = common.get_available_port(1000, 65535)
                except:
                    raise OSError(strings._('no_available_port'))
                self.tor_torrc = os.path.join(self.tor_data_directory.name, 'torrc')

            torrc_template = torrc_template.replace('{{data_directory}}',   self.tor_data_directory.name)
            torrc_template = torrc_template.replace('{{control_port}}',     str(self.tor_control_port))
            torrc_template = torrc_template.replace('{{control_socket}}',   str(self.tor_control_socket))
            torrc_template = torrc_template.replace('{{cookie_auth_file}}', self.tor_cookie_auth_file)
            torrc_template = torrc_template.replace('{{geo_ip_file}}',      self.tor_geo_ip_file_path)
            torrc_template = torrc_template.replace('{{geo_ipv6_file}}',    self.tor_geo_ipv6_file_path)
            torrc_template = torrc_template.replace('{{socks_port}}',       str(self.tor_socks_port))
            with open(self.tor_torrc, 'w') as f:
                f.write(torrc_template)

                # Bridge support
                if self.settings.get('tor_bridges_use_obfs4'):
                    f.write('ClientTransportPlugin obfs4 exec {}\n'.format(self.obfs4proxy_file_path))
                    with open(common.get_resource_path('torrc_template-obfs4')) as o:
                        for line in o:
                            f.write(line)
                elif self.settings.get('tor_bridges_use_meek_lite_amazon'):
                    f.write('ClientTransportPlugin meek_lite exec {}\n'.format(self.obfs4proxy_file_path))
                    with open(common.get_resource_path('torrc_template-meek_lite_amazon')) as o:
                        for line in o:
                            f.write(line)
                elif self.settings.get('tor_bridges_use_meek_lite_azure'):
                    f.write('ClientTransportPlugin meek_lite exec {}\n'.format(self.obfs4proxy_file_path))
                    with open(common.get_resource_path('torrc_template-meek_lite_azure')) as o:
                        for line in o:
                            f.write(line)

                if self.settings.get('tor_bridges_use_custom_bridges'):
                    if 'obfs4' in self.settings.get('tor_bridges_use_custom_bridges'):
                        f.write('ClientTransportPlugin obfs4 exec {}\n'.format(self.obfs4proxy_file_path))
                    elif 'meek_lite' in self.settings.get('tor_bridges_use_custom_bridges'):
                        f.write('ClientTransportPlugin meek_lite exec {}\n'.format(self.obfs4proxy_file_path))
                    f.write(self.settings.get('tor_bridges_use_custom_bridges'))
                    f.write('\nUseBridges 1')

            # Execute a tor subprocess
            start_ts = time.time()
            if self.system == 'Windows':
                # In Windows, hide console window when opening tor.exe subprocess
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                self.tor_proc = subprocess.Popen([self.tor_path, '-f', self.tor_torrc], stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo)
            else:
                self.tor_proc = subprocess.Popen([self.tor_path, '-f', self.tor_torrc], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

            # Wait for the tor controller to start
            time.sleep(2)

            # Connect to the controller
            try:
                if self.system == 'Windows':
                    self.c = Controller.from_port(port=self.tor_control_port)
                    self.c.authenticate()
                else:
                    self.c = Controller.from_socket_file(path=self.tor_control_socket)
                    self.c.authenticate()
            except Exception as e:
                raise BundledTorBroken(strings._('settings_error_bundled_tor_broken', True).format(e.args[0]))

            while True:
                try:
                    res = self.c.get_info("status/bootstrap-phase")
                except SocketClosed:
                    raise BundledTorCanceled()

                res_parts = shlex.split(res)
                progress = res_parts[2].split('=')[1]
                summary = res_parts[4].split('=')[1]

                # "\033[K" clears the rest of the line
                print("{}: {}% - {}{}".format(strings._('connecting_to_tor'), progress, summary, "\033[K"), end="\r")

                if callable(tor_status_update_func):
                    if not tor_status_update_func(progress, summary):
                        # If the dialog was canceled, stop connecting to Tor
                        common.log('Onion', 'connect', 'tor_status_update_func returned false, canceling connecting to Tor')
                        print()
                        return False

                if summary == 'Done':
                    print("")
                    break
                time.sleep(0.2)

                # If using bridges, it might take a bit longer to connect to Tor
                if self.settings.get('tor_bridges_use_custom_bridges') or \
                   self.settings.get('tor_bridges_use_obfs4') or \
                   self.settings.get('tor_bridges_use_meek_lite_amazon') or \
                   self.settings.get('tor_bridges_use_meek_lite_azure'):
                    connect_timeout = 150
                else:
                    # Timeout after 120 seconds
                    connect_timeout = 120
                if time.time() - start_ts > connect_timeout:
                    print("")
                    self.tor_proc.terminate()
                    raise BundledTorTimeout(strings._('settings_error_bundled_tor_timeout'))

        elif self.settings.get('connection_type') == 'automatic':
            # Automatically try to guess the right way to connect to Tor Browser

            # Try connecting to control port
            found_tor = False

            # If the TOR_CONTROL_PORT environment variable is set, use that
            env_port = os.environ.get('TOR_CONTROL_PORT')
            if env_port:
                try:
                    self.c = Controller.from_port(port=int(env_port))
                    found_tor = True
                except:
                    pass

            else:
                # Otherwise, try default ports for Tor Browser, Tor Messenger, and system tor
                try:
                    ports = [9151, 9153, 9051]
                    for port in ports:
                        self.c = Controller.from_port(port=port)
                        found_tor = True
                except:
                    pass

                # If this still didn't work, try guessing the default socket file path
                socket_file_path = ''
                if not found_tor:
                    try:
                        if self.system == 'Darwin':
                            socket_file_path = os.path.expanduser('~/Library/Application Support/TorBrowser-Data/Tor/control.socket')

                        self.c = Controller.from_socket_file(path=socket_file_path)
                        found_tor = True
                    except:
                        pass

            # If connecting to default control ports failed, so let's try
            # guessing the socket file name next
            if not found_tor:
                try:
                    if self.system == 'Linux' or self.system == 'BSD':
                        socket_file_path = '/run/user/{}/Tor/control.socket'.format(os.geteuid())
                    elif self.system == 'Darwin':
                        socket_file_path = '/run/user/{}/Tor/control.socket'.format(os.geteuid())
                    elif self.system == 'Windows':
                        # Windows doesn't support unix sockets
                        raise TorErrorAutomatic(strings._('settings_error_automatic'))

                    self.c = Controller.from_socket_file(path=socket_file_path)

                except:
                    raise TorErrorAutomatic(strings._('settings_error_automatic'))

            # Try authenticating
            try:
                self.c.authenticate()
            except:
                raise TorErrorAutomatic(strings._('settings_error_automatic'))

        else:
            # Use specific settings to connect to tor

            # Try connecting
            try:
                if self.settings.get('connection_type') == 'control_port':
                    self.c = Controller.from_port(address=self.settings.get('control_port_address'), port=self.settings.get('control_port_port'))
                elif self.settings.get('connection_type') == 'socket_file':
                    self.c = Controller.from_socket_file(path=self.settings.get('socket_file_path'))
                else:
                    raise TorErrorInvalidSetting(strings._("settings_error_unknown"))

            except:
                if self.settings.get('connection_type') == 'control_port':
                    raise TorErrorSocketPort(strings._("settings_error_socket_port").format(self.settings.get('control_port_address'), self.settings.get('control_port_port')))
                else:
                    raise TorErrorSocketFile(strings._("settings_error_socket_file").format(self.settings.get('socket_file_path')))


            # Try authenticating
            try:
                if self.settings.get('auth_type') == 'no_auth':
                    self.c.authenticate()
                elif self.settings.get('auth_type') == 'password':
                    self.c.authenticate(self.settings.get('auth_password'))
                else:
                    raise TorErrorInvalidSetting(strings._("settings_error_unknown"))

            except MissingPassword:
                raise TorErrorMissingPassword(strings._('settings_error_missing_password'))
            except UnreadableCookieFile:
                raise TorErrorUnreadableCookieFile(strings._('settings_error_unreadable_cookie_file'))
            except AuthenticationFailure:
                raise TorErrorAuthError(strings._('settings_error_auth').format(self.settings.get('control_port_address'), self.settings.get('control_port_port')))

        # If we made it this far, we should be connected to Tor
        self.connected_to_tor = True

        # Get the tor version
        self.tor_version = self.c.get_version().version_str

        # Do the versions of stem and tor that I'm using support ephemeral onion services?
        list_ephemeral_hidden_services = getattr(self.c, "list_ephemeral_hidden_services", None)
        self.supports_ephemeral = callable(list_ephemeral_hidden_services) and self.tor_version >= '0.2.7.1'

        # Do the versions of stem and tor that I'm using support stealth onion services?
        try:
            res = self.c.create_ephemeral_hidden_service({1:1}, basic_auth={'onionshare':None}, await_publication=False)
            tmp_service_id = res.service_id
            self.c.remove_ephemeral_hidden_service(tmp_service_id)
            self.supports_stealth = True
        except:
            # ephemeral stealth onion services are not supported
            self.supports_stealth = False
Ejemplo n.º 6
0
#import re
import platform
from influxdb import InfluxDBClient
from stem.control import EventType, Controller

INFLUXDB_HOST = "influxdb1"
INFLUXDB_PORT = 8086
INFLUXDB_USER = "******"
INFLUXDB_PASSWORD = "******"
INFLUXDB_DATABASE = "tor"

with Controller.from_socket_file() as controller:
  controller.authenticate()
  
 # p = re.compile('(?P<read>\d+)\,(?P<write>\d+)$')
  
  bytes_read = controller.get_info("traffic/read")
  bytes_written = controller.get_info("traffic/written")
  bandwidth = controller.get_info("bw-event-cache")
  #m = p.search(bandwidth)
  tx = 0
  rx = 0
  i = 0
  for value in (bandwidth.split(' '))[-60:]:
    value_split = value.split(',')
    rx += int(value_split[0])
    tx += int(value_split[1])
    i += 1

  rx /= i
  tx /= i
Ejemplo n.º 7
0
#!/usr/bin/python
from stem.control import Controller
from os import getenv
from re import match

PORT = getenv('TORCONTROL')

try:
	controller = Controller.from_socket_file(path=PORT)
except:
	try:
		controller = Controller.from_port(port=int(PORT))
	except:
		try:
			controller = Controller.from_port(address=PORT)
		except:
			if not PORT:
				print 'Failed to connect to TORCONTROL=%s' % PORT
				raise SystemExit(1)
			res = match('([^/:]*):([0-9]*)$', PORT).groups()
			if res[0]:
				if res[1]:
					controller = Controller.from_port(address=res[0],port=int(res[1]))
				else:
					controller = Controller.from_port(address=res[0])
			else:
				controller = Controller.from_port(port=int(res[1]))
	
controller.authenticate()

if __name__ == '__main__':
Ejemplo n.º 8
0
    def connect(
        self,
        custom_settings=None,
        config=None,
        tor_status_update_func=None,
        connect_timeout=120,
        local_only=False,
    ):
        if local_only:
            self.common.log("Onion", "connect",
                            "--local-only, so skip trying to connect")
            return

        # Either use settings that are passed in, or use them from common
        if custom_settings:
            self.settings = custom_settings
        elif config:
            self.common.load_settings(config)
            self.settings = self.common.settings
        else:
            self.common.load_settings()
            self.settings = self.common.settings

        self.common.log(
            "Onion",
            "connect",
            f"connection_type={self.settings.get('connection_type')}",
        )

        # The Tor controller
        self.c = None

        if self.settings.get("connection_type") == "bundled":
            # Create a torrc for this session
            if self.use_tmp_dir:
                self.tor_data_directory = tempfile.TemporaryDirectory(
                    dir=self.common.build_tmp_dir())
                self.tor_data_directory_name = self.tor_data_directory.name
            else:
                self.tor_data_directory_name = self.common.build_tor_dir()
            self.common.log(
                "Onion",
                "connect",
                f"tor_data_directory_name={self.tor_data_directory_name}",
            )

            # Create the torrc
            with open(self.common.get_resource_path("torrc_template")) as f:
                torrc_template = f.read()
            self.tor_cookie_auth_file = os.path.join(
                self.tor_data_directory_name, "cookie")
            try:
                self.tor_socks_port = self.common.get_available_port(
                    1000, 65535)
            except Exception:
                print("OnionShare port not available")
                raise PortNotAvailable()
            self.tor_torrc = os.path.join(self.tor_data_directory_name,
                                          "torrc")

            # If there is an existing OnionShare tor process, kill it
            for proc in psutil.process_iter(["pid", "name", "username"]):
                try:
                    cmdline = proc.cmdline()
                    if (cmdline[0] == self.tor_path and cmdline[1] == "-f"
                            and cmdline[2] == self.tor_torrc):
                        self.common.log(
                            "Onion", "connect",
                            "found a stale tor process, killing it")
                        proc.terminate()
                        proc.wait()
                        break
                except Exception:
                    pass

            if self.common.platform == "Windows" or self.common.platform == "Darwin":
                # Windows doesn't support unix sockets, so it must use a network port.
                # macOS can't use unix sockets either because socket filenames are limited to
                # 100 chars, and the macOS sandbox forces us to put the socket file in a place
                # with a really long path.
                torrc_template += "ControlPort {{control_port}}\n"
                try:
                    self.tor_control_port = self.common.get_available_port(
                        1000, 65535)
                except Exception:
                    print("OnionShare port not available")
                    raise PortNotAvailable()
                self.tor_control_socket = None
            else:
                # Linux and BSD can use unix sockets
                torrc_template += "ControlSocket {{control_socket}}\n"
                self.tor_control_port = None
                self.tor_control_socket = os.path.join(
                    self.tor_data_directory_name, "control_socket")

            torrc_template = torrc_template.replace(
                "{{data_directory}}", self.tor_data_directory_name)
            torrc_template = torrc_template.replace("{{control_port}}",
                                                    str(self.tor_control_port))
            torrc_template = torrc_template.replace(
                "{{control_socket}}", str(self.tor_control_socket))
            torrc_template = torrc_template.replace("{{cookie_auth_file}}",
                                                    self.tor_cookie_auth_file)
            torrc_template = torrc_template.replace("{{geo_ip_file}}",
                                                    self.tor_geo_ip_file_path)
            torrc_template = torrc_template.replace(
                "{{geo_ipv6_file}}", self.tor_geo_ipv6_file_path)
            torrc_template = torrc_template.replace("{{socks_port}}",
                                                    str(self.tor_socks_port))
            torrc_template = torrc_template.replace(
                "{{obfs4proxy_path}}", str(self.obfs4proxy_file_path))
            torrc_template = torrc_template.replace(
                "{{snowflake_path}}", str(self.snowflake_file_path))

            with open(self.tor_torrc, "w") as f:
                self.common.log("Onion", "connect",
                                "Writing torrc template file")
                f.write(torrc_template)

                # Bridge support
                if self.settings.get("bridges_enabled"):
                    f.write("\nUseBridges 1\n")
                    if self.settings.get("bridges_type") == "built-in":
                        use_torrc_bridge_templates = False
                        builtin_bridge_type = self.settings.get(
                            "bridges_builtin_pt")
                        # Use built-inbridges stored in settings, if they are there already.
                        # They are probably newer than that of our hardcoded copies.
                        if self.settings.get("bridges_builtin"):
                            try:
                                for line in self.settings.get(
                                        "bridges_builtin"
                                )[builtin_bridge_type]:
                                    if line.strip() != "":
                                        f.write(f"Bridge {line}\n")
                                self.common.log(
                                    "Onion",
                                    "connect",
                                    "Wrote in the built-in bridges from OnionShare settings",
                                )
                            except KeyError:
                                # Somehow we had built-in bridges in our settings, but
                                # not for this bridge type. Fall back to using the hard-
                                # coded templates.
                                use_torrc_bridge_templates = True
                        else:
                            use_torrc_bridge_templates = True
                        if use_torrc_bridge_templates:
                            if builtin_bridge_type == "obfs4":
                                with open(
                                        self.common.get_resource_path(
                                            "torrc_template-obfs4")) as o:
                                    f.write(o.read())
                            elif builtin_bridge_type == "meek-azure":
                                with open(
                                        self.common.get_resource_path(
                                            "torrc_template-meek_lite_azure")
                                ) as o:
                                    f.write(o.read())
                            elif builtin_bridge_type == "snowflake":
                                with open(
                                        self.common.get_resource_path(
                                            "torrc_template-snowflake")) as o:
                                    f.write(o.read())
                            self.common.log(
                                "Onion",
                                "connect",
                                "Wrote in the built-in bridges from torrc templates",
                            )
                    elif self.settings.get("bridges_type") == "moat":
                        for line in self.settings.get("bridges_moat").split(
                                "\n"):
                            if line.strip() != "":
                                f.write(f"Bridge {line}\n")

                    elif self.settings.get("bridges_type") == "custom":
                        for line in self.settings.get("bridges_custom").split(
                                "\n"):
                            if line.strip() != "":
                                f.write(f"Bridge {line}\n")

            # Execute a tor subprocess
            self.common.log("Onion", "connect",
                            f"starting {self.tor_path} subprocess")
            start_ts = time.time()
            if self.common.platform == "Windows":
                # In Windows, hide console window when opening tor.exe subprocess
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                self.tor_proc = subprocess.Popen(
                    [self.tor_path, "-f", self.tor_torrc],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    startupinfo=startupinfo,
                )
            else:
                if self.common.is_snapcraft():
                    env = None
                else:
                    env = {"LD_LIBRARY_PATH": os.path.dirname(self.tor_path)}

                self.tor_proc = subprocess.Popen(
                    [self.tor_path, "-f", self.tor_torrc],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    env=env,
                )

            # Wait for the tor controller to start
            self.common.log("Onion", "connect",
                            f"tor pid: {self.tor_proc.pid}")
            time.sleep(2)

            return_code = self.tor_proc.poll()
            if return_code != None:
                self.common.log(
                    "Onion", "connect",
                    f"tor process has terminated early: {return_code}")

            # Connect to the controller
            self.common.log("Onion", "connect",
                            "authenticating to tor controller")
            try:
                if (self.common.platform == "Windows"
                        or self.common.platform == "Darwin"):
                    self.c = Controller.from_port(port=self.tor_control_port)
                    self.c.authenticate()
                else:
                    self.c = Controller.from_socket_file(
                        path=self.tor_control_socket)
                    self.c.authenticate()
            except Exception as e:
                print("OnionShare could not connect to Tor:\n{}".format(
                    e.args[0]))
                print(traceback.format_exc())
                raise BundledTorBroken(e.args[0])

            while True:
                try:
                    res = self.c.get_info("status/bootstrap-phase")
                except SocketClosed:
                    raise BundledTorCanceled()

                res_parts = shlex.split(res)
                progress = res_parts[2].split("=")[1]
                summary = res_parts[4].split("=")[1]

                # "\033[K" clears the rest of the line
                print(
                    f"\rConnecting to the Tor network: {progress}% - {summary}\033[K",
                    end="",
                )

                if callable(tor_status_update_func):
                    if not tor_status_update_func(progress, summary):
                        # If the dialog was canceled, stop connecting to Tor
                        self.common.log(
                            "Onion",
                            "connect",
                            "tor_status_update_func returned false, canceling connecting to Tor",
                        )
                        print()
                        return False

                if summary == "Done":
                    print("")
                    break
                time.sleep(0.2)

                # If using bridges, it might take a bit longer to connect to Tor
                if self.settings.get("bridges_enabled"):
                    # Only override timeout if a custom timeout has not been passed in
                    if connect_timeout == 120:
                        connect_timeout = 150
                if time.time() - start_ts > connect_timeout:
                    print("")
                    try:
                        self.tor_proc.terminate()
                        print(
                            "Taking too long to connect to Tor. Maybe you aren't connected to the Internet, or have an inaccurate system clock?"
                        )
                        raise BundledTorTimeout()
                    except FileNotFoundError:
                        pass

        elif self.settings.get("connection_type") == "automatic":
            # Automatically try to guess the right way to connect to Tor Browser
            automatic_error = "Could not connect to the Tor controller. Is Tor Browser (available from torproject.org) running in the background?"

            # Try connecting to control port
            found_tor = False

            # If the TOR_CONTROL_PORT environment variable is set, use that
            env_port = os.environ.get("TOR_CONTROL_PORT")
            if env_port:
                try:
                    self.c = Controller.from_port(port=int(env_port))
                    found_tor = True
                except Exception:
                    pass

            else:
                # Otherwise, try default ports for Tor Browser, Tor Messenger, and system tor
                try:
                    ports = [9151, 9153, 9051]
                    for port in ports:
                        self.c = Controller.from_port(port=port)
                        found_tor = True
                except Exception:
                    pass

                # If this still didn't work, try guessing the default socket file path
                socket_file_path = ""
                if not found_tor:
                    try:
                        if self.common.platform == "Darwin":
                            socket_file_path = os.path.expanduser(
                                "~/Library/Application Support/TorBrowser-Data/Tor/control.socket"
                            )

                        self.c = Controller.from_socket_file(
                            path=socket_file_path)
                        found_tor = True
                    except Exception:
                        pass

            # If connecting to default control ports failed, so let's try
            # guessing the socket file name next
            if not found_tor:
                try:
                    if self.common.platform == "Linux" or self.common.platform == "BSD":
                        socket_file_path = (
                            f"/run/user/{os.geteuid()}/Tor/control.socket")
                    elif self.common.platform == "Darwin":
                        socket_file_path = (
                            f"/run/user/{os.geteuid()}/Tor/control.socket")
                    elif self.common.platform == "Windows":
                        # Windows doesn't support unix sockets
                        print(automatic_error)
                        raise TorErrorAutomatic()

                    self.c = Controller.from_socket_file(path=socket_file_path)

                except Exception:
                    print(automatic_error)
                    raise TorErrorAutomatic()

            # Try authenticating
            try:
                self.c.authenticate()
            except Exception:
                print(automatic_error)
                raise TorErrorAutomatic()

        else:
            # Use specific settings to connect to tor
            invalid_settings_error = "Can't connect to Tor controller because your settings don't make sense."

            # Try connecting
            try:
                if self.settings.get("connection_type") == "control_port":
                    self.c = Controller.from_port(
                        address=self.settings.get("control_port_address"),
                        port=self.settings.get("control_port_port"),
                    )
                elif self.settings.get("connection_type") == "socket_file":
                    self.c = Controller.from_socket_file(
                        path=self.settings.get("socket_file_path"))
                else:
                    print(invalid_settings_error)
                    raise TorErrorInvalidSetting()

            except Exception:
                if self.settings.get("connection_type") == "control_port":
                    print(
                        "Can't connect to the Tor controller at {}:{}.".format(
                            self.settings.get("control_port_address"),
                            self.settings.get("control_port_port"),
                        ))
                    raise TorErrorSocketPort(
                        self.settings.get("control_port_address"),
                        self.settings.get("control_port_port"),
                    )
                print(
                    "Can't connect to the Tor controller using socket file {}."
                    .format(self.settings.get("socket_file_path")))
                raise TorErrorSocketFile(self.settings.get("socket_file_path"))

            # Try authenticating
            try:
                if self.settings.get("auth_type") == "no_auth":
                    self.c.authenticate()
                elif self.settings.get("auth_type") == "password":
                    self.c.authenticate(self.settings.get("auth_password"))
                else:
                    print(invalid_settings_error)
                    raise TorErrorInvalidSetting()

            except MissingPassword:
                print(
                    "Connected to Tor controller, but it requires a password to authenticate."
                )
                raise TorErrorMissingPassword()
            except UnreadableCookieFile:
                print(
                    "Connected to the Tor controller, but password may be wrong, or your user is not permitted to read the cookie file."
                )
                raise TorErrorUnreadableCookieFile()
            except AuthenticationFailure:
                print(
                    "Connected to {}:{}, but can't authenticate. Maybe this isn't a Tor controller?"
                    .format(
                        self.settings.get("control_port_address"),
                        self.settings.get("control_port_port"),
                    ))
                raise TorErrorAuthError(
                    self.settings.get("control_port_address"),
                    self.settings.get("control_port_port"),
                )

        # If we made it this far, we should be connected to Tor
        self.connected_to_tor = True

        # Get the tor version
        self.tor_version = self.c.get_version().version_str
        self.common.log("Onion", "connect",
                        f"Connected to tor {self.tor_version}")

        # Do the versions of stem and tor that I'm using support ephemeral onion services?
        list_ephemeral_hidden_services = getattr(
            self.c, "list_ephemeral_hidden_services", None)
        self.supports_ephemeral = (callable(list_ephemeral_hidden_services)
                                   and self.tor_version >= "0.2.7.1")

        # Do the versions of stem and tor that I'm using support v3 stealth onion services?
        try:
            res = self.c.create_ephemeral_hidden_service(
                {1: 1},
                basic_auth=None,
                await_publication=False,
                key_type="NEW",
                key_content="ED25519-V3",
                client_auth_v3=
                "E2GOT5LTUTP3OAMRCRXO4GSH6VKJEUOXZQUC336SRKAHTTT5OVSA",
            )
            tmp_service_id = res.service_id
            self.c.remove_ephemeral_hidden_service(tmp_service_id)
            self.supports_stealth = True
        except Exception:
            # ephemeral stealth onion services are not supported
            self.supports_stealth = False

        # Does this version of Tor support next-gen ('v3') onions?
        # Note, this is the version of Tor where this bug was fixed:
        # https://trac.torproject.org/projects/tor/ticket/28619
        self.supports_v3_onions = self.tor_version >= Version("0.3.5.7")

        # Now that we are connected to Tor, if we are using built-in bridges,
        # update them with the latest copy available from the Tor API
        if (self.settings.get("bridges_enabled")
                and self.settings.get("bridges_type") == "built-in"):
            self.update_builtin_bridges()
Ejemplo n.º 9
0
def main():
    """
    Entry point when invoked over the command line.
    """
    setproctitle('onionbalance')
    args = parse_cmd_args().parse_args()
    config_file_options = settings.parse_config_file(args.config)

    # Update global configuration with options specified in the config file
    for setting in dir(config):
        if setting.isupper() and config_file_options.get(setting):
            setattr(config, setting, config_file_options.get(setting))

    # Override the log level if specified on the command line.
    if args.verbosity:
        config.LOG_LEVEL = args.verbosity.upper()

    # Write log file if configured in environment variable or config file
    if config.LOG_LOCATION:
        log.setup_file_logger(config.LOG_LOCATION)

    logger.setLevel(logging.__dict__[config.LOG_LEVEL.upper()])

    # Create a connection to the Tor unix domain control socket or control port
    try:
        tor_socket = (args.socket or config.TOR_CONTROL_SOCKET)
        tor_address = (args.ip or config.TOR_ADDRESS)
        tor_port = (args.port or config.TOR_PORT)
        try:
            controller = Controller.from_socket_file(path=tor_socket)
            logger.debug("Successfully connected to the Tor control socket "
                         "%s.", tor_socket)
        except stem.SocketError:
            logger.debug("Unable to connect to the Tor control socket %s.",
                         tor_socket)
            controller = Controller.from_port(address=tor_address,
                                              port=tor_port)
            logger.debug("Successfully connected to the Tor control port.")
    except stem.SocketError as exc:
        logger.error("Unable to connect to Tor control socket or port: %s",
                     exc)
        sys.exit(1)

    try:
        controller.authenticate(password=config.TOR_CONTROL_PASSWORD)
    except stem.connection.AuthenticationFailure as exc:
        logger.error("Unable to authenticate on the Tor control connection: "
                     "%s", exc)
        sys.exit(1)
    else:
        logger.debug("Successfully authenticated on the Tor control "
                     "connection.")

    status_socket = status.StatusSocket(config.STATUS_SOCKET_LOCATION)
    eventhandler.SignalHandler(controller, status_socket)

    # Disable no-member due to bug with "Instance of 'Enum' has no * member"
    # pylint: disable=no-member

    # Check that the Tor client supports the HSPOST control port command
    if not controller.get_version() >= stem.version.Requirement.HSPOST:
        logger.error("A Tor version >= %s is required. You may need to "
                     "compile Tor from source or install a package from "
                     "the experimental Tor repository.",
                     stem.version.Requirement.HSPOST)
        sys.exit(1)

    # Load the keys and config for each onion service
    settings.initialize_services(controller,
                                 config_file_options.get('services'))

    # Finished parsing all the config file.

    handler = eventhandler.EventHandler()
    controller.add_event_listener(handler.new_status,
                                  EventType.STATUS_GENERAL)
    controller.add_event_listener(handler.new_desc,
                                  EventType.HS_DESC)
    controller.add_event_listener(handler.new_desc_content,
                                  EventType.HS_DESC_CONTENT)

    # Schedule descriptor fetch and upload events
    scheduler.add_job(config.REFRESH_INTERVAL, fetch_instance_descriptors,
                      controller)
    scheduler.add_job(config.PUBLISH_CHECK_INTERVAL, publish_all_descriptors)

    # Run initial fetch of HS instance descriptors
    scheduler.run_all(delay_seconds=config.INITIAL_DELAY)

    # Begin main loop to poll for HS descriptors
    scheduler.run_forever()

    return 0
Ejemplo n.º 10
0
    def connect(self, settings=False, tor_status_update_func=None):
        common.log('Onion', 'connect')

        # Either use settings that are passed in, or load them from disk
        if settings:
            self.settings = settings
        else:
            self.settings = Settings()
            self.settings.load()

        # The Tor controller
        self.c = None

        if self.settings.get('connection_type') == 'bundled':
            if not self.bundle_tor_supported:
                raise BundledTorNotSupported(
                    strings._('settings_error_bundled_tor_not_supported'))

            # Create a torrc for this session
            self.tor_data_directory = tempfile.TemporaryDirectory()

            if self.system == 'Windows':
                # Windows needs to use network ports, doesn't support unix sockets
                torrc_template = open(
                    common.get_resource_path('torrc_template-windows')).read()
                self.tor_control_port = common.get_available_port(1000, 65535)
                self.tor_control_socket = None
                self.tor_cookie_auth_file = os.path.join(
                    self.tor_data_directory.name, 'cookie')
                self.tor_socks_port = common.get_available_port(1000, 65535)
                self.tor_torrc = os.path.join(self.tor_data_directory.name,
                                              'torrc')
            else:
                # Linux and Mac can use unix sockets
                with open(common.get_resource_path('torrc_template')) as f:
                    torrc_template = f.read()
                self.tor_control_port = None
                self.tor_control_socket = os.path.join(
                    self.tor_data_directory.name, 'control_socket')
                self.tor_cookie_auth_file = os.path.join(
                    self.tor_data_directory.name, 'cookie')
                self.tor_socks_port = common.get_available_port(1000, 65535)
                self.tor_torrc = os.path.join(self.tor_data_directory.name,
                                              'torrc')

            torrc_template = torrc_template.replace(
                '{{data_directory}}', self.tor_data_directory.name)
            torrc_template = torrc_template.replace('{{control_port}}',
                                                    str(self.tor_control_port))
            torrc_template = torrc_template.replace(
                '{{control_socket}}', str(self.tor_control_socket))
            torrc_template = torrc_template.replace('{{cookie_auth_file}}',
                                                    self.tor_cookie_auth_file)
            torrc_template = torrc_template.replace('{{geo_ip_file}}',
                                                    self.tor_geo_ip_file_path)
            torrc_template = torrc_template.replace(
                '{{geo_ipv6_file}}', self.tor_geo_ipv6_file_path)
            torrc_template = torrc_template.replace('{{socks_port}}',
                                                    str(self.tor_socks_port))
            with open(self.tor_torrc, 'w') as f:
                f.write(torrc_template)

            # Execute a tor subprocess
            start_ts = time.time()
            if self.system == 'Windows':
                # In Windows, hide console window when opening tor.exe subprocess
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                self.tor_proc = subprocess.Popen(
                    [self.tor_path, '-f', self.tor_torrc],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    startupinfo=startupinfo)
            else:
                self.tor_proc = subprocess.Popen(
                    [self.tor_path, '-f', self.tor_torrc],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE)

            # Wait for the tor controller to start
            time.sleep(2)

            # Connect to the controller
            try:
                if self.system == 'Windows':
                    self.c = Controller.from_port(port=self.tor_control_port)
                    self.c.authenticate()
                else:
                    self.c = Controller.from_socket_file(
                        path=self.tor_control_socket)
                    self.c.authenticate()
            except Exception as e:
                raise BundledTorBroken(
                    strings._('settings_error_bundled_tor_broken',
                              True).format(e.args[0]))

            while True:
                try:
                    res = self.c.get_info("status/bootstrap-phase")
                except SocketClosed:
                    raise BundledTorCanceled()

                res_parts = shlex.split(res)
                progress = res_parts[2].split('=')[1]
                summary = res_parts[4].split('=')[1]

                # "\033[K" clears the rest of the line
                print("{}: {}% - {}{}".format(strings._('connecting_to_tor'),
                                              progress, summary, "\033[K"),
                      end="\r")

                if callable(tor_status_update_func):
                    if not tor_status_update_func(progress, summary):
                        # If the dialog was canceled, stop connecting to Tor
                        common.log(
                            'Onion', 'connect',
                            'tor_status_update_func returned false, canceling connecting to Tor'
                        )
                        print()
                        return False

                if summary == 'Done':
                    print("")
                    break
                time.sleep(0.2)

                # Timeout after 45 seconds
                if time.time() - start_ts > 45:
                    print("")
                    self.tor_proc.terminate()
                    raise BundledTorTimeout(
                        strings._('settings_error_bundled_tor_timeout'))

        elif self.settings.get('connection_type') == 'automatic':
            # Automatically try to guess the right way to connect to Tor Browser

            # Try connecting to control port
            found_tor = False

            # If the TOR_CONTROL_PORT environment variable is set, use that
            env_port = os.environ.get('TOR_CONTROL_PORT')
            if env_port:
                try:
                    self.c = Controller.from_port(port=int(env_port))
                    found_tor = True
                except:
                    pass

            else:
                # Otherwise, try default ports for Tor Browser, Tor Messenger, and system tor
                try:
                    ports = [9151, 9153, 9051]
                    for port in ports:
                        self.c = Controller.from_port(port=port)
                        found_tor = True
                except:
                    pass

                # If this still didn't work, try guessing the default socket file path
                socket_file_path = ''
                if not found_tor:
                    try:
                        if self.system == 'Darwin':
                            socket_file_path = os.path.expanduser(
                                '~/Library/Application Support/TorBrowser-Data/Tor/control.socket'
                            )

                        self.c = Controller.from_socket_file(
                            path=socket_file_path)
                        found_tor = True
                    except:
                        pass

            # If connecting to default control ports failed, so let's try
            # guessing the socket file name next
            if not found_tor:
                try:
                    if self.system == 'Linux':
                        socket_file_path = '/run/user/{}/Tor/control.socket'.format(
                            os.geteuid())
                    elif self.system == 'Darwin':
                        socket_file_path = '/run/user/{}/Tor/control.socket'.format(
                            os.geteuid())
                    elif self.system == 'Windows':
                        # Windows doesn't support unix sockets
                        raise TorErrorAutomatic(
                            strings._('settings_error_automatic'))

                    self.c = Controller.from_socket_file(path=socket_file_path)

                except:
                    raise TorErrorAutomatic(
                        strings._('settings_error_automatic'))

            # Try authenticating
            try:
                self.c.authenticate()
            except:
                raise TorErrorAutomatic(strings._('settings_error_automatic'))

        else:
            # Use specific settings to connect to tor

            # Try connecting
            try:
                if self.settings.get('connection_type') == 'control_port':
                    self.c = Controller.from_port(
                        address=self.settings.get('control_port_address'),
                        port=self.settings.get('control_port_port'))
                elif self.settings.get('connection_type') == 'socket_file':
                    self.c = Controller.from_socket_file(
                        path=self.settings.get('socket_file_path'))
                else:
                    raise TorErrorInvalidSetting(
                        strings._("settings_error_unknown"))

            except:
                if self.settings.get('connection_type') == 'control_port':
                    raise TorErrorSocketPort(
                        strings._("settings_error_socket_port").format(
                            self.settings.get('control_port_address'),
                            self.settings.get('control_port_port')))
                else:
                    raise TorErrorSocketFile(
                        strings._("settings_error_socket_file").format(
                            self.settings.get('socket_file_path')))

            # Try authenticating
            try:
                if self.settings.get('auth_type') == 'no_auth':
                    self.c.authenticate()
                elif self.settings.get('auth_type') == 'password':
                    self.c.authenticate(self.settings.get('auth_password'))
                else:
                    raise TorErrorInvalidSetting(
                        strings._("settings_error_unknown"))

            except MissingPassword:
                raise TorErrorMissingPassword(
                    strings._('settings_error_missing_password'))
            except UnreadableCookieFile:
                raise TorErrorUnreadableCookieFile(
                    strings._('settings_error_unreadable_cookie_file'))
            except AuthenticationFailure:
                raise TorErrorAuthError(
                    strings._('settings_error_auth').format(
                        self.settings.get('control_port_address'),
                        self.settings.get('control_port_port')))

        # If we made it this far, we should be connected to Tor
        self.connected_to_tor = True

        # Get the tor version
        self.tor_version = self.c.get_version().version_str

        # Do the versions of stem and tor that I'm using support ephemeral onion services?
        list_ephemeral_hidden_services = getattr(
            self.c, "list_ephemeral_hidden_services", None)
        self.supports_ephemeral = callable(
            list_ephemeral_hidden_services) and self.tor_version >= '0.2.7.1'

        # Do the versions of stem and tor that I'm using support stealth onion services?
        try:
            res = self.c.create_ephemeral_hidden_service(
                {1: 1},
                basic_auth={'onionshare': None},
                await_publication=False)
            tmp_service_id = res.content()[0][2].split('=')[1]
            self.c.remove_ephemeral_hidden_service(tmp_service_id)
            self.supports_stealth = True
        except:
            # ephemeral stealth onion services are not supported
            self.supports_stealth = False
Ejemplo n.º 11
0
    def __init__(self,
                 transparent_torification=False,
                 stealth=False,
                 settings=False):
        self.transparent_torification = transparent_torification
        self.stealth = stealth

        # Either use settings that are passed in, or load them from disk
        if settings:
            self.settings = settings
        else:
            self.settings = Settings()
            self.settings.load()

        # files and dirs to delete on shutdown
        self.cleanup_filenames = []
        self.service_id = None

        # Try to connect to Tor
        self.c = None

        if self.settings.get('connection_type') == 'automatic':
            # Automatically try to guess the right way to connect to Tor Browser
            p = platform.system()

            # Try connecting to control port
            found_tor = False

            # If the TOR_CONTROL_PORT environment variable is set, use that
            env_port = os.environ.get('TOR_CONTROL_PORT')
            if env_port:
                try:
                    self.c = Controller.from_port(port=int(env_port))
                    found_tor = True
                except:
                    pass

            else:
                # Otherwise, try default ports for Tor Browser, Tor Messenger, and system tor
                try:
                    ports = [9151, 9153, 9051]
                    for port in ports:
                        self.c = Controller.from_port(port=port)
                        found_tor = True
                except:
                    pass

                # If this still didn't work, try guessing the default socket file path
                socket_file_path = ''
                if not found_tor:
                    try:
                        if p == 'Darwin':
                            socket_file_path = os.path.expanduser(
                                '~/Library/Application Support/TorBrowser-Data/Tor/control.socket'
                            )

                        self.c = Controller.from_socket_file(
                            path=socket_file_path)
                        found_tor = True
                    except:
                        pass

            # If connecting to default control ports failed, so let's try
            # guessing the socket file name next
            if not found_tor:
                try:
                    if p == 'Linux':
                        socket_file_path = '/run/user/{}/Tor/control.socket'.format(
                            os.geteuid())
                    elif p == 'Darwin':
                        # TODO: figure out the unix socket path in OS X
                        socket_file_path = '/run/user/{}/Tor/control.socket'.format(
                            os.geteuid())
                    elif p == 'Windows':
                        # Windows doesn't support unix sockets
                        raise TorErrorAutomatic(
                            strings._('settings_error_automatic'))

                    self.c = Controller.from_socket_file(path=socket_file_path)

                except:
                    raise TorErrorAutomatic(
                        strings._('settings_error_automatic'))

            # Try authenticating
            try:
                self.c.authenticate()
            except:
                raise TorErrorAutomatic(strings._('settings_error_automatic'))

        else:
            # Use specific settings to connect to tor

            # Try connecting
            try:
                if self.settings.get('connection_type') == 'control_port':
                    self.c = Controller.from_port(
                        address=self.settings.get('control_port_address'),
                        port=self.settings.get('control_port_port'))
                elif self.settings.get('connection_type') == 'socket_file':
                    self.c = Controller.from_socket_file(
                        path=self.settings.get('socket_file_path'))
                else:
                    raise TorErrorInvalidSetting(
                        strings._("settings_error_unknown"))

            except:
                if self.settings.get('connection_type') == 'control_port':
                    raise TorErrorSocketPort(
                        strings._("settings_error_socket_port").format(
                            self.settings.get('control_port_address'),
                            self.settings.get('control_port_port')))
                else:
                    raise TorErrorSocketFile(
                        strings._("settings_error_socket_file").format(
                            self.settings.get('socket_file_path')))

            # Try authenticating
            try:
                if self.settings.get('auth_type') == 'no_auth':
                    self.c.authenticate()
                elif self.settings.get('auth_type') == 'password':
                    self.c.authenticate(self.settings.get('auth_password'))
                else:
                    raise TorErrorInvalidSetting(
                        strings._("settings_error_unknown"))

            except MissingPassword:
                raise TorErrorMissingPassword(
                    strings._('settings_error_missing_password'))
            except UnreadableCookieFile:
                raise TorErrorUnreadableCookieFile(
                    strings._('settings_error_unreadable_cookie_file'))
            except AuthenticationFailure:
                raise TorErrorAuthError(
                    strings._('settings_error_auth').format(
                        self.settings.get('control_port_address'),
                        self.settings.get('control_port_port')))

        # get the tor version
        self.tor_version = self.c.get_version().version_str

        # do the versions of stem and tor that I'm using support ephemeral onion services?
        list_ephemeral_hidden_services = getattr(
            self.c, "list_ephemeral_hidden_services", None)
        self.supports_ephemeral = callable(
            list_ephemeral_hidden_services) and self.tor_version >= '0.2.7.1'

        # do the versions of stem and tor that I'm using support stealth onion services?
        try:
            res = self.c.create_ephemeral_hidden_service(
                {1: 1},
                basic_auth={'onionshare': None},
                await_publication=False)
            tmp_service_id = res.content()[0][2].split('=')[1]
            self.c.remove_ephemeral_hidden_service(tmp_service_id)
            self.supports_stealth = True
        except:
            # ephemeral stealth onion services are not supported
            self.supports_stealth = False
Ejemplo n.º 12
0
def _get_controller_socket(args):
    return Controller.from_socket_file(path=args.ctrl_socket)
Ejemplo n.º 13
0
    def connect(
        self,
        custom_settings=False,
        config=False,
        tor_status_update_func=None,
        connect_timeout=120,
    ):
        self.common.log("Onion", "connect")

        # Either use settings that are passed in, or use them from common
        if custom_settings:
            self.settings = custom_settings
        elif config:
            self.common.load_settings(config)
            self.settings = self.common.settings
        else:
            self.common.load_settings()
            self.settings = self.common.settings

        strings.load_strings(self.common)
        # The Tor controller
        self.c = None

        if self.settings.get("connection_type") == "bundled":
            if not self.bundle_tor_supported:
                raise BundledTorNotSupported(
                    strings._("settings_error_bundled_tor_not_supported")
                )

            # Create a torrc for this session
            self.tor_data_directory = tempfile.TemporaryDirectory(
                dir=self.common.build_data_dir()
            )
            self.common.log(
                "Onion",
                "connect",
                "tor_data_directory={}".format(self.tor_data_directory.name),
            )

            # Create the torrc
            with open(self.common.get_resource_path("torrc_template")) as f:
                torrc_template = f.read()
            self.tor_cookie_auth_file = os.path.join(
                self.tor_data_directory.name, "cookie"
            )
            try:
                self.tor_socks_port = self.common.get_available_port(1000, 65535)
            except:
                raise OSError(strings._("no_available_port"))
            self.tor_torrc = os.path.join(self.tor_data_directory.name, "torrc")

            if self.common.platform == "Windows" or self.common.platform == "Darwin":
                # Windows doesn't support unix sockets, so it must use a network port.
                # macOS can't use unix sockets either because socket filenames are limited to
                # 100 chars, and the macOS sandbox forces us to put the socket file in a place
                # with a really long path.
                torrc_template += "ControlPort {{control_port}}\n"
                try:
                    self.tor_control_port = self.common.get_available_port(1000, 65535)
                except:
                    raise OSError(strings._("no_available_port"))
                self.tor_control_socket = None
            else:
                # Linux and BSD can use unix sockets
                torrc_template += "ControlSocket {{control_socket}}\n"
                self.tor_control_port = None
                self.tor_control_socket = os.path.join(
                    self.tor_data_directory.name, "control_socket"
                )

            torrc_template = torrc_template.replace(
                "{{data_directory}}", self.tor_data_directory.name
            )
            torrc_template = torrc_template.replace(
                "{{control_port}}", str(self.tor_control_port)
            )
            torrc_template = torrc_template.replace(
                "{{control_socket}}", str(self.tor_control_socket)
            )
            torrc_template = torrc_template.replace(
                "{{cookie_auth_file}}", self.tor_cookie_auth_file
            )
            torrc_template = torrc_template.replace(
                "{{geo_ip_file}}", self.tor_geo_ip_file_path
            )
            torrc_template = torrc_template.replace(
                "{{geo_ipv6_file}}", self.tor_geo_ipv6_file_path
            )
            torrc_template = torrc_template.replace(
                "{{socks_port}}", str(self.tor_socks_port)
            )

            with open(self.tor_torrc, "w") as f:
                f.write(torrc_template)

                # Bridge support
                if self.settings.get("tor_bridges_use_obfs4"):
                    f.write(
                        "ClientTransportPlugin obfs4 exec {}\n".format(
                            self.obfs4proxy_file_path
                        )
                    )
                    with open(
                        self.common.get_resource_path("torrc_template-obfs4")
                    ) as o:
                        for line in o:
                            f.write(line)
                elif self.settings.get("tor_bridges_use_meek_lite_azure"):
                    f.write(
                        "ClientTransportPlugin meek_lite exec {}\n".format(
                            self.obfs4proxy_file_path
                        )
                    )
                    with open(
                        self.common.get_resource_path("torrc_template-meek_lite_azure")
                    ) as o:
                        for line in o:
                            f.write(line)

                if self.settings.get("tor_bridges_use_custom_bridges"):
                    if "obfs4" in self.settings.get("tor_bridges_use_custom_bridges"):
                        f.write(
                            "ClientTransportPlugin obfs4 exec {}\n".format(
                                self.obfs4proxy_file_path
                            )
                        )
                    elif "meek_lite" in self.settings.get(
                        "tor_bridges_use_custom_bridges"
                    ):
                        f.write(
                            "ClientTransportPlugin meek_lite exec {}\n".format(
                                self.obfs4proxy_file_path
                            )
                        )
                    f.write(self.settings.get("tor_bridges_use_custom_bridges"))
                    f.write("\nUseBridges 1")

            # Execute a tor subprocess
            start_ts = time.time()
            if self.common.platform == "Windows":
                # In Windows, hide console window when opening tor.exe subprocess
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                self.tor_proc = subprocess.Popen(
                    [self.tor_path, "-f", self.tor_torrc],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                    startupinfo=startupinfo,
                )
            else:
                self.tor_proc = subprocess.Popen(
                    [self.tor_path, "-f", self.tor_torrc],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE,
                )

            # Wait for the tor controller to start
            time.sleep(2)

            # Connect to the controller
            try:
                if (
                    self.common.platform == "Windows"
                    or self.common.platform == "Darwin"
                ):
                    self.c = Controller.from_port(port=self.tor_control_port)
                    self.c.authenticate()
                else:
                    self.c = Controller.from_socket_file(path=self.tor_control_socket)
                    self.c.authenticate()
            except Exception as e:
                raise BundledTorBroken(
                    strings._("settings_error_bundled_tor_broken").format(e.args[0])
                )

            while True:
                try:
                    res = self.c.get_info("status/bootstrap-phase")
                except SocketClosed:
                    raise BundledTorCanceled()

                res_parts = shlex.split(res)
                progress = res_parts[2].split("=")[1]
                summary = res_parts[4].split("=")[1]

                # "\033[K" clears the rest of the line
                print(
                    "Connecting to the Tor network: {}% - {}{}".format(
                        progress, summary, "\033[K"
                    ),
                    end="\r",
                )

                if callable(tor_status_update_func):
                    if not tor_status_update_func(progress, summary):
                        # If the dialog was canceled, stop connecting to Tor
                        self.common.log(
                            "Onion",
                            "connect",
                            "tor_status_update_func returned false, canceling connecting to Tor",
                        )
                        print()
                        return False

                if summary == "Done":
                    print("")
                    break
                time.sleep(0.2)

                # If using bridges, it might take a bit longer to connect to Tor
                if (
                    self.settings.get("tor_bridges_use_custom_bridges")
                    or self.settings.get("tor_bridges_use_obfs4")
                    or self.settings.get("tor_bridges_use_meek_lite_azure")
                ):
                    # Only override timeout if a custom timeout has not been passed in
                    if connect_timeout == 120:
                        connect_timeout = 150
                if time.time() - start_ts > connect_timeout:
                    print("")
                    try:
                        self.tor_proc.terminate()
                        raise BundledTorTimeout(
                            strings._("settings_error_bundled_tor_timeout")
                        )
                    except FileNotFoundError:
                        pass

        elif self.settings.get("connection_type") == "automatic":
            # Automatically try to guess the right way to connect to Tor Browser

            # Try connecting to control port
            found_tor = False

            # If the TOR_CONTROL_PORT environment variable is set, use that
            env_port = os.environ.get("TOR_CONTROL_PORT")
            if env_port:
                try:
                    self.c = Controller.from_port(port=int(env_port))
                    found_tor = True
                except:
                    pass

            else:
                # Otherwise, try default ports for Tor Browser, Tor Messenger, and system tor
                try:
                    ports = [9151, 9153, 9051]
                    for port in ports:
                        self.c = Controller.from_port(port=port)
                        found_tor = True
                except:
                    pass

                # If this still didn't work, try guessing the default socket file path
                socket_file_path = ""
                if not found_tor:
                    try:
                        if self.common.platform == "Darwin":
                            socket_file_path = os.path.expanduser(
                                "~/Library/Application Support/TorBrowser-Data/Tor/control.socket"
                            )

                        self.c = Controller.from_socket_file(path=socket_file_path)
                        found_tor = True
                    except:
                        pass

            # If connecting to default control ports failed, so let's try
            # guessing the socket file name next
            if not found_tor:
                try:
                    if self.common.platform == "Linux" or self.common.platform == "BSD":
                        socket_file_path = "/run/user/{}/Tor/control.socket".format(
                            os.geteuid()
                        )
                    elif self.common.platform == "Darwin":
                        socket_file_path = "/run/user/{}/Tor/control.socket".format(
                            os.geteuid()
                        )
                    elif self.common.platform == "Windows":
                        # Windows doesn't support unix sockets
                        raise TorErrorAutomatic(strings._("settings_error_automatic"))

                    self.c = Controller.from_socket_file(path=socket_file_path)

                except:
                    raise TorErrorAutomatic(strings._("settings_error_automatic"))

            # Try authenticating
            try:
                self.c.authenticate()
            except:
                raise TorErrorAutomatic(strings._("settings_error_automatic"))

        else:
            # Use specific settings to connect to tor

            # Try connecting
            try:
                if self.settings.get("connection_type") == "control_port":
                    self.c = Controller.from_port(
                        address=self.settings.get("control_port_address"),
                        port=self.settings.get("control_port_port"),
                    )
                elif self.settings.get("connection_type") == "socket_file":
                    self.c = Controller.from_socket_file(
                        path=self.settings.get("socket_file_path")
                    )
                else:
                    raise TorErrorInvalidSetting(strings._("settings_error_unknown"))

            except:
                if self.settings.get("connection_type") == "control_port":
                    raise TorErrorSocketPort(
                        strings._("settings_error_socket_port").format(
                            self.settings.get("control_port_address"),
                            self.settings.get("control_port_port"),
                        )
                    )
                else:
                    raise TorErrorSocketFile(
                        strings._("settings_error_socket_file").format(
                            self.settings.get("socket_file_path")
                        )
                    )

            # Try authenticating
            try:
                if self.settings.get("auth_type") == "no_auth":
                    self.c.authenticate()
                elif self.settings.get("auth_type") == "password":
                    self.c.authenticate(self.settings.get("auth_password"))
                else:
                    raise TorErrorInvalidSetting(strings._("settings_error_unknown"))

            except MissingPassword:
                raise TorErrorMissingPassword(
                    strings._("settings_error_missing_password")
                )
            except UnreadableCookieFile:
                raise TorErrorUnreadableCookieFile(
                    strings._("settings_error_unreadable_cookie_file")
                )
            except AuthenticationFailure:
                raise TorErrorAuthError(
                    strings._("settings_error_auth").format(
                        self.settings.get("control_port_address"),
                        self.settings.get("control_port_port"),
                    )
                )

        # If we made it this far, we should be connected to Tor
        self.connected_to_tor = True

        # Get the tor version
        self.tor_version = self.c.get_version().version_str
        self.common.log(
            "Onion", "connect", "Connected to tor {}".format(self.tor_version)
        )

        # Do the versions of stem and tor that I'm using support ephemeral onion services?
        list_ephemeral_hidden_services = getattr(
            self.c, "list_ephemeral_hidden_services", None
        )
        self.supports_ephemeral = (
            callable(list_ephemeral_hidden_services) and self.tor_version >= "0.2.7.1"
        )

        # Do the versions of stem and tor that I'm using support stealth onion services?
        try:
            res = self.c.create_ephemeral_hidden_service(
                {1: 1}, basic_auth={"onionshare": None}, await_publication=False
            )
            tmp_service_id = res.service_id
            self.c.remove_ephemeral_hidden_service(tmp_service_id)
            self.supports_stealth = True
        except:
            # ephemeral stealth onion services are not supported
            self.supports_stealth = False

        # Does this version of Tor support next-gen ('v3') onions?
        # Note, this is the version of Tor where this bug was fixed:
        # https://trac.torproject.org/projects/tor/ticket/28619
        self.supports_v3_onions = self.tor_version >= Version("0.3.5.7")