youandme/src/yam/yam.py

172 lines
6.0 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
2020-04-16 11:41:49 +00:00
import sys
2020-04-20 08:36:41 +00:00
import socket
from time import sleep
2020-04-20 08:36:41 +00:00
from threading import Thread
import tempfile
import argparse
from string import printable
from base64 import b85decode, b85encode
2020-04-20 08:36:41 +00:00
import socks
2020-04-20 08:36:41 +00:00
from stem.control import Controller
2020-04-16 11:41:49 +00:00
try:
from youandme.tor import launch_tor
except ModuleNotFoundError:
pass
from youandme.commands import terminator
2020-04-16 11:41:49 +00:00
from youandme.server import server
2020-04-20 08:36:41 +00:00
from youandme.client import client
from youandme.stream import encode_and_send, decoded_recv_stream
2020-04-20 08:36:41 +00:00
class Connection:
connected = True
2020-04-20 08:36:41 +00:00
class _Address:
address = ""
def _get_open_port():
# taken from (but modified) stackoverflow.com/a/2838309
# by stackoverflow.com/users/133374/albert
# ccy-by-sa-3 creativecommons.org/licenses/by-sa/3.0/
# changes from source: import moved to top of file, bind to localhost
# vulnerable to race condition
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("127.0.0.1", 0))
s.listen(1)
port = s.getsockname()[1]
s.close()
return port
2020-04-20 08:36:41 +00:00
def connector(host, send_data, recv_data,
address="", control_port=1337, socks_port=1338):
2020-04-20 08:36:41 +00:00
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = sock.connect_ex(('127.0.0.1', socks_port))
sock.close()
2020-04-20 08:36:41 +00:00
if result != 0:
try:
launch_tor(control_port=control_port, socks_port=socks_port)
except OSError:
with tempfile.TemporaryDirectory() as tmpdirname:
launch_tor(control_port=control_port,
socks_port=socks_port, data_dir=tmpdirname)
2020-04-20 08:36:41 +00:00
if host:
with Controller.from_port(port=control_port) as controller:
controller.authenticate()
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
ip = '127.0.0.1'
s.bind((ip, 0))
s.listen(1)
port = s.getsockname()[1]
serv = controller.create_ephemeral_hidden_service(
{1337: '127.0.0.1:' + str(port)},
key_content='ED25519-V3',
await_publication=True,
)
_Address.address = serv.service_id
conn, addr = s.accept()
server(0.01, controller, conn, send_data, recv_data,
Connection)
2020-04-20 08:36:41 +00:00
else:
if not address.endswith('.onion'):
address += '.onion'
try:
client(0.01, address, socks_port, send_data, recv_data, Connection)
except socks.GeneralProxyError:
Connection.connected = False
2020-04-16 11:41:49 +00:00
def chat(mode, send_data, recv_data, alpha):
print("A notice will be shown when connection is established, " +
"but messages may be typed now.")
2020-04-20 11:15:19 +00:00
display_buffer = []
2020-04-20 08:36:41 +00:00
if mode == 'host':
print('Creating tunnel...')
2020-04-20 08:36:41 +00:00
while _Address.address == "":
sleep(0.01)
print('Tunnel address:')
2020-04-20 08:36:41 +00:00
print(_Address.address)
2020-04-20 08:36:41 +00:00
def display_new():
buffer = []
while True:
try:
for message in decoded_recv_stream(recv_data, 0.5):
if alpha:
for c in message:
c = c.decode('utf-8')
if c not in printable:
continue
if len(buffer) < 1000:
buffer.append(c)
print("\033[1;33m" + "".join(buffer) + "\033[0m")
else:
print("\033[1;33m" + message.decode('utf-8') + "\033[0m")
except ValueError:
pass
2020-04-20 08:36:41 +00:00
Thread(target=display_new, daemon=True).start()
2020-04-20 08:36:41 +00:00
def make_message():
while True:
new = input("\033[0m").encode('utf-8') # nosec
encode_and_send(send_data, new)
2020-04-20 08:36:41 +00:00
Thread(target=make_message, daemon=True).start()
encode_and_send(send_data, "Connection established")
2020-04-20 08:36:41 +00:00
while True:
2020-04-20 11:15:19 +00:00
try:
if not Connection.connected:
print("Cease dancing ☹️")
2020-04-20 11:15:19 +00:00
break
sleep(1)
except KeyboardInterrupt:
break
PORT_MESSAGE = "Specify any free ports above 1023"
2020-04-16 11:41:49 +00:00
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='End-to-end encrypted messaging with metadata privacy')
parser.add_argument('connection', choices=['host', 'conn'])
parser.add_argument('--socks-port',
help='Socks proxy port (will use random if not given)',
default=0, type=int)
parser.add_argument('--control-port',
help='Tor control port (will use random if not given)',
default=0, type=int)
parser.add_argument('--alphanum-only',
help='Only stdout en-us typical characters',
default=False, type=bool)
parser.add_argument('--address',
help='Address to connect to. No port.', default='')
args = parser.parse_args()
if args.socks_port == 0: args.socks_port = _get_open_port()
if args.control_port == 0: args.control_port = _get_open_port()
send_data = bytearray()
recv_data = bytearray()
if args.connection == 'conn':
if not args.address:
print("Must specify address if connecting (--address)",
file=sys.stderr)
sys.exit(3)
Thread(target=connector,
args=[False, send_data, recv_data],
kwargs={'address': args.address,
'socks_port': args.socks_port,
'control_port': args.control_port}, daemon=True).start()
chat('conn', send_data, recv_data, args.alphanum_only)
else:
Thread(target=connector, args=[True, send_data, recv_data],
kwargs={'socks_port': args.socks_port,
'control_port': args.control_port}, daemon=True).start()
chat('host', send_data, recv_data, args.alphanum_only)