コード例 #1
0
def process_received_packet(ts: 'datetime', ts_bytes: bytes, header: bytes,
                            payload_bytes: bytes, onion_pub_key: bytes,
                            short_addr: str, queues: 'QueueDict',
                            gateway: 'Gateway') -> None:
    """Process received packet."""
    if header == PUBLIC_KEY_DATAGRAM_HEADER:
        if len(payload_bytes) == TFC_PUBLIC_KEY_LENGTH:
            msg = f"Received public key from {short_addr} at {ts.strftime('%b %d - %H:%M:%S.%f')[:-4]}:"
            print_key(msg, payload_bytes, gateway.settings, public_key=True)
            queues[PUB_KEY_SEND_QUEUE].put((onion_pub_key, payload_bytes))

    elif header == MESSAGE_DATAGRAM_HEADER:
        queues[DST_MESSAGE_QUEUE].put(header + ts_bytes + onion_pub_key +
                                      ORIGIN_CONTACT_HEADER + payload_bytes)
        rp_print(f"Message   from contact {short_addr}", ts)

    elif header in [
            GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER,
            GROUP_MSG_MEMBER_ADD_HEADER, GROUP_MSG_MEMBER_REM_HEADER,
            GROUP_MSG_EXIT_GROUP_HEADER
    ]:
        queues[GROUP_MSG_QUEUE].put((header, payload_bytes, short_addr))

    else:
        rp_print(f"Received invalid packet from {short_addr}", ts, bold=True)
コード例 #2
0
ファイル: tcb.py プロジェクト: tannercollin/tfc
def src_incoming(queues:   'QueueDict',
                 gateway:  'Gateway',
                 unittest: bool = False
                 ) -> None:
    """\
    Redirect messages received from Source Computer to appropriate queues.
    """
    packets_from_sc   = queues[GATEWAY_QUEUE]
    packets_to_dc     = queues[DST_MESSAGE_QUEUE]
    commands_to_dc    = queues[DST_COMMAND_QUEUE]
    messages_to_flask = queues[M_TO_FLASK_QUEUE]
    files_to_flask    = queues[F_TO_FLASK_QUEUE]
    commands_to_relay = queues[SRC_TO_RELAY_QUEUE]

    while True:
        with ignored(EOFError, KeyboardInterrupt):
            while packets_from_sc.qsize() == 0:
                time.sleep(0.01)

            ts, packet = packets_from_sc.get()  # type: datetime, bytes
            ts_bytes   = int_to_bytes(int(ts.strftime('%Y%m%d%H%M%S%f')[:-4]))

            try:
                packet = gateway.detect_errors(packet)
            except FunctionReturn:
                continue

            header, packet = separate_header(packet, DATAGRAM_HEADER_LENGTH)

            if header == UNENCRYPTED_DATAGRAM_HEADER:
                commands_to_relay.put(packet)

            elif header in [COMMAND_DATAGRAM_HEADER, LOCAL_KEY_DATAGRAM_HEADER]:
                commands_to_dc.put(header + ts_bytes + packet)
                p_type = 'Command  ' if header == COMMAND_DATAGRAM_HEADER else 'Local key'
                rp_print(f"{p_type} to local Receiver", ts)

            elif header in [MESSAGE_DATAGRAM_HEADER, PUBLIC_KEY_DATAGRAM_HEADER]:
                onion_pub_key, payload = separate_header(packet, ONION_SERVICE_PUBLIC_KEY_LENGTH)
                packet_str             = header.decode() + b85encode(payload)
                queue_to_flask(packet_str, onion_pub_key, messages_to_flask, ts, header)
                if header == MESSAGE_DATAGRAM_HEADER:
                    packets_to_dc.put(header + ts_bytes + onion_pub_key + ORIGIN_USER_HEADER + payload)

            elif header == FILE_DATAGRAM_HEADER:
                no_contacts_b, payload = separate_header(packet, ENCODED_INTEGER_LENGTH)
                no_contacts            = bytes_to_int(no_contacts_b)
                ser_accounts, file_ct  = separate_header(payload, no_contacts * ONION_SERVICE_PUBLIC_KEY_LENGTH)
                pub_keys               = split_byte_string(ser_accounts, item_len=ONION_SERVICE_PUBLIC_KEY_LENGTH)
                for onion_pub_key in pub_keys:
                    queue_to_flask(file_ct, onion_pub_key, files_to_flask, ts, header)

            elif header in [GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER,
                            GROUP_MSG_MEMBER_ADD_HEADER, GROUP_MSG_MEMBER_REM_HEADER,
                            GROUP_MSG_EXIT_GROUP_HEADER]:
                process_group_management_message(ts, packet, header, messages_to_flask)

            if unittest:
                break
コード例 #3
0
    def connect(self, port: int) -> None:
        """Launch Tor as a subprocess.

        If TFC is running on top of Tails, do not launch a separate
        instance of Tor.
        """
        if self.platform_is_tails():
            self.controller = Controller.from_port(port=TOR_CONTROL_PORT)
            self.controller.authenticate()
            return None

        tor_data_directory = tempfile.TemporaryDirectory()
        tor_control_socket = os.path.join(tor_data_directory.name, 'control_socket')

        if not os.path.isfile('/usr/bin/tor'):
            raise CriticalError("Check that Tor is installed.")

        while True:
            try:
                self.tor_process = stem.process.launch_tor_with_config(
                    config={'DataDirectory':   tor_data_directory.name,
                            'SocksPort':       str(port),
                            'ControlSocket':   tor_control_socket,
                            'AvoidDiskWrites': '1',
                            'Log':             'notice stdout',
                            'GeoIPFile':       '/usr/share/tor/geoip',
                            'GeoIPv6File ':    '/usr/share/tor/geoip6'},
                    tor_cmd='/usr/bin/tor')
                break

            except OSError:
                pass  # Tor timed out. Try again.

        start_ts = time.monotonic()
        self.controller = stem.control.Controller.from_socket_file(path=tor_control_socket)
        self.controller.authenticate()

        while True:
            time.sleep(0.1)

            try:
                response = self.controller.get_info("status/bootstrap-phase")
            except stem.SocketClosed:
                raise CriticalError("Tor socket closed.")

            res_parts = shlex.split(response)
            summary   = res_parts[4].split('=')[1]

            if summary == 'Done':
                tor_version = self.controller.get_version().version_str.split(' (')[0]
                rp_print(f"Setup  70% - Tor {tor_version} is now running", bold=True)
                break

            if time.monotonic() - start_ts > 15:
                start_ts = time.monotonic()
                self.controller = stem.control.Controller.from_socket_file(path=tor_control_socket)
                self.controller.authenticate()
コード例 #4
0
def process_command_datagram(ts: 'datetime', packet: bytes, header: bytes,
                             queues: 'QueueDict') -> None:
    """Process command datagram."""
    commands_to_dc = queues[DST_COMMAND_QUEUE]
    ts_bytes = int_to_bytes(int(ts.strftime("%Y%m%d%H%M%S%f")[:-4]))

    commands_to_dc.put(header + ts_bytes + packet)

    p_type = "Command  " if header == COMMAND_DATAGRAM_HEADER else "Local key"
    rp_print(f"{p_type} to local Receiver", ts)
コード例 #5
0
def remove_client_process(onion_pub_keys: List[bytes],
                          proc_dict: Dict[bytes, Process]) -> None:
    """Remove client process."""
    for onion_pub_key in onion_pub_keys:
        if onion_pub_key in proc_dict:
            process = proc_dict[onion_pub_key]  # type: Process
            process.terminate()
            proc_dict.pop(onion_pub_key)
            rp_print(f"Removed {pub_key_to_short_address(onion_pub_key)}",
                     bold=True)
コード例 #6
0
ファイル: client.py プロジェクト: gtog/tfc
def client_scheduler(queues: 'QueueDict',
                     gateway: 'Gateway',
                     url_token_private_key: X448PrivateKey,
                     unittest: bool = False) -> None:
    """Manage `client` processes."""
    proc_dict = dict()  # type: Dict[bytes, Process]

    # Wait for Tor port from `onion_service` process.
    while True:
        with ignored(EOFError, KeyboardInterrupt):
            while queues[TOR_DATA_QUEUE].qsize() == 0:
                time.sleep(0.1)
            tor_port, onion_addr_user = queues[TOR_DATA_QUEUE].get()
            break

    while True:
        with ignored(EOFError, KeyboardInterrupt):

            while queues[CONTACT_KEY_QUEUE].qsize() == 0:
                time.sleep(0.1)

            command, ser_public_keys, is_existing_contact = queues[
                CONTACT_KEY_QUEUE].get()

            onion_pub_keys = split_byte_string(
                ser_public_keys, ONION_SERVICE_PUBLIC_KEY_LENGTH)

            if command == RP_ADD_CONTACT_HEADER:
                for onion_pub_key in onion_pub_keys:
                    if onion_pub_key not in proc_dict:
                        onion_addr_user = '' if is_existing_contact else onion_addr_user
                        proc_dict[onion_pub_key] = Process(
                            target=client,
                            args=(onion_pub_key, queues, url_token_private_key,
                                  tor_port, gateway, onion_addr_user))
                        proc_dict[onion_pub_key].start()

            elif command == RP_REMOVE_CONTACT_HEADER:
                for onion_pub_key in onion_pub_keys:
                    if onion_pub_key in proc_dict:
                        process = proc_dict[onion_pub_key]  # type: Process
                        process.terminate()
                        proc_dict.pop(onion_pub_key)
                        rp_print(
                            f"Removed {pub_key_to_short_address(onion_pub_key)}",
                            bold=True)

            if unittest and queues[UNITTEST_QUEUE].qsize() != 0:
                break
コード例 #7
0
def client(onion_pub_key: bytes,
           queues: 'QueueDict',
           url_token_private_key: X448PrivateKey,
           tor_port: str,
           gateway: 'Gateway',
           onion_addr_user: str,
           unit_test: bool = False) -> None:
    """Load packets from contact's Onion Service."""
    cached_pk = ''
    short_addr = pub_key_to_short_address(onion_pub_key)
    onion_addr = pub_key_to_onion_address(onion_pub_key)
    check_delay = RELAY_CLIENT_MIN_DELAY
    is_online = False

    session = requests.session()
    session.proxies = {
        'http': f'socks5h://127.0.0.1:{tor_port}',
        'https': f'socks5h://127.0.0.1:{tor_port}'
    }

    rp_print(f"Connecting to {short_addr}...", bold=True)

    # When Transmitter Program sends contact under UNENCRYPTED_ADD_EXISTING_CONTACT, this function
    # receives user's own Onion address: That way it knows to request the contact to add them:
    if onion_addr_user:
        send_contact_request(onion_addr, onion_addr_user, session)

    while True:
        with ignored(EOFError, KeyboardInterrupt, SoftError):
            time.sleep(check_delay)

            url_token_public_key_hex = load_url_token(onion_addr, session)
            is_online, check_delay = manage_contact_status(
                url_token_public_key_hex, check_delay, is_online, short_addr)

            if not is_online:
                continue

            url_token, cached_pk = update_url_token(url_token_private_key,
                                                    url_token_public_key_hex,
                                                    cached_pk, onion_pub_key,
                                                    queues)

            get_data_loop(onion_addr, url_token, short_addr, onion_pub_key,
                          queues, session, gateway)

            if unit_test:
                break
コード例 #8
0
    def connect(self, port: int) -> None:
        """Launch Tor as a subprocess.

        If TFC is running on top of Tails, do not launch a separate
        instance of Tor.
        """
        if self.platform_is_tails():
            self.controller = Controller.from_port(port=TOR_CONTROL_PORT)
            self.controller.authenticate()
            return None

        tor_data_directory = tempfile.TemporaryDirectory()
        tor_control_socket = os.path.join(tor_data_directory.name,
                                          'control_socket')

        if not os.path.isfile('/usr/bin/tor'):
            raise CriticalError("Check that Tor is installed.")

        self.launch_tor_process(port, tor_control_socket, tor_data_directory)

        start_ts = time.monotonic()
        self.controller = stem.control.Controller.from_socket_file(
            path=tor_control_socket)
        self.controller.authenticate()

        while True:
            time.sleep(0.1)

            try:
                response = self.controller.get_info("status/bootstrap-phase")
            except stem.SocketClosed:
                raise CriticalError("Tor socket closed.")

            res_parts = shlex.split(response)
            summary = res_parts[4].split('=')[1]

            if summary == 'Done':
                tor_version = self.controller.get_version().version_str.split(
                    ' (')[0]
                rp_print(f"Setup  70% - Tor {tor_version} is now running",
                         bold=True)
                break

            if time.monotonic() - start_ts > 15:
                start_ts = time.monotonic()
                self.controller = stem.control.Controller.from_socket_file(
                    path=tor_control_socket)
                self.controller.authenticate()
コード例 #9
0
def check_for_files(url_token: str, onion_pub_key: bytes, onion_addr: str,
                    short_addr: str, session: 'Session',
                    queues: 'QueueDict') -> None:
    """See if a file is available from contact.."""
    try:
        file_data = session.get(f"http://{onion_addr}.onion/{url_token}/files",
                                stream=True).content
        if file_data:
            ts = datetime.now()
            ts_bytes = int_to_bytes(int(ts.strftime("%Y%m%d%H%M%S%f")[:-4]))
            packet = FILE_DATAGRAM_HEADER + ts_bytes + onion_pub_key + ORIGIN_CONTACT_HEADER + file_data
            queues[DST_MESSAGE_QUEUE].put(packet)
            rp_print(f"File      from contact {short_addr}", ts)

    except requests.exceptions.RequestException:
        pass
コード例 #10
0
ファイル: onion.py プロジェクト: dimwap/tfc
def onion_service(queues: Dict[bytes, 'Queue[Any]']) -> None:
    """Manage the Tor Onion Service and control Tor via stem."""
    rp_print("Setup   0% - Waiting for Onion Service configuration...",
             bold=True)
    while queues[ONION_KEY_QUEUE].qsize() == 0:
        time.sleep(0.1)

    private_key, c_code = queues[ONION_KEY_QUEUE].get()  # type: bytes, bytes
    public_key_user = bytes(
        nacl.signing.SigningKey(seed=private_key).verify_key)
    onion_addr_user = pub_key_to_onion_address(public_key_user)
    buffer_key = hashlib.blake2b(BUFFER_KEY,
                                 key=private_key,
                                 digest_size=SYMMETRIC_KEY_LENGTH).digest()

    try:
        rp_print("Setup  10% - Launching Tor...", bold=True)
        tor_port = get_available_port(1000, 65535)
        tor = Tor()
        tor.connect(tor_port)
    except (EOFError, KeyboardInterrupt):
        return

    if tor.controller is None:
        raise CriticalError("No Tor controller")

    try:
        rp_print("Setup  75% - Launching Onion Service...", bold=True)
        key_data = stem_compatible_ed25519_key_from_private_key(private_key)
        response = tor.controller.create_ephemeral_hidden_service(
            ports={80: 5000},
            key_type='ED25519-V3',
            key_content=key_data,
            await_publication=True)
        rp_print("Setup 100% - Onion Service is now published.", bold=True)

        m_print([
            "Your TFC account is:", onion_addr_user, '',
            f"Onion Service confirmation code (to Transmitter): {c_code.hex()}"
        ],
                box=True)

        # Allow the client to start looking for contacts at this point.
        queues[TOR_DATA_QUEUE].put((tor_port, onion_addr_user))
        queues[USER_ACCOUNT_QUEUE].put(onion_addr_user)

        # Pass buffer key to related processes
        queues[TX_BUF_KEY_QUEUE].put(buffer_key)
        queues[RX_BUF_KEY_QUEUE].put(buffer_key)

    except (KeyboardInterrupt, stem.SocketClosed):
        tor.stop()
        return

    monitor_queues(tor, response, queues)
コード例 #11
0
def queue_to_flask(packet: Union[bytes, str], onion_pub_key: bytes,
                   flask_queue: 'Queue[Tuple[Union[bytes, str], bytes]]',
                   ts: 'datetime', header: bytes) -> None:
    """Put packet to flask queue and print message."""
    p_type = {
        MESSAGE_DATAGRAM_HEADER: 'Message  ',
        PUBLIC_KEY_DATAGRAM_HEADER: 'Pub key  ',
        FILE_DATAGRAM_HEADER: 'File     ',
        GROUP_MSG_INVITE_HEADER: 'G invite ',
        GROUP_MSG_JOIN_HEADER: 'G join   ',
        GROUP_MSG_MEMBER_ADD_HEADER: 'G add    ',
        GROUP_MSG_MEMBER_REM_HEADER: 'G remove ',
        GROUP_MSG_EXIT_GROUP_HEADER: 'G exit   '
    }[header]

    flask_queue.put((packet, onion_pub_key))
    rp_print(f"{p_type} to contact {pub_key_to_short_address(onion_pub_key)}",
             ts)
コード例 #12
0
def manage_contact_status(ut_pubkey_hex: str, check_delay: float,
                          is_online: bool,
                          short_addr: str) -> Tuple[bool, float]:
    """Manage online status of contact based on availability of URL token's public key."""
    if ut_pubkey_hex == "":
        if check_delay < RELAY_CLIENT_MAX_DELAY:
            check_delay *= 2
        if check_delay > CLIENT_OFFLINE_THRESHOLD and is_online:
            is_online = False
            rp_print(f"{short_addr} is now offline", bold=True)

    else:
        check_delay = RELAY_CLIENT_MIN_DELAY
        if not is_online:
            is_online = True
            rp_print(f"{short_addr} is now online", bold=True)

    return is_online, check_delay
コード例 #13
0
def buffer_to_flask(packet: Union[bytes, str],
                    onion_pub_key: bytes,
                    ts: 'datetime',
                    header: bytes,
                    buf_key: bytes,
                    file: bool = False) -> None:
    """Buffer outgoing datagram for Flask and print message."""
    p_type = {
        MESSAGE_DATAGRAM_HEADER: 'Message  ',
        PUBLIC_KEY_DATAGRAM_HEADER: 'Pub key  ',
        FILE_DATAGRAM_HEADER: 'File     ',
        GROUP_MSG_INVITE_HEADER: 'G invite ',
        GROUP_MSG_JOIN_HEADER: 'G join   ',
        GROUP_MSG_MEMBER_ADD_HEADER: 'G add    ',
        GROUP_MSG_MEMBER_REM_HEADER: 'G remove ',
        GROUP_MSG_EXIT_GROUP_HEADER: 'G exit   '
    }[header]

    if buf_key is None:
        raise SoftError("Error: No buffer key available for packet buffering.")

    if isinstance(packet, str):
        packet = packet.encode()

    file_name = RELAY_BUFFER_OUTGOING_FILE if file else RELAY_BUFFER_OUTGOING_MESSAGE
    file_dir = RELAY_BUFFER_OUTGOING_F_DIR if file else RELAY_BUFFER_OUTGOING_M_DIR
    sub_dir = hashlib.blake2b(onion_pub_key,
                              key=buf_key,
                              digest_size=BLAKE2_DIGEST_LENGTH).hexdigest()

    enc_packet = encrypt_and_sign(packet, key=buf_key)

    store_unique(enc_packet, f"{file_dir}/{sub_dir}/", file_name)

    rp_print(f"{p_type} to contact {pub_key_to_short_address(onion_pub_key)}",
             ts)
コード例 #14
0
 def test_works_without_timestamp(self):
     self.assertIsNone(rp_print("testMessage"))
コード例 #15
0
ファイル: client.py プロジェクト: gtog/tfc
def client(onion_pub_key: bytes,
           queues: 'QueueDict',
           url_token_private_key: X448PrivateKey,
           tor_port: str,
           gateway: 'Gateway',
           onion_addr_user: str,
           unittest: bool = False) -> None:
    """Load packets from contact's Onion Service."""
    url_token = ''
    cached_pk = ''
    short_addr = pub_key_to_short_address(onion_pub_key)
    onion_addr = pub_key_to_onion_address(onion_pub_key)
    check_delay = RELAY_CLIENT_MIN_DELAY
    is_online = False

    session = requests.session()
    session.proxies = {
        'http': f'socks5h://127.0.0.1:{tor_port}',
        'https': f'socks5h://127.0.0.1:{tor_port}'
    }

    rp_print(f"Connecting to {short_addr}...", bold=True)

    # When Transmitter Program sends contact under UNENCRYPTED_ADD_EXISTING_CONTACT, this function
    # receives user's own Onion address: That way it knows to request the contact to add them:
    if onion_addr_user:
        while True:
            try:
                reply = session.get(
                    f'http://{onion_addr}.onion/contact_request/{onion_addr_user}',
                    timeout=45).text
                if reply == "OK":
                    break
            except requests.exceptions.RequestException:
                time.sleep(RELAY_CLIENT_MIN_DELAY)

    while True:
        with ignored(EOFError, KeyboardInterrupt):
            time.sleep(check_delay)

            # Obtain URL token
            # ----------------

            # Load URL token public key from contact's Onion Service root domain
            try:
                url_token_public_key_hex = session.get(
                    f'http://{onion_addr}.onion/', timeout=45).text
            except requests.exceptions.RequestException:
                url_token_public_key_hex = ''

            # Manage online status of contact based on availability of URL token's public key
            if url_token_public_key_hex == '':
                if check_delay < RELAY_CLIENT_MAX_DELAY:
                    check_delay *= 2
                if check_delay > CLIENT_OFFLINE_THRESHOLD and is_online:
                    is_online = False
                    rp_print(f"{short_addr} is now offline", bold=True)
                continue
            else:
                check_delay = RELAY_CLIENT_MIN_DELAY
                if not is_online:
                    is_online = True
                    rp_print(f"{short_addr} is now online", bold=True)

            # When contact's URL token public key changes, update URL token
            if url_token_public_key_hex != cached_pk:
                try:
                    public_key = bytes.fromhex(url_token_public_key_hex)

                    if len(public_key
                           ) != TFC_PUBLIC_KEY_LENGTH or public_key == bytes(
                               TFC_PUBLIC_KEY_LENGTH):
                        raise ValueError

                    shared_secret = url_token_private_key.exchange(
                        X448PublicKey.from_public_bytes(public_key))
                    url_token = hashlib.blake2b(
                        shared_secret,
                        digest_size=SYMMETRIC_KEY_LENGTH).hexdigest()
                except (TypeError, ValueError):
                    continue

                cached_pk = url_token_public_key_hex  # Update client's URL token public key
                queues[URL_TOKEN_QUEUE].put(
                    (onion_pub_key,
                     url_token))  # Update Flask server's URL token for contact

            # Load TFC data with URL token
            # ----------------------------

            get_data_loop(onion_addr, url_token, short_addr, onion_pub_key,
                          queues, session, gateway)

            if unittest:
                break
コード例 #16
0
def onion_service(queues: Dict[bytes, 'Queue[Any]']) -> None:
    """Manage the Tor Onion Service and control Tor via stem."""
    rp_print("Setup   0% - Waiting for Onion Service configuration...", bold=True)
    while queues[ONION_KEY_QUEUE].qsize() == 0:
        time.sleep(0.1)

    private_key, c_code = queues[ONION_KEY_QUEUE].get()  # type: bytes, bytes
    public_key_user     = bytes(nacl.signing.SigningKey(seed=private_key).verify_key)
    onion_addr_user     = pub_key_to_onion_address(public_key_user)

    try:
        rp_print("Setup  10% - Launching Tor...", bold=True)
        tor_port = get_available_port(1000, 65535)
        tor      = Tor()
        tor.connect(tor_port)
    except (EOFError, KeyboardInterrupt):
        return

    if tor.controller is None:
        raise CriticalError("No Tor controller")

    try:
        rp_print("Setup  75% - Launching Onion Service...", bold=True)
        key_data = stem_compatible_ed25519_key_from_private_key(private_key)
        response = tor.controller.create_ephemeral_hidden_service(ports={80: 5000},
                                                                  key_type='ED25519-V3',
                                                                  key_content=key_data,
                                                                  await_publication=True)
        rp_print("Setup 100% - Onion Service is now published.", bold=True)

        m_print(["Your TFC account is:",
                 onion_addr_user, '',
                 f"Onion Service confirmation code (to Transmitter): {c_code.hex()}"], box=True)

        # Allow the client to start looking for contacts at this point.
        queues[TOR_DATA_QUEUE].put((tor_port, onion_addr_user))

    except (KeyboardInterrupt, stem.SocketClosed):
        tor.stop()
        return

    while True:
        try:
            time.sleep(0.1)

            if queues[ONION_KEY_QUEUE].qsize() > 0:
                _, c_code = queues[ONION_KEY_QUEUE].get()

                m_print(["Onion Service is already running.", '',
                         f"Onion Service confirmation code (to Transmitter): {c_code.hex()}"], box=True)

            if queues[ONION_CLOSE_QUEUE].qsize() > 0:
                command = queues[ONION_CLOSE_QUEUE].get()
                if not tor.platform_is_tails() and command == EXIT:
                    tor.controller.remove_hidden_service(response.service_id)
                    tor.stop()
                queues[EXIT_QUEUE].put(command)
                time.sleep(5)
                break

        except (EOFError, KeyboardInterrupt):
            pass
        except stem.SocketClosed:
            tor.controller.remove_hidden_service(response.service_id)
            tor.stop()
            break
コード例 #17
0
ファイル: client.py プロジェクト: gtog/tfc
def get_data_loop(onion_addr: str, url_token: str, short_addr: str,
                  onion_pub_key: bytes, queues: 'QueueDict',
                  session: 'Session', gateway: 'Gateway') -> None:
    """Load TFC data from contact's Onion Service using valid URL token."""
    while True:
        try:
            # See if a file is available
            try:
                file_data = session.get(
                    f'http://{onion_addr}.onion/{url_token}/files',
                    stream=True).content
                if file_data:
                    ts = datetime.now()
                    ts_bytes = int_to_bytes(
                        int(ts.strftime('%Y%m%d%H%M%S%f')[:-4]))
                    packet = FILE_DATAGRAM_HEADER + ts_bytes + onion_pub_key + ORIGIN_CONTACT_HEADER + file_data
                    queues[DST_MESSAGE_QUEUE].put(packet)
                    rp_print(f"File      from contact {short_addr}", ts)

            except requests.exceptions.RequestException:
                pass

            # See if messages are available
            try:
                r = session.get(
                    f'http://{onion_addr}.onion/{url_token}/messages',
                    stream=True)
            except requests.exceptions.RequestException:
                return None

            for line in r.iter_lines(
            ):  # Iterates over newline-separated datagrams

                if not line:
                    continue

                try:
                    header, payload = separate_header(
                        line, DATAGRAM_HEADER_LENGTH)  # type: bytes, bytes
                    payload_bytes = base64.b85decode(payload)
                except (UnicodeError, ValueError):
                    continue

                ts = datetime.now()
                ts_bytes = int_to_bytes(int(
                    ts.strftime('%Y%m%d%H%M%S%f')[:-4]))

                if header == PUBLIC_KEY_DATAGRAM_HEADER:
                    if len(payload_bytes) == TFC_PUBLIC_KEY_LENGTH:
                        msg = f"Received public key from {short_addr} at {ts.strftime('%b %d - %H:%M:%S.%f')[:-4]}:"
                        print_key(msg,
                                  payload_bytes,
                                  gateway.settings,
                                  public_key=True)

                elif header == MESSAGE_DATAGRAM_HEADER:
                    queues[DST_MESSAGE_QUEUE].put(header + ts_bytes +
                                                  onion_pub_key +
                                                  ORIGIN_CONTACT_HEADER +
                                                  payload_bytes)
                    rp_print(f"Message   from contact {short_addr}", ts)

                elif header in [
                        GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER,
                        GROUP_MSG_MEMBER_ADD_HEADER,
                        GROUP_MSG_MEMBER_REM_HEADER,
                        GROUP_MSG_EXIT_GROUP_HEADER
                ]:
                    queues[GROUP_MSG_QUEUE].put(
                        (header, payload_bytes, short_addr))

                else:
                    rp_print(f"Received invalid packet from {short_addr}",
                             ts,
                             bold=True)

        except requests.exceptions.RequestException:
            break