From 4e8f7e2761b6dd1adbfa0f7538d0077f7925b9f3 Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Thu, 20 Sep 2018 23:47:40 -0500 Subject: [PATCH] work on sockets --- onionr/api.py | 2 +- onionr/communicator2.py | 26 +-- onionr/onionrsockets.py | 149 ++++++------------ .../default-plugins/metadataprocessor/main.py | 2 +- 4 files changed, 56 insertions(+), 123 deletions(-) diff --git a/onionr/api.py b/onionr/api.py index adfabf88..21ba7a66 100755 --- a/onionr/api.py +++ b/onionr/api.py @@ -140,7 +140,7 @@ class API: self.overrideCSP = False return resp - + @app.route('/www/private/') def www_private(path): startTime = math.floor(time.time()) diff --git a/onionr/communicator2.py b/onionr/communicator2.py index 7c8b6c0b..56a8e8e6 100755 --- a/onionr/communicator2.py +++ b/onionr/communicator2.py @@ -108,6 +108,8 @@ class OnionrCommunicatorDaemon: cleanupTimer.count = (cleanupTimer.frequency - 60) announceTimer.count = (cleanupTimer.frequency - 60) + self.socketServer = onionrsockets.OnionrSocketServer(self._core) + # Main daemon loop, mainly for calling timers, don't do any complex operations here to avoid locking try: while not self.shutdown: @@ -467,31 +469,15 @@ class OnionrCommunicatorDaemon: elif cmd[0] == 'uploadBlock': self.blockToUpload = cmd[1] threading.Thread(target=self.uploadBlock).start() - elif cmd[0] == 'startSocket': - # Create a socket or connect to one. - # The socket handler (such as the plugin or app using it) is specified in startData['reason] - startData = json.loads(cmd[1]) - threading.Thread(target=self.startSocket, args=(startData,)).start() + elif cmd[0] == 'addSocket': + socketInfo = json.loads(cmd[1]) + if socketInfo['reason'] in ('chat'): + onionrsockets.OnionrSocketClient(self._core, socketInfo['peer']) else: logger.info('Recieved daemonQueue command:' + cmd[0]) self.decrementThreadCount('daemonCommands') - def startSocket(self, startData): - # Start a socket client - mySocket = onionrsockets.OnionrSockets(self._core, startData) - self.sockets[mySocket.socketID] = mySocket - - sockProgram = '' # Function for socket handler (application) - - if startData['reason'] == 'chat': - sockProgram = onionrchat.OnionrChat - else: - del self.sockets[mySocket.socketID] # Delete socket if we have no handler for it - - threading.Thread(target=sockProgram, args=(self, mySocket.socketID)).start() - mySocket.startConn() - def uploadBlock(self): '''Upload our block to a few peers''' # when inserting a block, we try to upload it to a few peers to add some deniability diff --git a/onionr/onionrsockets.py b/onionr/onionrsockets.py index dc9702c1..bb505c8c 100644 --- a/onionr/onionrsockets.py +++ b/onionr/onionrsockets.py @@ -18,121 +18,68 @@ along with this program. If not, see . ''' import stem.control -import socket, selectors, socks, config, uuid -import onionrexceptions, time, onionrchat +import socks, config, uuid +import onionrexceptions, time, requests from dependencies import secrets -sel = selectors.DefaultSelector() +from flask import request, Response, abort -class OnionrSockets: - def __init__(self, coreInst, socketInfo): - '''Create a new Socket object. This interface is named a bit misleadingly - and does not actually forward network requests. - - Accepts coreInst, an instance of Onionr core library, and socketInfo, a dict with these values: - 'peer': peer master public key - 'address': string, if we're connecting to a socket, this is the address we connect to. Not applicable if we're creating our own - create: bool - ''' - self.socketID = secrets.token_hex(32) # Generate an ID for this socket +class OnionrSocketServer: + def __init__(self, coreInst): + self.sockets = {} # pubkey: tor address + self.connPool = {} + self.bindPort = 1337 self._core = coreInst - self.socketInfo = socketInfo + self.responseData = {} + self.killSocket = False + app = flask.Flask(__name__) - # Make sure socketInfo provides all necessary values - for i in ('peer', 'address', 'create', 'port'): - try: - socketInfo[i] - except KeyError: - raise ValueError('Must provide peer, address, and create in socketInfo dict argument') + http_server = WSGIServer((socket.service_id, bindPort), app) + http_server.serve_forever() - self.isServer = socketInfo['create'] # if we are the one creating the service + @app.route('/dc/', methods=['POST']) + def acceptConn(self): + data = request.form['data'] + data = self._core._utils.bytesTorStr(data) - self.remotePeer = socketInfo['peer'] - self.socketPort = socketInfo['port'] - self.serverAddress = socketInfo['address'] - self.connected = False - - self.readData = [] - self.sendData = 0 - config.reload() - - def startConn(self): - if self.isServer: - self.createServer() + if request.host in self.connPool: + self.connPool[request.host].append(data) else: - self.connectServer() - - def createServer(self): - # Create our HS and advertise it via a block - dataID = uuid.uuid4().hex - ourAddress = '' - ourPort = 1337 - ourInternalPort = 1338 + self.connPool[request.host] = [data] - # Setup the empheral HS + retData = self.responseData[request.host] + + self.responseData[request.host] = '' + + return retData + + def setResponseData(self, host, data): + self.responseData[host] = data + + def addSocket(self, peer): + bindPort = 1337 with stem.control.Controller.from_port(port=config.get('tor.controlPort')) as controller: controller.authenticate(config.get('tor.controlpassword')) - socketHS = controller.create_ephemeral_hidden_service({ourPort: ourInternalPort}, await_publication = True) - ourAddress = socketHS.service_id - # Advertise the server - meta = {'address': ourAddress, 'port': ourPort} - self._core.insertBlock(dataID, header='openSocket', encryptType='asym', asymPeer=self.remotePeer, sign=True, meta=meta) + socket = controller.create_ephemeral_hidden_service({80: bindPort}, await_publication = True) + self.sockets[peer] = socket.service_id - # Build the socket server - sock = socket.socket() - sock.bind(('127.0.0.1', ourInternalPort)) - sock.listen(100) - sock.setblocking(False) - sel.register(sock, selectors.EVENT_READ, self._accept) + self.responseData[socket.service_id] = '' - while True: - events = sel.select() - for key, mask in events: - callback = key.data - callback(key.fileobj, mask) + self._core.insertBlock(uuid.uuid4(), header='startSocket', sign=True, encryptType='asym', asymPeer=peer, meta={}) + while not self.killSocket: + time.sleep(3) return - - def _accept(self, sock, mask): - # Just accept the connection and pass it to our handler - conn, addr = sock.accept() - conn.setblocking(False) - sel.register(conn, selectors.EVENT_READ, self._read) - self.connected = True - - def _read(self, conn, mask): - data = conn.recv(1024) - if data: - data = data.decode() - self.readData.append(data) - else: - sel.unregister(conn) - conn.close() - - def addSendData(self, data): - try: - data = data.encode() - except AttributeError: - pass - self.sendData = data - def getReadData(self): - try: - data = self.readData.pop(0) - except IndexError: - data = '' - return data +class OnionrSocketClient: + def __init__(self, coreInst): + self.sockets = {} # pubkey: tor address + self.connPool = {} + self.bindPort = 1337 + self._core = coreInst + self.response = '' + self.request = '' + self.connected = False - def connectServer(self): - # Set the Tor proxy - socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', config.get('tor.socksport'), rdns=True) - socket.socket = socks.socksocket - remoteSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - with remoteSocket as s: - s.connect((self.serverAddress, self.port)) - data = s.recv(1024) - if self.sendData != 0: - s.send(self.sendData) - self.sendData = 0 - return \ No newline at end of file + def getResponse(self, peer): + self._core._utils.doPostRequest(self.) \ No newline at end of file diff --git a/onionr/static-data/default-plugins/metadataprocessor/main.py b/onionr/static-data/default-plugins/metadataprocessor/main.py index 759db071..85b7d9de 100644 --- a/onionr/static-data/default-plugins/metadataprocessor/main.py +++ b/onionr/static-data/default-plugins/metadataprocessor/main.py @@ -91,7 +91,7 @@ def on_processBlocks(api): raise ValueError("Missing socket reason") socketInfo = json.dumps({'peer': api.data['signer'], 'address': address, 'port': port, 'create': False, 'reason': reason}) - api.get_core().daemonQueueAdd('startSocket', socketInfo) + api.get_core().daemonQueueAdd('addSocket', socketInfo) def on_init(api, data = None):