diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a79fdb..a682631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ This project uses Semantic Versioning +## 1.0.0 + +Made yam a proper script (install and you can run from path with $ yam.py ) + ## 0.0.0 Initial release diff --git a/setup.py b/setup.py index 83d48cc..3e61b23 100644 --- a/setup.py +++ b/setup.py @@ -4,13 +4,16 @@ this_directory = path.abspath(path.dirname(__file__)) with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f: long_description = f.read() +packages = find_packages(exclude=['contrib', 'docs', 'tests']) setup(name='youandme', - version='0.0.2', + version='1.0.0', description='Simple private data sharing via bytearrays, Tor tunneling and metadata paranoia', long_description=long_description, long_description_content_type='text/markdown', author='Kevin Froman', + packages=packages, + scripts=['src/yam/yam.py'], author_email='beardog@mailbox.org', url='https://chaoswebs.net', install_requires=[ diff --git a/src/yam/__init__.py b/src/yam/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/yam.py b/src/yam/yam.py similarity index 53% rename from src/yam.py rename to src/yam/yam.py index c25cc56..3a72354 100644 --- a/src/yam.py +++ b/src/yam/yam.py @@ -1,7 +1,10 @@ +#!/usr/bin/env python3 import sys import socket from time import time, sleep from threading import Thread +import tempfile +import argparse from stem.control import Controller @@ -16,12 +19,26 @@ from youandme.client import client class _Address: address = "" +def _get_open_port(): + # taken from (but modified) https://stackoverflow.com/a/2838309 by https://stackoverflow.com/users/133374/albert ccy-by-sa-3 https://creativecommons.org/licenses/by-sa/3.0/ + # changes from source: import moved to top of file, bind specifically 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 def connector(host, send_data, recv_data, address="", control_port=1337, socks_port=1338): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result = sock.connect_ex(('127.0.0.1', socks_port)) if result != 0: - launch_tor(control_port=control_port, socks_port=socks_port) + 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) if host: with Controller.from_port(port=control_port) as controller: controller.authenticate() @@ -79,18 +96,33 @@ def chat(mode, send_data, recv_data): break sleep(1) except KeyboardInterrupt: - pass + break +PORT_MESSAGE = "Specify any free ports above 1023" if __name__ == "__main__": - if len(sys.argv) >= 2: - if sys.argv[1] == 'chat': - send_data = bytearray() - recv_data = bytearray() - if sys.argv[2] == 'host': - Thread(target=connector, args=[True, send_data, recv_data], kwargs={'socks_port': int(input("socks")), 'control_port': int(input('control port'))}, daemon=True).start() - elif sys.argv[2].startswith('conn'): - Thread(target=connector, args=[False, send_data, recv_data], kwargs={'address': sys.argv[3], 'socks_port': int(input("socks")), 'control_port': int(input('control port'))}, daemon=True).start() - else: - print('Must specify host or conn') - sys.exit(1) - chat(sys.argv[2], send_data, recv_data) + parser = argparse.ArgumentParser(description='End-to-end encrpyted instant messaging with metadata privacy and perfect forward secrecy') + 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('--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) + 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) \ No newline at end of file diff --git a/src/youandme/tor.py b/src/youandme/tor.py index 95e7797..62189fe 100644 --- a/src/youandme/tor.py +++ b/src/youandme/tor.py @@ -1,14 +1,17 @@ import stem.process -def launch_tor(control_port="1336", socks_port="1337"): +def launch_tor(control_port="1336", socks_port="1337", data_dir=""): control_port = str(control_port) socks_port = str(socks_port) - stem.process.launch_tor_with_config( config = { 'ControlPort': control_port, 'SocksPort': socks_port, 'Log': [ 'NOTICE stdout' ], - }, take_ownership=True) + } + if data_dir: + config['DataDirectory'] = data_dir + stem.process.launch_tor_with_config( + config = config, take_ownership=True) diff --git a/tests/test_client.py b/tests/test_client.py index 8984885..832a28d 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -7,8 +7,19 @@ import stem.process from stem.control import Controller from youandme import client -control_port = str(1353) -socks_port = str(1354) +def get_open_port(): + # taken from (but modified) https://stackoverflow.com/a/2838309 by https://stackoverflow.com/users/133374/albert ccy-by-sa-3 https://creativecommons.org/licenses/by-sa/3.0/ + # changes from source: import moved to top of file, bind specifically to localhost + 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 + +control_port = str(get_open_port()) +socks_port = str(get_open_port()) +assert control_port != socks_port stem.process.launch_tor_with_config( config = { diff --git a/tests/test_server.py b/tests/test_server.py index 1085a3e..6863c1a 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -1,23 +1,35 @@ import unittest import socket from threading import Thread +import tempfile import time import stem import stem.process from stem.control import Controller from youandme import server -control_port = str(1353) -socks_port = str(1354) +def get_open_port(): + # taken from (but modified) https://stackoverflow.com/a/2838309 by https://stackoverflow.com/users/133374/albert ccy-by-sa-3 https://creativecommons.org/licenses/by-sa/3.0/ + # changes from source: import moved to top of file, bind specifically to localhost + 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 -stem.process.launch_tor_with_config( -config = { - 'ControlPort': control_port, - 'SocksPort': socks_port, - 'Log': [ - 'NOTICE stdout' - ], -}, take_ownership=True) +control_port = str(get_open_port()) +socks_port = str(get_open_port()) +assert control_port != socks_port + +with tempfile.TemporaryDirectory() as tmpdirname: + stem.process.launch_tor_with_config( + config = { + 'ControlPort': control_port, + 'SocksPort': socks_port, + 'DataDirectory': tmpdirname + + }, take_ownership=True, init_msg_handler=lambda i: print(i)) def send_test_data(ip, port): time.sleep(2) @@ -52,7 +64,7 @@ class TestServer(unittest.TestCase): ) Thread(target=send_test_data, args=[ip, port], daemon=True).start() conn, addr = s.accept() - Thread(target=server, args=[0.1, controller, conn, send_data, recv_data], daemon=True).start() + Thread(target=server.server, args=[0.1, controller, conn, send_data, recv_data], daemon=True).start() time.sleep(1) max_iters = 10000000 c = 0